3a931b5a746a183764303980506010547883c3c1
[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, see <http://www.gnu.org/licenses/>. *
26 ***************************************************************************/
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 /* see Embedded-HOWTO.txt in Jim Tcl project hosted on BerliOS*/
33 #define JIM_EMBEDDED
34
35 /* @todo the inclusion of target.h here is a layering violation */
36 #include <jtag/jtag.h>
37 #include <target/target.h>
38 #include "command.h"
39 #include "configuration.h"
40 #include "log.h"
41 #include "time_support.h"
42 #include "jim-eventloop.h"
43
44 /* nice short description of source file */
45 #define __THIS__FILE__ "command.c"
46
47 struct log_capture_state {
48 Jim_Interp *interp;
49 Jim_Obj *output;
50 };
51
52 static int unregister_command(struct command_context *context,
53 const char *cmd_prefix, const char *name);
54 static int jim_command_dispatch(Jim_Interp *interp, int argc, Jim_Obj * const *argv);
55 static int help_add_command(struct command_context *cmd_ctx,
56 const char *cmd_name, const char *help_text, const char *usage_text);
57 static int help_del_command(struct command_context *cmd_ctx, const char *cmd_name);
58
59 /* set of functions to wrap jimtcl internal data */
60 static inline bool jimcmd_is_proc(Jim_Cmd *cmd)
61 {
62 return cmd->isproc;
63 }
64
65 static inline bool jimcmd_is_ocd_command(Jim_Cmd *cmd)
66 {
67 return !cmd->isproc && cmd->u.native.cmdProc == jim_command_dispatch;
68 }
69
70 static inline void *jimcmd_privdata(Jim_Cmd *cmd)
71 {
72 return cmd->isproc ? NULL : cmd->u.native.privData;
73 }
74
75 static void tcl_output(void *privData, const char *file, unsigned line,
76 const char *function, const char *string)
77 {
78 struct log_capture_state *state = privData;
79 Jim_AppendString(state->interp, state->output, string, strlen(string));
80 }
81
82 static struct log_capture_state *command_log_capture_start(Jim_Interp *interp)
83 {
84 /* capture log output and return it. A garbage collect can
85 * happen, so we need a reference count to this object */
86 Jim_Obj *jim_output = Jim_NewStringObj(interp, "", 0);
87 if (!jim_output)
88 return NULL;
89
90 Jim_IncrRefCount(jim_output);
91
92 struct log_capture_state *state = malloc(sizeof(*state));
93 if (!state) {
94 LOG_ERROR("Out of memory");
95 Jim_DecrRefCount(interp, jim_output);
96 return NULL;
97 }
98
99 state->interp = interp;
100 state->output = jim_output;
101
102 log_add_callback(tcl_output, state);
103
104 return state;
105 }
106
107 /* Classic openocd commands provide progress output which we
108 * will capture and return as a Tcl return value.
109 *
110 * However, if a non-openocd command has been invoked, then it
111 * makes sense to return the tcl return value from that command.
112 *
113 * The tcl return value is empty for openocd commands that provide
114 * progress output.
115 *
116 * Therefore we set the tcl return value only if we actually
117 * captured output.
118 */
119 static void command_log_capture_finish(struct log_capture_state *state)
120 {
121 if (NULL == state)
122 return;
123
124 log_remove_callback(tcl_output, state);
125
126 int length;
127 Jim_GetString(state->output, &length);
128
129 if (length > 0)
130 Jim_SetResult(state->interp, state->output);
131 else {
132 /* No output captured, use tcl return value (which could
133 * be empty too). */
134 }
135 Jim_DecrRefCount(state->interp, state->output);
136
137 free(state);
138 }
139
140 /*
141 * FIXME: workaround for memory leak in jimtcl 0.80
142 * Jim API Jim_CreateCommand() converts the command name in a Jim object and
143 * does not free the object. Fixed for jimtcl 0.81 by e4416cf86f0b
144 * Use the internal jimtcl API Jim_CreateCommandObj, not exported by jim.h,
145 * and override the bugged API through preprocessor's macro.
146 * This workaround works only when jimtcl is compiled as OpenOCD submodule.
147 * It's broken on macOS, so it's currently restricted on Linux only.
148 * If jimtcl is linked-in from a precompiled library, either static or dynamic,
149 * the symbol Jim_CreateCommandObj is not exported and the build will use the
150 * bugged API.
151 * To be removed when OpenOCD will switch to jimtcl 0.81
152 */
153 #if JIM_VERSION == 80 && defined __linux__
154 static int workaround_createcommand(Jim_Interp *interp, const char *cmdName,
155 Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc);
156 int Jim_CreateCommandObj(Jim_Interp *interp, Jim_Obj *cmdNameObj,
157 Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
158 __attribute__((weak, alias("workaround_createcommand")));
159 static int workaround_createcommand(Jim_Interp *interp, const char *cmdName,
160 Jim_CmdProc *cmdProc, void *privData, Jim_DelCmdProc *delProc)
161 {
162 if ((void *)Jim_CreateCommandObj == (void *)workaround_createcommand)
163 return Jim_CreateCommand(interp, cmdName, cmdProc, privData, delProc);
164
165 Jim_Obj *cmd_name = Jim_NewStringObj(interp, cmdName, -1);
166 Jim_IncrRefCount(cmd_name);
167 int retval = Jim_CreateCommandObj(interp, cmd_name, cmdProc, privData, delProc);
168 Jim_DecrRefCount(interp, cmd_name);
169 return retval;
170 }
171 #define Jim_CreateCommand workaround_createcommand
172 #endif /* JIM_VERSION == 80 && defined __linux__*/
173 /* FIXME: end of workaround for memory leak in jimtcl 0.80 */
174
175 static int command_retval_set(Jim_Interp *interp, int retval)
176 {
177 int *return_retval = Jim_GetAssocData(interp, "retval");
178 if (return_retval != NULL)
179 *return_retval = retval;
180
181 return (retval == ERROR_OK) ? JIM_OK : retval;
182 }
183
184 extern struct command_context *global_cmd_ctx;
185
186 /* dump a single line to the log for the command.
187 * Do nothing in case we are not at debug level 3 */
188 static void script_debug(Jim_Interp *interp, unsigned int argc, Jim_Obj * const *argv)
189 {
190 if (debug_level < LOG_LVL_DEBUG)
191 return;
192
193 char *dbg = alloc_printf("command -");
194 for (unsigned i = 0; i < argc; i++) {
195 int len;
196 const char *w = Jim_GetString(argv[i], &len);
197 char *t = alloc_printf("%s %s", dbg, w);
198 free(dbg);
199 dbg = t;
200 }
201 LOG_DEBUG("%s", dbg);
202 free(dbg);
203 }
204
205 static void script_command_args_free(char **words, unsigned nwords)
206 {
207 for (unsigned i = 0; i < nwords; i++)
208 free(words[i]);
209 free(words);
210 }
211
212 static char **script_command_args_alloc(
213 unsigned argc, Jim_Obj * const *argv, unsigned *nwords)
214 {
215 char **words = malloc(argc * sizeof(char *));
216 if (NULL == words)
217 return NULL;
218
219 unsigned i;
220 for (i = 0; i < argc; i++) {
221 int len;
222 const char *w = Jim_GetString(argv[i], &len);
223 words[i] = strdup(w);
224 if (words[i] == NULL) {
225 script_command_args_free(words, i);
226 return NULL;
227 }
228 }
229 *nwords = i;
230 return words;
231 }
232
233 struct command_context *current_command_context(Jim_Interp *interp)
234 {
235 /* grab the command context from the associated data */
236 struct command_context *cmd_ctx = Jim_GetAssocData(interp, "context");
237 if (NULL == cmd_ctx) {
238 /* Tcl can invoke commands directly instead of via command_run_line(). This would
239 * happen when the Jim Tcl interpreter is provided by eCos or if we are running
240 * commands in a startup script.
241 *
242 * A telnet or gdb server would provide a non-default command context to
243 * handle piping of error output, have a separate current target, etc.
244 */
245 cmd_ctx = global_cmd_ctx;
246 }
247 return cmd_ctx;
248 }
249
250 /**
251 * Find a openocd command from fullname.
252 * @returns Returns the named command if it is registred in interp.
253 * Returns NULL otherwise.
254 */
255 static struct command *command_find_from_name(Jim_Interp *interp, const char *name)
256 {
257 if (!name)
258 return NULL;
259
260 Jim_Obj *jim_name = Jim_NewStringObj(interp, name, -1);
261 Jim_IncrRefCount(jim_name);
262 Jim_Cmd *cmd = Jim_GetCommand(interp, jim_name, JIM_NONE);
263 Jim_DecrRefCount(interp, jim_name);
264 if (!cmd || jimcmd_is_proc(cmd) || !jimcmd_is_ocd_command(cmd))
265 return NULL;
266
267 return jimcmd_privdata(cmd);
268 }
269
270 static struct command *command_new(struct command_context *cmd_ctx,
271 const char *full_name, const struct command_registration *cr)
272 {
273 assert(cr->name);
274
275 /*
276 * If it is a non-jim command with no .usage specified,
277 * log an error.
278 *
279 * strlen(.usage) == 0 means that the command takes no
280 * arguments.
281 */
282 if (!cr->jim_handler && !cr->usage)
283 LOG_ERROR("BUG: command '%s' does not have the "
284 "'.usage' field filled out",
285 full_name);
286
287 struct command *c = calloc(1, sizeof(struct command));
288 if (NULL == c)
289 return NULL;
290
291 c->name = strdup(cr->name);
292 if (!c->name) {
293 free(c);
294 return NULL;
295 }
296
297 c->handler = cr->handler;
298 c->jim_handler = cr->jim_handler;
299 c->mode = cr->mode;
300
301 if (cr->help || cr->usage)
302 help_add_command(cmd_ctx, full_name, cr->help, cr->usage);
303
304 return c;
305 }
306
307 static void command_free(struct Jim_Interp *interp, void *priv)
308 {
309 struct command *c = priv;
310
311 free(c->name);
312 free(c);
313 }
314
315 static struct command *register_command(struct command_context *context,
316 const char *cmd_prefix, const struct command_registration *cr)
317 {
318 char *full_name;
319
320 if (!context || !cr->name)
321 return NULL;
322
323 if (cmd_prefix)
324 full_name = alloc_printf("%s %s", cmd_prefix, cr->name);
325 else
326 full_name = strdup(cr->name);
327 if (!full_name)
328 return NULL;
329
330 struct command *c = command_find_from_name(context->interp, full_name);
331 if (c) {
332 /* TODO: originally we treated attempting to register a cmd twice as an error
333 * Sometimes we need this behaviour, such as with flash banks.
334 * http://www.mail-archive.com/openocd-development@lists.berlios.de/msg11152.html */
335 LOG_DEBUG("command '%s' is already registered", full_name);
336 free(full_name);
337 return c;
338 }
339
340 c = command_new(context, full_name, cr);
341 if (!c) {
342 free(full_name);
343 return NULL;
344 }
345
346 LOG_DEBUG("registering '%s'...", full_name);
347 int retval = Jim_CreateCommand(context->interp, full_name,
348 jim_command_dispatch, c, command_free);
349 if (retval != JIM_OK) {
350 command_run_linef(context, "del_help_text {%s}", full_name);
351 command_run_linef(context, "del_usage_text {%s}", full_name);
352 free(c);
353 free(full_name);
354 return NULL;
355 }
356
357 free(full_name);
358 return c;
359 }
360
361 int __register_commands(struct command_context *cmd_ctx, const char *cmd_prefix,
362 const struct command_registration *cmds, void *data,
363 struct target *override_target)
364 {
365 int retval = ERROR_OK;
366 unsigned i;
367 for (i = 0; cmds[i].name || cmds[i].chain; i++) {
368 const struct command_registration *cr = cmds + i;
369
370 struct command *c = NULL;
371 if (NULL != cr->name) {
372 c = register_command(cmd_ctx, cmd_prefix, cr);
373 if (NULL == c) {
374 retval = ERROR_FAIL;
375 break;
376 }
377 c->jim_handler_data = data;
378 c->jim_override_target = override_target;
379 }
380 if (NULL != cr->chain) {
381 if (cr->name) {
382 if (cmd_prefix) {
383 char *new_prefix = alloc_printf("%s %s", cmd_prefix, cr->name);
384 if (!new_prefix) {
385 retval = ERROR_FAIL;
386 break;
387 }
388 retval = __register_commands(cmd_ctx, new_prefix, cr->chain, data, override_target);
389 free(new_prefix);
390 } else {
391 retval = __register_commands(cmd_ctx, cr->name, cr->chain, data, override_target);
392 }
393 } else {
394 retval = __register_commands(cmd_ctx, cmd_prefix, cr->chain, data, override_target);
395 }
396 if (ERROR_OK != retval)
397 break;
398 }
399 }
400 if (ERROR_OK != retval) {
401 for (unsigned j = 0; j < i; j++)
402 unregister_command(cmd_ctx, cmd_prefix, cmds[j].name);
403 }
404 return retval;
405 }
406
407 static __attribute__ ((format (PRINTF_ATTRIBUTE_FORMAT, 2, 3)))
408 int unregister_commands_match(struct command_context *cmd_ctx, const char *format, ...)
409 {
410 Jim_Interp *interp = cmd_ctx->interp;
411 va_list ap;
412
413 va_start(ap, format);
414 char *query = alloc_vprintf(format, ap);
415 va_end(ap);
416 if (!query)
417 return ERROR_FAIL;
418
419 char *query_cmd = alloc_printf("info commands {%s}", query);
420 free(query);
421 if (!query_cmd)
422 return ERROR_FAIL;
423
424 int retval = Jim_EvalSource(interp, __THIS__FILE__, __LINE__, query_cmd);
425 free(query_cmd);
426 if (retval != JIM_OK)
427 return ERROR_FAIL;
428
429 Jim_Obj *list = Jim_GetResult(interp);
430 Jim_IncrRefCount(list);
431
432 int len = Jim_ListLength(interp, list);
433 for (int i = 0; i < len; i++) {
434 Jim_Obj *elem = Jim_ListGetIndex(interp, list, i);
435 Jim_IncrRefCount(elem);
436
437 const char *name = Jim_GetString(elem, NULL);
438 struct command *c = command_find_from_name(interp, name);
439 if (!c) {
440 /* not openocd command */
441 Jim_DecrRefCount(interp, elem);
442 continue;
443 }
444 LOG_DEBUG("delete command \"%s\"", name);
445 #if JIM_VERSION >= 80
446 Jim_DeleteCommand(interp, elem);
447 #else
448 Jim_DeleteCommand(interp, name);
449 #endif
450
451 help_del_command(cmd_ctx, name);
452
453 Jim_DecrRefCount(interp, elem);
454 }
455
456 Jim_DecrRefCount(interp, list);
457 return ERROR_OK;
458 }
459
460 int unregister_all_commands(struct command_context *context,
461 const char *cmd_prefix)
462 {
463 if (!context)
464 return ERROR_OK;
465
466 if (!cmd_prefix || !*cmd_prefix)
467 return unregister_commands_match(context, "*");
468
469 int retval = unregister_commands_match(context, "%s *", cmd_prefix);
470 if (retval != ERROR_OK)
471 return retval;
472
473 return unregister_commands_match(context, "%s", cmd_prefix);
474 }
475
476 static int unregister_command(struct command_context *context,
477 const char *cmd_prefix, const char *name)
478 {
479 if (!context || !name)
480 return ERROR_COMMAND_SYNTAX_ERROR;
481
482 if (!cmd_prefix || !*cmd_prefix)
483 return unregister_commands_match(context, "%s", name);
484
485 return unregister_commands_match(context, "%s %s", cmd_prefix, name);
486 }
487
488 void command_output_text(struct command_context *context, const char *data)
489 {
490 if (context && context->output_handler && data)
491 context->output_handler(context, data);
492 }
493
494 void command_print_sameline(struct command_invocation *cmd, const char *format, ...)
495 {
496 char *string;
497
498 va_list ap;
499 va_start(ap, format);
500
501 string = alloc_vprintf(format, ap);
502 if (string != NULL && cmd) {
503 /* we want this collected in the log + we also want to pick it up as a tcl return
504 * value.
505 *
506 * The latter bit isn't precisely neat, but will do for now.
507 */
508 Jim_AppendString(cmd->ctx->interp, cmd->output, string, -1);
509 /* We already printed it above
510 * command_output_text(context, string); */
511 free(string);
512 }
513
514 va_end(ap);
515 }
516
517 void command_print(struct command_invocation *cmd, const char *format, ...)
518 {
519 char *string;
520
521 va_list ap;
522 va_start(ap, format);
523
524 string = alloc_vprintf(format, ap);
525 if (string != NULL && cmd) {
526 strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one
527 *char longer */
528 /* we want this collected in the log + we also want to pick it up as a tcl return
529 * value.
530 *
531 * The latter bit isn't precisely neat, but will do for now.
532 */
533 Jim_AppendString(cmd->ctx->interp, cmd->output, string, -1);
534 /* We already printed it above
535 * command_output_text(context, string); */
536 free(string);
537 }
538
539 va_end(ap);
540 }
541
542 static bool command_can_run(struct command_context *cmd_ctx, struct command *c, const char *full_name)
543 {
544 if (c->mode == COMMAND_ANY || c->mode == cmd_ctx->mode)
545 return true;
546
547 /* Many commands may be run only before/after 'init' */
548 const char *when;
549 switch (c->mode) {
550 case COMMAND_CONFIG:
551 when = "before";
552 break;
553 case COMMAND_EXEC:
554 when = "after";
555 break;
556 /* handle the impossible with humor; it guarantees a bug report! */
557 default:
558 when = "if Cthulhu is summoned by";
559 break;
560 }
561 LOG_ERROR("The '%s' command must be used %s 'init'.",
562 full_name ? full_name : c->name, when);
563 return false;
564 }
565
566 static int run_command(struct command_context *context,
567 struct command *c, const char **words, unsigned num_words)
568 {
569 struct command_invocation cmd = {
570 .ctx = context,
571 .current = c,
572 .name = c->name,
573 .argc = num_words - 1,
574 .argv = words + 1,
575 };
576
577 cmd.output = Jim_NewEmptyStringObj(context->interp);
578 Jim_IncrRefCount(cmd.output);
579
580 int retval = c->handler(&cmd);
581 if (retval == ERROR_COMMAND_SYNTAX_ERROR) {
582 /* Print help for command */
583 command_run_linef(context, "usage %s", words[0]);
584 } else if (retval == ERROR_COMMAND_CLOSE_CONNECTION) {
585 /* just fall through for a shutdown request */
586 } else {
587 if (retval != ERROR_OK)
588 LOG_DEBUG("Command '%s' failed with error code %d",
589 words[0], retval);
590 /* Use the command output as the Tcl result */
591 Jim_SetResult(context->interp, cmd.output);
592 }
593 Jim_DecrRefCount(context->interp, cmd.output);
594
595 return retval;
596 }
597
598 int command_run_line(struct command_context *context, char *line)
599 {
600 /* all the parent commands have been registered with the interpreter
601 * so, can just evaluate the line as a script and check for
602 * results
603 */
604 /* run the line thru a script engine */
605 int retval = ERROR_FAIL;
606 int retcode;
607 /* Beware! This code needs to be reentrant. It is also possible
608 * for OpenOCD commands to be invoked directly from Tcl. This would
609 * happen when the Jim Tcl interpreter is provided by eCos for
610 * instance.
611 */
612 struct target *saved_target_override = context->current_target_override;
613 context->current_target_override = NULL;
614
615 Jim_Interp *interp = context->interp;
616 struct command_context *old_context = Jim_GetAssocData(interp, "context");
617 Jim_DeleteAssocData(interp, "context");
618 retcode = Jim_SetAssocData(interp, "context", NULL, context);
619 if (retcode == JIM_OK) {
620 /* associated the return value */
621 Jim_DeleteAssocData(interp, "retval");
622 retcode = Jim_SetAssocData(interp, "retval", NULL, &retval);
623 if (retcode == JIM_OK) {
624 retcode = Jim_Eval_Named(interp, line, 0, 0);
625
626 Jim_DeleteAssocData(interp, "retval");
627 }
628 Jim_DeleteAssocData(interp, "context");
629 int inner_retcode = Jim_SetAssocData(interp, "context", NULL, old_context);
630 if (retcode == JIM_OK)
631 retcode = inner_retcode;
632 }
633 context->current_target_override = saved_target_override;
634 if (retcode == JIM_OK) {
635 const char *result;
636 int reslen;
637
638 result = Jim_GetString(Jim_GetResult(interp), &reslen);
639 if (reslen > 0) {
640 command_output_text(context, result);
641 command_output_text(context, "\n");
642 }
643 retval = ERROR_OK;
644 } else if (retcode == JIM_EXIT) {
645 /* ignore.
646 * exit(Jim_GetExitCode(interp)); */
647 } else if (retcode == ERROR_COMMAND_CLOSE_CONNECTION) {
648 return retcode;
649 } else {
650 Jim_MakeErrorMessage(interp);
651 /* error is broadcast */
652 LOG_USER("%s", Jim_GetString(Jim_GetResult(interp), NULL));
653
654 if (retval == ERROR_OK) {
655 /* It wasn't a low level OpenOCD command that failed */
656 return ERROR_FAIL;
657 }
658 return retval;
659 }
660
661 return retval;
662 }
663
664 int command_run_linef(struct command_context *context, const char *format, ...)
665 {
666 int retval = ERROR_FAIL;
667 char *string;
668 va_list ap;
669 va_start(ap, format);
670 string = alloc_vprintf(format, ap);
671 if (string != NULL) {
672 retval = command_run_line(context, string);
673 free(string);
674 }
675 va_end(ap);
676 return retval;
677 }
678
679 void command_set_output_handler(struct command_context *context,
680 command_output_handler_t output_handler, void *priv)
681 {
682 context->output_handler = output_handler;
683 context->output_handler_priv = priv;
684 }
685
686 struct command_context *copy_command_context(struct command_context *context)
687 {
688 struct command_context *copy_context = malloc(sizeof(struct command_context));
689
690 *copy_context = *context;
691
692 return copy_context;
693 }
694
695 void command_done(struct command_context *cmd_ctx)
696 {
697 if (NULL == cmd_ctx)
698 return;
699
700 free(cmd_ctx);
701 }
702
703 /* find full path to file */
704 static int jim_find(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
705 {
706 if (argc != 2)
707 return JIM_ERR;
708 const char *file = Jim_GetString(argv[1], NULL);
709 char *full_path = find_file(file);
710 if (full_path == NULL)
711 return JIM_ERR;
712 Jim_Obj *result = Jim_NewStringObj(interp, full_path, strlen(full_path));
713 free(full_path);
714
715 Jim_SetResult(interp, result);
716 return JIM_OK;
717 }
718
719 COMMAND_HANDLER(jim_echo)
720 {
721 if (CMD_ARGC == 2 && !strcmp(CMD_ARGV[0], "-n")) {
722 LOG_USER_N("%s", CMD_ARGV[1]);
723 return JIM_OK;
724 }
725 if (CMD_ARGC != 1)
726 return JIM_ERR;
727 LOG_USER("%s", CMD_ARGV[0]);
728 return JIM_OK;
729 }
730
731 /* Capture progress output and return as tcl return value. If the
732 * progress output was empty, return tcl return value.
733 */
734 static int jim_capture(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
735 {
736 if (argc != 2)
737 return JIM_ERR;
738
739 struct log_capture_state *state = command_log_capture_start(interp);
740
741 /* disable polling during capture. This avoids capturing output
742 * from polling.
743 *
744 * This is necessary in order to avoid accidentally getting a non-empty
745 * string for tcl fn's.
746 */
747 bool save_poll = jtag_poll_get_enabled();
748
749 jtag_poll_set_enabled(false);
750
751 const char *str = Jim_GetString(argv[1], NULL);
752 int retcode = Jim_Eval_Named(interp, str, __THIS__FILE__, __LINE__);
753
754 jtag_poll_set_enabled(save_poll);
755
756 command_log_capture_finish(state);
757
758 return retcode;
759 }
760
761 struct help_entry {
762 struct list_head lh;
763 char *cmd_name;
764 char *help;
765 char *usage;
766 };
767
768 static COMMAND_HELPER(command_help_show, struct help_entry *c,
769 bool show_help, const char *cmd_match);
770
771 static COMMAND_HELPER(command_help_show_list, bool show_help, const char *cmd_match)
772 {
773 struct help_entry *entry;
774
775 list_for_each_entry(entry, CMD_CTX->help_list, lh)
776 CALL_COMMAND_HANDLER(command_help_show, entry, show_help, cmd_match);
777 return ERROR_OK;
778 }
779
780 #define HELP_LINE_WIDTH(_n) (int)(76 - (2 * _n))
781
782 static void command_help_show_indent(unsigned n)
783 {
784 for (unsigned i = 0; i < n; i++)
785 LOG_USER_N(" ");
786 }
787 static void command_help_show_wrap(const char *str, unsigned n, unsigned n2)
788 {
789 const char *cp = str, *last = str;
790 while (*cp) {
791 const char *next = last;
792 do {
793 cp = next;
794 do {
795 next++;
796 } while (*next != ' ' && *next != '\t' && *next != '\0');
797 } while ((next - last < HELP_LINE_WIDTH(n)) && *next != '\0');
798 if (next - last < HELP_LINE_WIDTH(n))
799 cp = next;
800 command_help_show_indent(n);
801 LOG_USER("%.*s", (int)(cp - last), last);
802 last = cp + 1;
803 n = n2;
804 }
805 }
806
807 static COMMAND_HELPER(command_help_show, struct help_entry *c,
808 bool show_help, const char *cmd_match)
809 {
810 unsigned int n = 0;
811 for (const char *s = strchr(c->cmd_name, ' '); s; s = strchr(s + 1, ' '))
812 n++;
813
814 /* If the match string occurs anywhere, we print out
815 * stuff for this command. */
816 bool is_match = (strstr(c->cmd_name, cmd_match) != NULL) ||
817 ((c->usage != NULL) && (strstr(c->usage, cmd_match) != NULL)) ||
818 ((c->help != NULL) && (strstr(c->help, cmd_match) != NULL));
819
820 if (is_match) {
821 command_help_show_indent(n);
822 LOG_USER_N("%s", c->cmd_name);
823
824 if (c->usage && strlen(c->usage) > 0) {
825 LOG_USER_N(" ");
826 command_help_show_wrap(c->usage, 0, n + 5);
827 } else
828 LOG_USER_N("\n");
829 }
830
831 if (is_match && show_help) {
832 char *msg;
833
834 /* TODO: factorize jim_command_mode() to avoid running jim command here */
835 char *request = alloc_printf("command mode %s", c->cmd_name);
836 if (!request) {
837 LOG_ERROR("Out of memory");
838 return ERROR_FAIL;
839 }
840 int retval = Jim_Eval(CMD_CTX->interp, request);
841 free(request);
842 enum command_mode mode = COMMAND_UNKNOWN;
843 if (retval != JIM_ERR) {
844 const char *result = Jim_GetString(Jim_GetResult(CMD_CTX->interp), NULL);
845 if (!strcmp(result, "any"))
846 mode = COMMAND_ANY;
847 else if (!strcmp(result, "config"))
848 mode = COMMAND_CONFIG;
849 else if (!strcmp(result, "exec"))
850 mode = COMMAND_EXEC;
851 }
852
853 /* Normal commands are runtime-only; highlight exceptions */
854 if (mode != COMMAND_EXEC) {
855 const char *stage_msg = "";
856
857 switch (mode) {
858 case COMMAND_CONFIG:
859 stage_msg = " (configuration command)";
860 break;
861 case COMMAND_ANY:
862 stage_msg = " (command valid any time)";
863 break;
864 default:
865 stage_msg = " (?mode error?)";
866 break;
867 }
868 msg = alloc_printf("%s%s", c->help ? : "", stage_msg);
869 } else
870 msg = alloc_printf("%s", c->help ? : "");
871
872 if (NULL != msg) {
873 command_help_show_wrap(msg, n + 3, n + 3);
874 free(msg);
875 } else
876 return -ENOMEM;
877 }
878
879 return ERROR_OK;
880 }
881
882 COMMAND_HANDLER(handle_help_command)
883 {
884 bool full = strcmp(CMD_NAME, "help") == 0;
885 int retval;
886 char *cmd_match;
887
888 if (CMD_ARGC <= 0)
889 cmd_match = strdup("");
890
891 else {
892 cmd_match = strdup(CMD_ARGV[0]);
893
894 for (unsigned int i = 1; i < CMD_ARGC && cmd_match; ++i) {
895 char *prev = cmd_match;
896 cmd_match = alloc_printf("%s %s", prev, CMD_ARGV[i]);
897 free(prev);
898 }
899 }
900
901 if (cmd_match == NULL) {
902 LOG_ERROR("unable to build search string");
903 return -ENOMEM;
904 }
905 retval = CALL_COMMAND_HANDLER(command_help_show_list, full, cmd_match);
906
907 free(cmd_match);
908 return retval;
909 }
910
911 static char *alloc_concatenate_strings(int argc, Jim_Obj * const *argv)
912 {
913 char *prev, *all;
914 int i;
915
916 assert(argc >= 1);
917
918 all = strdup(Jim_GetString(argv[0], NULL));
919 if (!all) {
920 LOG_ERROR("Out of memory");
921 return NULL;
922 }
923
924 for (i = 1; i < argc; ++i) {
925 prev = all;
926 all = alloc_printf("%s %s", all, Jim_GetString(argv[i], NULL));
927 free(prev);
928 if (!all) {
929 LOG_ERROR("Out of memory");
930 return NULL;
931 }
932 }
933
934 return all;
935 }
936
937 static int exec_command(Jim_Interp *interp, struct command_context *cmd_ctx,
938 struct command *c, int argc, Jim_Obj * const *argv)
939 {
940 if (c->jim_handler)
941 return c->jim_handler(interp, argc, argv);
942
943 /* use c->handler */
944 unsigned int nwords;
945 char **words = script_command_args_alloc(argc, argv, &nwords);
946 if (!words)
947 return JIM_ERR;
948
949 int retval = run_command(cmd_ctx, c, (const char **)words, nwords);
950 script_command_args_free(words, nwords);
951 return command_retval_set(interp, retval);
952 }
953
954 static int jim_command_dispatch(Jim_Interp *interp, int argc, Jim_Obj * const *argv)
955 {
956 script_debug(interp, argc, argv);
957
958 /* check subcommands */
959 if (argc > 1) {
960 char *s = alloc_printf("%s %s", Jim_GetString(argv[0], NULL), Jim_GetString(argv[1], NULL));
961 Jim_Obj *js = Jim_NewStringObj(interp, s, -1);
962 Jim_IncrRefCount(js);
963 free(s);
964 Jim_Cmd *cmd = Jim_GetCommand(interp, js, JIM_NONE);
965 if (cmd) {
966 int retval = Jim_EvalObjPrefix(interp, js, argc - 2, argv + 2);
967 Jim_DecrRefCount(interp, js);
968 return retval;
969 }
970 Jim_DecrRefCount(interp, js);
971 }
972
973 struct command *c = jim_to_command(interp);
974 if (!c->jim_handler && !c->handler) {
975 Jim_EvalObjPrefix(interp, Jim_NewStringObj(interp, "usage", -1), 1, argv);
976 return JIM_ERR;
977 }
978
979 struct command_context *cmd_ctx = current_command_context(interp);
980
981 if (!command_can_run(cmd_ctx, c, Jim_GetString(argv[0], NULL)))
982 return JIM_ERR;
983
984 target_call_timer_callbacks_now();
985
986 /*
987 * Black magic of overridden current target:
988 * If the command we are going to handle has a target prefix,
989 * override the current target temporarily for the time
990 * of processing the command.
991 * current_target_override is used also for event handlers
992 * therefore we prevent touching it if command has no prefix.
993 * Previous override is saved and restored back to ensure
994 * correct work when jim_command_dispatch() is re-entered.
995 */
996 struct target *saved_target_override = cmd_ctx->current_target_override;
997 if (c->jim_override_target)
998 cmd_ctx->current_target_override = c->jim_override_target;
999
1000 int retval = exec_command(interp, cmd_ctx, c, argc, argv);
1001
1002 if (c->jim_override_target)
1003 cmd_ctx->current_target_override = saved_target_override;
1004
1005 return retval;
1006 }
1007
1008 static int jim_command_mode(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
1009 {
1010 struct command_context *cmd_ctx = current_command_context(interp);
1011 enum command_mode mode;
1012
1013 if (argc > 1) {
1014 char *full_name = alloc_concatenate_strings(argc - 1, argv + 1);
1015 if (!full_name)
1016 return JIM_ERR;
1017 Jim_Obj *s = Jim_NewStringObj(interp, full_name, -1);
1018 Jim_IncrRefCount(s);
1019 Jim_Cmd *cmd = Jim_GetCommand(interp, s, JIM_NONE);
1020 Jim_DecrRefCount(interp, s);
1021 free(full_name);
1022 if (!cmd || !(jimcmd_is_proc(cmd) || jimcmd_is_ocd_command(cmd))) {
1023 Jim_SetResultString(interp, "unknown", -1);
1024 return JIM_OK;
1025 }
1026
1027 if (jimcmd_is_proc(cmd)) {
1028 /* tcl proc */
1029 mode = COMMAND_ANY;
1030 } else {
1031 struct command *c = jimcmd_privdata(cmd);
1032
1033 mode = c->mode;
1034 }
1035 } else
1036 mode = cmd_ctx->mode;
1037
1038 const char *mode_str;
1039 switch (mode) {
1040 case COMMAND_ANY:
1041 mode_str = "any";
1042 break;
1043 case COMMAND_CONFIG:
1044 mode_str = "config";
1045 break;
1046 case COMMAND_EXEC:
1047 mode_str = "exec";
1048 break;
1049 default:
1050 mode_str = "unknown";
1051 break;
1052 }
1053 Jim_SetResultString(interp, mode_str, -1);
1054 return JIM_OK;
1055 }
1056
1057 int help_del_all_commands(struct command_context *cmd_ctx)
1058 {
1059 struct help_entry *curr, *n;
1060
1061 list_for_each_entry_safe(curr, n, cmd_ctx->help_list, lh) {
1062 list_del(&curr->lh);
1063 free(curr->cmd_name);
1064 free(curr->help);
1065 free(curr->usage);
1066 free(curr);
1067 }
1068 return ERROR_OK;
1069 }
1070
1071 static int help_del_command(struct command_context *cmd_ctx, const char *cmd_name)
1072 {
1073 struct help_entry *curr;
1074
1075 list_for_each_entry(curr, cmd_ctx->help_list, lh) {
1076 if (!strcmp(cmd_name, curr->cmd_name)) {
1077 list_del(&curr->lh);
1078 free(curr->cmd_name);
1079 free(curr->help);
1080 free(curr->usage);
1081 free(curr);
1082 break;
1083 }
1084 }
1085
1086 return ERROR_OK;
1087 }
1088
1089 static int help_add_command(struct command_context *cmd_ctx,
1090 const char *cmd_name, const char *help_text, const char *usage_text)
1091 {
1092 int cmp = -1; /* add after curr */
1093 struct help_entry *curr;
1094
1095 list_for_each_entry_reverse(curr, cmd_ctx->help_list, lh) {
1096 cmp = strcmp(cmd_name, curr->cmd_name);
1097 if (cmp >= 0)
1098 break;
1099 }
1100
1101 struct help_entry *entry;
1102 if (cmp) {
1103 entry = calloc(1, sizeof(*entry));
1104 if (!entry) {
1105 LOG_ERROR("Out of memory");
1106 return ERROR_FAIL;
1107 }
1108 entry->cmd_name = strdup(cmd_name);
1109 if (!entry->cmd_name) {
1110 LOG_ERROR("Out of memory");
1111 free(entry);
1112 return ERROR_FAIL;
1113 }
1114 list_add(&entry->lh, &curr->lh);
1115 } else {
1116 entry = curr;
1117 }
1118
1119 if (help_text) {
1120 char *text = strdup(help_text);
1121 if (!text) {
1122 LOG_ERROR("Out of memory");
1123 return ERROR_FAIL;
1124 }
1125 free(entry->help);
1126 entry->help = text;
1127 }
1128
1129 if (usage_text) {
1130 char *text = strdup(usage_text);
1131 if (!text) {
1132 LOG_ERROR("Out of memory");
1133 return ERROR_FAIL;
1134 }
1135 free(entry->usage);
1136 entry->usage = text;
1137 }
1138
1139 return ERROR_OK;
1140 }
1141
1142 COMMAND_HANDLER(handle_help_add_command)
1143 {
1144 if (CMD_ARGC != 2)
1145 return ERROR_COMMAND_SYNTAX_ERROR;
1146
1147 const char *help = !strcmp(CMD_NAME, "add_help_text") ? CMD_ARGV[1] : NULL;
1148 const char *usage = !strcmp(CMD_NAME, "add_usage_text") ? CMD_ARGV[1] : NULL;
1149 if (!help && !usage) {
1150 LOG_ERROR("command name '%s' is unknown", CMD_NAME);
1151 return ERROR_COMMAND_SYNTAX_ERROR;
1152 }
1153 const char *cmd_name = CMD_ARGV[0];
1154 return help_add_command(CMD_CTX, cmd_name, help, usage);
1155 }
1156
1157 /* sleep command sleeps for <n> milliseconds
1158 * this is useful in target startup scripts
1159 */
1160 COMMAND_HANDLER(handle_sleep_command)
1161 {
1162 bool busy = false;
1163 if (CMD_ARGC == 2) {
1164 if (strcmp(CMD_ARGV[1], "busy") == 0)
1165 busy = true;
1166 else
1167 return ERROR_COMMAND_SYNTAX_ERROR;
1168 } else if (CMD_ARGC < 1 || CMD_ARGC > 2)
1169 return ERROR_COMMAND_SYNTAX_ERROR;
1170
1171 unsigned long duration = 0;
1172 int retval = parse_ulong(CMD_ARGV[0], &duration);
1173 if (ERROR_OK != retval)
1174 return retval;
1175
1176 if (!busy) {
1177 int64_t then = timeval_ms();
1178 while (timeval_ms() - then < (int64_t)duration) {
1179 target_call_timer_callbacks_now();
1180 usleep(1000);
1181 }
1182 } else
1183 busy_sleep(duration);
1184
1185 return ERROR_OK;
1186 }
1187
1188 static const struct command_registration command_subcommand_handlers[] = {
1189 {
1190 .name = "mode",
1191 .mode = COMMAND_ANY,
1192 .jim_handler = jim_command_mode,
1193 .usage = "[command_name ...]",
1194 .help = "Returns the command modes allowed by a command: "
1195 "'any', 'config', or 'exec'. If no command is "
1196 "specified, returns the current command mode. "
1197 "Returns 'unknown' if an unknown command is given. "
1198 "Command can be multiple tokens.",
1199 },
1200 COMMAND_REGISTRATION_DONE
1201 };
1202
1203 static const struct command_registration command_builtin_handlers[] = {
1204 {
1205 .name = "ocd_find",
1206 .mode = COMMAND_ANY,
1207 .jim_handler = jim_find,
1208 .help = "find full path to file",
1209 .usage = "file",
1210 },
1211 {
1212 .name = "capture",
1213 .mode = COMMAND_ANY,
1214 .jim_handler = jim_capture,
1215 .help = "Capture progress output and return as tcl return value. If the "
1216 "progress output was empty, return tcl return value.",
1217 .usage = "command",
1218 },
1219 {
1220 .name = "echo",
1221 .handler = jim_echo,
1222 .mode = COMMAND_ANY,
1223 .help = "Logs a message at \"user\" priority. "
1224 "Output message to stdout. "
1225 "Option \"-n\" suppresses trailing newline",
1226 .usage = "[-n] string",
1227 },
1228 {
1229 .name = "add_help_text",
1230 .handler = handle_help_add_command,
1231 .mode = COMMAND_ANY,
1232 .help = "Add new command help text; "
1233 "Command can be multiple tokens.",
1234 .usage = "command_name helptext_string",
1235 },
1236 {
1237 .name = "add_usage_text",
1238 .handler = handle_help_add_command,
1239 .mode = COMMAND_ANY,
1240 .help = "Add new command usage text; "
1241 "command can be multiple tokens.",
1242 .usage = "command_name usage_string",
1243 },
1244 {
1245 .name = "sleep",
1246 .handler = handle_sleep_command,
1247 .mode = COMMAND_ANY,
1248 .help = "Sleep for specified number of milliseconds. "
1249 "\"busy\" will busy wait instead (avoid this).",
1250 .usage = "milliseconds ['busy']",
1251 },
1252 {
1253 .name = "help",
1254 .handler = handle_help_command,
1255 .mode = COMMAND_ANY,
1256 .help = "Show full command help; "
1257 "command can be multiple tokens.",
1258 .usage = "[command_name]",
1259 },
1260 {
1261 .name = "usage",
1262 .handler = handle_help_command,
1263 .mode = COMMAND_ANY,
1264 .help = "Show basic command usage; "
1265 "command can be multiple tokens.",
1266 .usage = "[command_name]",
1267 },
1268 {
1269 .name = "command",
1270 .mode = COMMAND_ANY,
1271 .help = "core command group (introspection)",
1272 .chain = command_subcommand_handlers,
1273 .usage = "",
1274 },
1275 COMMAND_REGISTRATION_DONE
1276 };
1277
1278 struct command_context *command_init(const char *startup_tcl, Jim_Interp *interp)
1279 {
1280 struct command_context *context = calloc(1, sizeof(struct command_context));
1281
1282 context->mode = COMMAND_EXEC;
1283
1284 /* context can be duplicated. Put list head on separate mem-chunk to keep list consistent */
1285 context->help_list = malloc(sizeof(*context->help_list));
1286 INIT_LIST_HEAD(context->help_list);
1287
1288 /* Create a jim interpreter if we were not handed one */
1289 if (interp == NULL) {
1290 /* Create an interpreter */
1291 interp = Jim_CreateInterp();
1292 /* Add all the Jim core commands */
1293 Jim_RegisterCoreCommands(interp);
1294 Jim_InitStaticExtensions(interp);
1295 }
1296
1297 context->interp = interp;
1298
1299 register_commands(context, NULL, command_builtin_handlers);
1300
1301 Jim_SetAssocData(interp, "context", NULL, context);
1302 if (Jim_Eval_Named(interp, startup_tcl, "embedded:startup.tcl", 1) == JIM_ERR) {
1303 LOG_ERROR("Failed to run startup.tcl (embedded into OpenOCD)");
1304 Jim_MakeErrorMessage(interp);
1305 LOG_USER_N("%s", Jim_GetString(Jim_GetResult(interp), NULL));
1306 exit(-1);
1307 }
1308 Jim_DeleteAssocData(interp, "context");
1309
1310 return context;
1311 }
1312
1313 void command_exit(struct command_context *context)
1314 {
1315 if (!context)
1316 return;
1317
1318 Jim_FreeInterp(context->interp);
1319 free(context->help_list);
1320 command_done(context);
1321 }
1322
1323 int command_context_mode(struct command_context *cmd_ctx, enum command_mode mode)
1324 {
1325 if (!cmd_ctx)
1326 return ERROR_COMMAND_SYNTAX_ERROR;
1327
1328 cmd_ctx->mode = mode;
1329 return ERROR_OK;
1330 }
1331
1332 void process_jim_events(struct command_context *cmd_ctx)
1333 {
1334 static int recursion;
1335 if (recursion)
1336 return;
1337
1338 recursion++;
1339 Jim_ProcessEvents(cmd_ctx->interp, JIM_ALL_EVENTS | JIM_DONT_WAIT);
1340 recursion--;
1341 }
1342
1343 #define DEFINE_PARSE_NUM_TYPE(name, type, func, min, max) \
1344 int parse ## name(const char *str, type * ul) \
1345 { \
1346 if (!*str) { \
1347 LOG_ERROR("Invalid command argument"); \
1348 return ERROR_COMMAND_ARGUMENT_INVALID; \
1349 } \
1350 char *end; \
1351 errno = 0; \
1352 *ul = func(str, &end, 0); \
1353 if (*end) { \
1354 LOG_ERROR("Invalid command argument"); \
1355 return ERROR_COMMAND_ARGUMENT_INVALID; \
1356 } \
1357 if ((max == *ul) && (ERANGE == errno)) { \
1358 LOG_ERROR("Argument overflow"); \
1359 return ERROR_COMMAND_ARGUMENT_OVERFLOW; \
1360 } \
1361 if (min && (min == *ul) && (ERANGE == errno)) { \
1362 LOG_ERROR("Argument underflow"); \
1363 return ERROR_COMMAND_ARGUMENT_UNDERFLOW; \
1364 } \
1365 return ERROR_OK; \
1366 }
1367 DEFINE_PARSE_NUM_TYPE(_ulong, unsigned long, strtoul, 0, ULONG_MAX)
1368 DEFINE_PARSE_NUM_TYPE(_ullong, unsigned long long, strtoull, 0, ULLONG_MAX)
1369 DEFINE_PARSE_NUM_TYPE(_long, long, strtol, LONG_MIN, LONG_MAX)
1370 DEFINE_PARSE_NUM_TYPE(_llong, long long, strtoll, LLONG_MIN, LLONG_MAX)
1371
1372 #define DEFINE_PARSE_WRAPPER(name, type, min, max, functype, funcname) \
1373 int parse ## name(const char *str, type * ul) \
1374 { \
1375 functype n; \
1376 int retval = parse ## funcname(str, &n); \
1377 if (ERROR_OK != retval) \
1378 return retval; \
1379 if (n > max) \
1380 return ERROR_COMMAND_ARGUMENT_OVERFLOW; \
1381 if (min) \
1382 return ERROR_COMMAND_ARGUMENT_UNDERFLOW; \
1383 *ul = n; \
1384 return ERROR_OK; \
1385 }
1386
1387 #define DEFINE_PARSE_ULONGLONG(name, type, min, max) \
1388 DEFINE_PARSE_WRAPPER(name, type, min, max, unsigned long long, _ullong)
1389 DEFINE_PARSE_ULONGLONG(_uint, unsigned, 0, UINT_MAX)
1390 DEFINE_PARSE_ULONGLONG(_u64, uint64_t, 0, UINT64_MAX)
1391 DEFINE_PARSE_ULONGLONG(_u32, uint32_t, 0, UINT32_MAX)
1392 DEFINE_PARSE_ULONGLONG(_u16, uint16_t, 0, UINT16_MAX)
1393 DEFINE_PARSE_ULONGLONG(_u8, uint8_t, 0, UINT8_MAX)
1394
1395 DEFINE_PARSE_ULONGLONG(_target_addr, target_addr_t, 0, TARGET_ADDR_MAX)
1396
1397 #define DEFINE_PARSE_LONGLONG(name, type, min, max) \
1398 DEFINE_PARSE_WRAPPER(name, type, min, max, long long, _llong)
1399 DEFINE_PARSE_LONGLONG(_int, int, n < INT_MIN, INT_MAX)
1400 DEFINE_PARSE_LONGLONG(_s64, int64_t, n < INT64_MIN, INT64_MAX)
1401 DEFINE_PARSE_LONGLONG(_s32, int32_t, n < INT32_MIN, INT32_MAX)
1402 DEFINE_PARSE_LONGLONG(_s16, int16_t, n < INT16_MIN, INT16_MAX)
1403 DEFINE_PARSE_LONGLONG(_s8, int8_t, n < INT8_MIN, INT8_MAX)
1404
1405 static int command_parse_bool(const char *in, bool *out,
1406 const char *on, const char *off)
1407 {
1408 if (strcasecmp(in, on) == 0)
1409 *out = true;
1410 else if (strcasecmp(in, off) == 0)
1411 *out = false;
1412 else
1413 return ERROR_COMMAND_SYNTAX_ERROR;
1414 return ERROR_OK;
1415 }
1416
1417 int command_parse_bool_arg(const char *in, bool *out)
1418 {
1419 if (command_parse_bool(in, out, "on", "off") == ERROR_OK)
1420 return ERROR_OK;
1421 if (command_parse_bool(in, out, "enable", "disable") == ERROR_OK)
1422 return ERROR_OK;
1423 if (command_parse_bool(in, out, "true", "false") == ERROR_OK)
1424 return ERROR_OK;
1425 if (command_parse_bool(in, out, "yes", "no") == ERROR_OK)
1426 return ERROR_OK;
1427 if (command_parse_bool(in, out, "1", "0") == ERROR_OK)
1428 return ERROR_OK;
1429 return ERROR_COMMAND_SYNTAX_ERROR;
1430 }
1431
1432 COMMAND_HELPER(handle_command_parse_bool, bool *out, const char *label)
1433 {
1434 switch (CMD_ARGC) {
1435 case 1: {
1436 const char *in = CMD_ARGV[0];
1437 if (command_parse_bool_arg(in, out) != ERROR_OK) {
1438 LOG_ERROR("%s: argument '%s' is not valid", CMD_NAME, in);
1439 return ERROR_COMMAND_SYNTAX_ERROR;
1440 }
1441 }
1442 /* fallthrough */
1443 case 0:
1444 LOG_INFO("%s is %s", label, *out ? "enabled" : "disabled");
1445 break;
1446 default:
1447 return ERROR_COMMAND_SYNTAX_ERROR;
1448 }
1449 return ERROR_OK;
1450 }

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)