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