+void command_set_output_handler(struct command_context* context,
+ command_output_handler_t output_handler, void *priv)
+{
+ context->output_handler = output_handler;
+ context->output_handler_priv = priv;
+}
+
+struct command_context* copy_command_context(struct command_context* context)
+{
+ struct command_context* copy_context = malloc(sizeof(struct command_context));
+
+ *copy_context = *context;
+
+ return copy_context;
+}
+
+void command_done(struct command_context *cmd_ctx)
+{
+ if (NULL == cmd_ctx)
+ return;
+
+ free(cmd_ctx);
+}
+
+/* find full path to file */
+static int jim_find(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ if (argc != 2)
+ return JIM_ERR;
+ const char *file = Jim_GetString(argv[1], NULL);
+ char *full_path = find_file(file);
+ if (full_path == NULL)
+ return JIM_ERR;
+ Jim_Obj *result = Jim_NewStringObj(interp, full_path, strlen(full_path));
+ free(full_path);
+
+ Jim_SetResult(interp, result);
+ return JIM_OK;
+}
+
+static int jim_echo(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ if (argc != 2)
+ return JIM_ERR;
+ const char *str = Jim_GetString(argv[1], NULL);
+ LOG_USER("%s", str);
+ return JIM_OK;
+}
+
+static size_t openocd_jim_fwrite(const void *_ptr, size_t size, size_t n, void *cookie)
+{
+ size_t nbytes;
+ const char *ptr;
+ Jim_Interp *interp;
+
+ /* make it a char easier to read code */
+ ptr = _ptr;
+ interp = cookie;
+ nbytes = size * n;
+ if (ptr == NULL || interp == NULL || nbytes == 0) {
+ return 0;
+ }
+
+ /* do we have to chunk it? */
+ if (ptr[nbytes] == 0)
+ {
+ /* no it is a C style string */
+ LOG_USER_N("%s", ptr);
+ return strlen(ptr);
+ }
+ /* GRR we must chunk - not null terminated */
+ while (nbytes) {
+ char chunk[128 + 1];
+ int x;
+
+ x = nbytes;
+ if (x > 128) {
+ x = 128;
+ }
+ /* copy it */
+ memcpy(chunk, ptr, x);
+ /* terminate it */
+ chunk[n] = 0;
+ /* output it */
+ LOG_USER_N("%s", chunk);
+ ptr += x;
+ nbytes -= x;
+ }
+
+ return n;
+}
+
+static size_t openocd_jim_fread(void *ptr, size_t size, size_t n, void *cookie)
+{
+ /* TCL wants to read... tell him no */
+ return 0;
+}
+
+static int openocd_jim_vfprintf(void *cookie, const char *fmt, va_list ap)
+{
+ char *cp;
+ int n;
+ Jim_Interp *interp;
+
+ n = -1;
+ interp = cookie;
+ if (interp == NULL)
+ return n;
+
+ cp = alloc_vprintf(fmt, ap);
+ if (cp)
+ {
+ LOG_USER_N("%s", cp);
+ n = strlen(cp);
+ free(cp);
+ }
+ return n;
+}
+
+static int openocd_jim_fflush(void *cookie)
+{
+ /* nothing to flush */
+ return 0;
+}
+
+static char* openocd_jim_fgets(char *s, int size, void *cookie)
+{
+ /* not supported */
+ errno = ENOTSUP;
+ return NULL;
+}
+
+static int jim_capture(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
+{
+ if (argc != 2)
+ return JIM_ERR;
+
+ struct log_capture_state *state = command_log_capture_start(interp);
+
+ const char *str = Jim_GetString(argv[1], NULL);
+ int retcode = Jim_Eval_Named(interp, str, __THIS__FILE__, __LINE__);
+
+ command_log_capture_finish(state);
+
+ return retcode;
+}
+
+static COMMAND_HELPER(command_help_find, struct command *head,
+ struct command **out)
+{
+ if (0 == CMD_ARGC)
+ return ERROR_INVALID_ARGUMENTS;
+ *out = command_find(head, CMD_ARGV[0]);
+ if (NULL == *out && strncmp(CMD_ARGV[0], "ocd_", 4) == 0)
+ *out = command_find(head, CMD_ARGV[0] + 4);
+ if (NULL == *out)
+ return ERROR_INVALID_ARGUMENTS;
+ if (--CMD_ARGC == 0)
+ return ERROR_OK;
+ CMD_ARGV++;
+ return CALL_COMMAND_HANDLER(command_help_find, (*out)->children, out);
+}
+
+static COMMAND_HELPER(command_help_show, struct command *c, unsigned n,
+ bool show_help, const char *match);
+
+static COMMAND_HELPER(command_help_show_list, struct command *head, unsigned n,
+ bool show_help, const char *match)
+{
+ for (struct command *c = head; NULL != c; c = c->next)
+ CALL_COMMAND_HANDLER(command_help_show, c, n, show_help, match);
+ return ERROR_OK;
+}
+
+#define HELP_LINE_WIDTH(_n) (int)(76 - (2 * _n))
+
+static void command_help_show_indent(unsigned n)
+{
+ for (unsigned i = 0; i < n; i++)
+ LOG_USER_N(" ");
+}
+static void command_help_show_wrap(const char *str, unsigned n, unsigned n2)
+{
+ const char *cp = str, *last = str;
+ while (*cp)
+ {
+ const char *next = last;
+ do {
+ cp = next;
+ do {
+ next++;
+ } while (*next != ' ' && *next != '\t' && *next != '\0');
+ } while ((next - last < HELP_LINE_WIDTH(n)) && *next != '\0');
+ if (next - last < HELP_LINE_WIDTH(n))
+ cp = next;
+ command_help_show_indent(n);
+ LOG_USER_N("%.*s", (int)(cp - last), last);
+ LOG_USER_N("\n");
+ last = cp + 1;
+ n = n2;
+ }
+}
+static COMMAND_HELPER(command_help_show, struct command *c, unsigned n,
+ bool show_help, const char *match)
+{
+ if (!command_can_run(CMD_CTX, c))
+ return ERROR_OK;
+
+ char *cmd_name = command_name(c, ' ');
+ if (NULL == cmd_name)
+ return -ENOMEM;
+
+ /* If the match string occurs anywhere, we print out
+ * stuff for this command. */
+ bool is_match = (strstr(cmd_name, match) != NULL) ||
+ ((c->usage != NULL) && (strstr(c->usage, match) != NULL)) ||
+ ((c->help != NULL) && (strstr(c->help, match) != NULL));
+
+ if (is_match)
+ {
+ command_help_show_indent(n);
+ LOG_USER_N("%s", cmd_name);
+ }
+ free(cmd_name);
+
+ if (is_match)
+ {
+ if (c->usage) {
+ LOG_USER_N(" ");
+ command_help_show_wrap(c->usage, 0, n + 5);
+ }
+ else
+ LOG_USER_N("\n");
+ }
+
+ if (is_match && show_help)
+ {
+ char *msg;
+
+ /* Normal commands are runtime-only; highlight exceptions */
+ if (c->mode != COMMAND_EXEC) {
+ const char *stage_msg = "";
+
+ switch (c->mode) {
+ case COMMAND_CONFIG:
+ stage_msg = " (configuration command)";
+ break;
+ case COMMAND_ANY:
+ stage_msg = " (command valid any time)";
+ break;
+ default:
+ stage_msg = " (?mode error?)";
+ break;
+ }
+ msg = alloc_printf("%s%s", c->help ? : "", stage_msg);
+ } else
+ msg = alloc_printf("%s", c->help ? : "");
+
+ if (NULL != msg)
+ {
+ command_help_show_wrap(msg, n + 3, n + 3);
+ free(msg);
+ } else
+ return -ENOMEM;
+ }
+
+ if (++n >= 2)
+ return ERROR_OK;
+
+ return CALL_COMMAND_HANDLER(command_help_show_list,
+ c->children, n, show_help, match);
+}
+COMMAND_HANDLER(handle_help_command)
+{
+ bool full = strcmp(CMD_NAME, "help") == 0;
+ int retval;
+ struct command *c = CMD_CTX->commands;
+ char *match = NULL;
+
+ if (CMD_ARGC == 0)
+ match = "";
+ else if (CMD_ARGC >= 1) {
+ unsigned i;
+
+ for (i = 0; i < CMD_ARGC; ++i) {
+ if (NULL != match) {
+ char *prev = match;
+
+ match = alloc_printf("%s %s", match,
+ CMD_ARGV[i]);
+ free(prev);
+ if (NULL == match) {
+ LOG_ERROR("unable to build "
+ "search string");
+ return -ENOMEM;
+ }
+ } else {
+ match = alloc_printf("%s", CMD_ARGV[i]);
+ if (NULL == match) {
+ LOG_ERROR("unable to build "
+ "search string");
+ return -ENOMEM;
+ }
+ }
+ }
+ } else
+ return ERROR_COMMAND_SYNTAX_ERROR;
+
+ retval = CALL_COMMAND_HANDLER(command_help_show_list,
+ c, 0, full, match);
+
+ if (CMD_ARGC >= 1)
+ free(match);
+ return retval;
+}
+
+static int command_unknown_find(unsigned argc, Jim_Obj *const *argv,
+ struct command *head, struct command **out, bool top_level)