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

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)