2008-02-25 11:48:04 -06:00
|
|
|
/***************************************************************************
|
|
|
|
* Copyright (C) 2005 by Dominic Rath *
|
|
|
|
* Dominic.Rath@gmx.de *
|
|
|
|
* *
|
2009-07-17 14:54:25 -05:00
|
|
|
* Copyright (C) 2007,2008 Øyvind Harboe *
|
2008-07-25 01:54:17 -05:00
|
|
|
* oyvind.harboe@zylin.com *
|
2008-09-20 05:50:53 -05:00
|
|
|
* *
|
2008-09-01 16:22:20 -05:00
|
|
|
* Copyright (C) 2008, Duane Ellis *
|
|
|
|
* openocd@duaneeellis.com *
|
2008-07-25 01:54:17 -05:00
|
|
|
* *
|
2008-02-25 11:48:04 -06:00
|
|
|
* part of this file is taken from libcli (libcli.sourceforge.net) *
|
|
|
|
* Copyright (C) David Parrish (david@dparrish.com) *
|
|
|
|
* *
|
|
|
|
* This program is free software; you can redistribute it and/or modify *
|
|
|
|
* it under the terms of the GNU General Public License as published by *
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or *
|
|
|
|
* (at your option) any later version. *
|
|
|
|
* *
|
|
|
|
* This program is distributed in the hope that it will be useful, *
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
|
|
* GNU General Public License for more details. *
|
|
|
|
* *
|
|
|
|
* You should have received a copy of the GNU General Public License *
|
|
|
|
* along with this program; if not, write to the *
|
|
|
|
* Free Software Foundation, Inc., *
|
|
|
|
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
|
|
|
|
***************************************************************************/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2009-05-15 15:35:53 -05:00
|
|
|
#if !BUILD_ECOSBOARD
|
|
|
|
/* see Embedder-HOWTO.txt in Jim Tcl project hosted on BerliOS*/
|
|
|
|
#define JIM_EMBEDDED
|
|
|
|
#endif
|
|
|
|
|
2009-05-10 22:59:02 -05:00
|
|
|
// @todo the inclusion of target.h here is a layering violation
|
2008-07-18 06:38:23 -05:00
|
|
|
#include "target.h"
|
2008-02-25 11:48:04 -06:00
|
|
|
#include "command.h"
|
2008-07-18 06:38:23 -05:00
|
|
|
#include "configuration.h"
|
2008-02-25 11:48:04 -06:00
|
|
|
#include "log.h"
|
|
|
|
#include "time_support.h"
|
2008-08-07 11:37:20 -05:00
|
|
|
#include "jim-eventloop.h"
|
2008-02-25 11:48:04 -06:00
|
|
|
|
2008-07-11 10:07:58 -05:00
|
|
|
|
2008-07-18 01:24:57 -05:00
|
|
|
Jim_Interp *interp = NULL;
|
2008-04-23 00:42:42 -05:00
|
|
|
|
2009-11-13 15:25:47 -06:00
|
|
|
static int run_command(struct command_context *context,
|
2009-11-13 15:30:50 -06:00
|
|
|
struct command *c, const char *words[], unsigned num_words);
|
2008-07-18 06:38:23 -05:00
|
|
|
|
2009-11-09 08:12:02 -06:00
|
|
|
static void tcl_output(void *privData, const char *file, unsigned line,
|
|
|
|
const char *function, const char *string)
|
2008-10-09 03:08:29 -05:00
|
|
|
{
|
2009-06-23 17:42:54 -05:00
|
|
|
Jim_Obj *tclOutput = (Jim_Obj *)privData;
|
2008-07-17 03:34:14 -05:00
|
|
|
Jim_AppendString(interp, tclOutput, string, strlen(string));
|
|
|
|
}
|
|
|
|
|
2009-11-13 15:25:47 -06:00
|
|
|
extern struct command_context *global_cmd_ctx;
|
2008-07-24 05:16:44 -05:00
|
|
|
|
2009-11-09 08:07:21 -06:00
|
|
|
void script_debug(Jim_Interp *interp, const char *name,
|
|
|
|
unsigned argc, Jim_Obj *const *argv)
|
2009-06-12 17:06:02 -05:00
|
|
|
{
|
|
|
|
LOG_DEBUG("command - %s", name);
|
2009-11-09 08:07:21 -06:00
|
|
|
for (unsigned i = 0; i < argc; i++)
|
|
|
|
{
|
2009-06-12 17:06:02 -05:00
|
|
|
int len;
|
|
|
|
const char *w = Jim_GetString(argv[i], &len);
|
|
|
|
|
|
|
|
/* end of line comment? */
|
|
|
|
if (*w == '#')
|
|
|
|
break;
|
|
|
|
|
|
|
|
LOG_DEBUG("%s - argv[%d]=%s", name, i, w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-18 18:34:34 -06:00
|
|
|
static void script_command_args_free(const char **words, unsigned nwords)
|
|
|
|
{
|
|
|
|
for (unsigned i = 0; i < nwords; i++)
|
|
|
|
free((void *)words[i]);
|
|
|
|
free(words);
|
|
|
|
}
|
|
|
|
static const char **script_command_args_alloc(
|
|
|
|
unsigned argc, Jim_Obj *const *argv, unsigned *nwords)
|
|
|
|
{
|
|
|
|
const char **words = malloc(argc * sizeof(char *));
|
|
|
|
if (NULL == words)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
unsigned i;
|
|
|
|
for (i = 0; i < argc; i++)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
const char *w = Jim_GetString(argv[i], &len);
|
|
|
|
/* a comment may end the line early */
|
|
|
|
if (*w == '#')
|
|
|
|
break;
|
|
|
|
|
|
|
|
words[i] = strdup(w);
|
|
|
|
if (words[i] == NULL)
|
|
|
|
{
|
|
|
|
script_command_args_free(words, i);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
*nwords = i;
|
|
|
|
return words;
|
|
|
|
}
|
|
|
|
|
2008-07-17 03:34:14 -05:00
|
|
|
static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
|
|
|
{
|
|
|
|
/* the private data is stashed in the interp structure */
|
2009-11-13 15:30:50 -06:00
|
|
|
struct command *c;
|
2009-11-13 15:25:47 -06:00
|
|
|
struct command_context *context;
|
2008-07-24 05:16:44 -05:00
|
|
|
int retval;
|
2008-07-17 03:34:14 -05:00
|
|
|
|
2008-11-11 08:47:01 -06:00
|
|
|
/* DANGER!!!! be careful what we invoke here, since interp->cmdPrivData might
|
|
|
|
* get overwritten by running other Jim commands! Treat it as an
|
|
|
|
* emphemeral global variable that is used in lieu of an argument
|
|
|
|
* to the fn and fish it out manually.
|
|
|
|
*/
|
|
|
|
c = interp->cmdPrivData;
|
2009-06-23 17:42:03 -05:00
|
|
|
if (c == NULL)
|
2008-11-11 08:47:01 -06:00
|
|
|
{
|
2009-06-23 17:42:03 -05:00
|
|
|
LOG_ERROR("BUG: interp->cmdPrivData == NULL");
|
2008-11-11 08:47:01 -06:00
|
|
|
return JIM_ERR;
|
|
|
|
}
|
2008-07-17 03:34:14 -05:00
|
|
|
target_call_timer_callbacks_now();
|
2008-10-09 03:08:29 -05:00
|
|
|
LOG_USER_N("%s", ""); /* Keep GDB connection alive*/
|
|
|
|
|
2009-06-12 17:06:02 -05:00
|
|
|
script_debug(interp, c->name, argc, argv);
|
2008-07-17 03:34:14 -05:00
|
|
|
|
2009-11-18 18:34:34 -06:00
|
|
|
unsigned nwords;
|
|
|
|
const char **words = script_command_args_alloc(argc, argv, &nwords);
|
|
|
|
if (NULL == words)
|
|
|
|
return JIM_ERR;
|
2008-07-17 03:34:14 -05:00
|
|
|
|
|
|
|
/* grab the command context from the associated data */
|
|
|
|
context = Jim_GetAssocData(interp, "context");
|
2008-07-24 05:16:44 -05:00
|
|
|
if (context == NULL)
|
2008-07-17 03:34:14 -05:00
|
|
|
{
|
2008-07-24 05:16:44 -05:00
|
|
|
/* Tcl can invoke commands directly instead of via command_run_line(). This would
|
|
|
|
* happen when the Jim Tcl interpreter is provided by eCos.
|
|
|
|
*/
|
|
|
|
context = global_cmd_ctx;
|
2008-07-17 03:34:14 -05:00
|
|
|
}
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2008-07-24 05:16:44 -05:00
|
|
|
/* capture log output and return it */
|
|
|
|
Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
|
|
|
|
/* a garbage collect can happen, so we need a reference count to this object */
|
|
|
|
Jim_IncrRefCount(tclOutput);
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2008-07-24 05:16:44 -05:00
|
|
|
log_add_callback(tcl_output, tclOutput);
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2009-11-16 07:46:15 -06:00
|
|
|
retval = run_command(context, c, (const char **)words, nwords);
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2008-07-24 05:16:44 -05:00
|
|
|
log_remove_callback(tcl_output, tclOutput);
|
|
|
|
|
|
|
|
/* We dump output into this local variable */
|
2008-07-24 15:46:15 -05:00
|
|
|
Jim_SetResult(interp, tclOutput);
|
2008-07-24 05:16:44 -05:00
|
|
|
Jim_DecrRefCount(interp, tclOutput);
|
2008-07-05 04:57:19 -05:00
|
|
|
|
2009-11-18 18:34:34 -06:00
|
|
|
script_command_args_free(words, nwords);
|
2008-02-25 11:48:04 -06:00
|
|
|
|
2008-07-24 05:16:44 -05:00
|
|
|
int *return_retval = Jim_GetAssocData(interp, "retval");
|
|
|
|
if (return_retval != NULL)
|
|
|
|
{
|
|
|
|
*return_retval = retval;
|
|
|
|
}
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2009-06-23 17:42:03 -05:00
|
|
|
return (retval == ERROR_OK)?JIM_OK:JIM_ERR;
|
2008-07-17 03:34:14 -05:00
|
|
|
}
|
2008-02-25 11:48:04 -06:00
|
|
|
|
2008-11-26 07:42:02 -06:00
|
|
|
/* nice short description of source file */
|
|
|
|
#define __THIS__FILE__ "command.c"
|
|
|
|
|
2009-11-11 12:20:55 -06:00
|
|
|
/**
|
|
|
|
* Find a command by name from a list of commands.
|
|
|
|
* @returns The named command if found, or NULL.
|
|
|
|
*/
|
2009-11-19 09:26:28 -06:00
|
|
|
static struct command *command_find(struct command *head, const char *name)
|
2008-02-25 11:48:04 -06:00
|
|
|
{
|
2009-11-19 09:26:28 -06:00
|
|
|
for (struct command *cc = head; cc; cc = cc->next)
|
2009-11-11 12:20:55 -06:00
|
|
|
{
|
|
|
|
if (strcmp(cc->name, name) == 0)
|
|
|
|
return cc;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2009-11-11 12:20:55 -06:00
|
|
|
/**
|
|
|
|
* Add the command to the end of linked list.
|
|
|
|
* @returns Returns false if the named command already exists in the list.
|
|
|
|
* Returns true otherwise.
|
|
|
|
*/
|
2009-11-13 15:30:50 -06:00
|
|
|
static void command_add_child(struct command **head, struct command *c)
|
2009-11-11 12:20:55 -06:00
|
|
|
{
|
|
|
|
assert(head);
|
|
|
|
if (NULL == *head)
|
|
|
|
{
|
|
|
|
*head = c;
|
|
|
|
return;
|
|
|
|
}
|
2009-11-13 15:30:50 -06:00
|
|
|
struct command *cc = *head;
|
2009-11-11 12:20:55 -06:00
|
|
|
while (cc->next) cc = cc->next;
|
|
|
|
cc->next = c;
|
|
|
|
}
|
|
|
|
|
2009-11-19 10:38:17 -06:00
|
|
|
static struct command **command_list_for_parent(
|
|
|
|
struct command_context *cmd_ctx, struct command *parent)
|
2009-11-11 12:20:55 -06:00
|
|
|
{
|
2009-11-19 10:38:17 -06:00
|
|
|
return parent ? &parent->children : &cmd_ctx->commands;
|
|
|
|
}
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2009-11-19 10:38:17 -06:00
|
|
|
static struct command *command_new(struct command_context *cmd_ctx,
|
|
|
|
struct command *parent, const char *name,
|
|
|
|
command_handler_t handler, enum command_mode mode,
|
|
|
|
const char *help)
|
|
|
|
{
|
|
|
|
assert(name);
|
2009-11-11 12:20:55 -06:00
|
|
|
|
2009-11-19 10:38:17 -06:00
|
|
|
struct command *c = malloc(sizeof(struct command));
|
|
|
|
memset(c, 0, sizeof(struct command));
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2008-02-25 11:48:04 -06:00
|
|
|
c->name = strdup(name);
|
2009-11-19 08:48:37 -06:00
|
|
|
if (help)
|
|
|
|
c->help = strdup(help);
|
2008-02-25 11:48:04 -06:00
|
|
|
c->parent = parent;
|
|
|
|
c->handler = handler;
|
|
|
|
c->mode = mode;
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2009-11-19 10:38:17 -06:00
|
|
|
command_add_child(command_list_for_parent(cmd_ctx, parent), c);
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2009-11-19 10:38:17 -06:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
static void command_free(struct command *c)
|
|
|
|
{
|
|
|
|
/// @todo if command has a handler, unregister its jim command!
|
|
|
|
|
|
|
|
while (NULL != c->children)
|
|
|
|
{
|
|
|
|
struct command *tmp = c->children;
|
|
|
|
c->children = tmp->next;
|
|
|
|
command_free(tmp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c->name)
|
|
|
|
free(c->name);
|
2009-11-19 08:48:37 -06:00
|
|
|
if (c->help)
|
|
|
|
free((void*)c->help);
|
2009-11-19 10:38:17 -06:00
|
|
|
free(c);
|
|
|
|
}
|
|
|
|
|
|
|
|
struct command* register_command(struct command_context *context,
|
|
|
|
struct command *parent, const char *name,
|
|
|
|
command_handler_t handler, enum command_mode mode,
|
|
|
|
const char *help)
|
|
|
|
{
|
|
|
|
if (!context || !name)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
struct command **head = command_list_for_parent(context, parent);
|
|
|
|
struct command *c = command_find(*head, name);
|
|
|
|
if (NULL != c)
|
|
|
|
{
|
|
|
|
LOG_ERROR("command '%s' is already registered in '%s' context",
|
|
|
|
name, parent ? parent->name : "<global>");
|
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
|
|
|
c = command_new(context, parent, name, handler, mode, help);
|
|
|
|
/* if allocation failed or it is a placeholder (no handler), we're done */
|
|
|
|
if (NULL == c || NULL == c->handler)
|
2008-07-17 03:34:14 -05:00
|
|
|
return c;
|
|
|
|
|
2009-11-11 07:26:17 -06:00
|
|
|
const char *full_name = command_name(c, '_');
|
|
|
|
|
|
|
|
const char *ocd_name = alloc_printf("ocd_%s", full_name);
|
|
|
|
Jim_CreateCommand(interp, ocd_name, script_command, c, NULL);
|
|
|
|
free((void *)ocd_name);
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2008-07-18 11:25:15 -05:00
|
|
|
/* we now need to add an overrideable proc */
|
2009-11-11 07:26:17 -06:00
|
|
|
const char *override_name = alloc_printf("proc %s {args} {"
|
|
|
|
"if {[catch {eval ocd_%s $args}] == 0} "
|
|
|
|
"{return \"\"} else {return -code error}}",
|
|
|
|
full_name, full_name);
|
2009-06-23 17:47:42 -05:00
|
|
|
Jim_Eval_Named(interp, override_name, __THIS__FILE__, __LINE__);
|
2008-07-18 11:25:15 -05:00
|
|
|
free((void *)override_name);
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2009-11-11 07:26:17 -06:00
|
|
|
free((void *)full_name);
|
|
|
|
|
2008-02-25 11:48:04 -06:00
|
|
|
return c;
|
|
|
|
}
|
|
|
|
|
2009-11-19 10:38:17 -06:00
|
|
|
int unregister_all_commands(struct command_context *context,
|
|
|
|
struct command *parent)
|
2008-05-07 07:15:19 -05:00
|
|
|
{
|
|
|
|
if (context == NULL)
|
|
|
|
return ERROR_OK;
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2009-11-19 10:38:17 -06:00
|
|
|
struct command **head = command_list_for_parent(context, parent);
|
|
|
|
while (NULL != *head)
|
2008-05-07 07:15:19 -05:00
|
|
|
{
|
2009-11-19 10:38:17 -06:00
|
|
|
struct command *tmp = *head;
|
|
|
|
*head = tmp->next;
|
|
|
|
command_free(tmp);
|
2008-05-07 07:15:19 -05:00
|
|
|
}
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2008-05-07 07:15:19 -05:00
|
|
|
return ERROR_OK;
|
|
|
|
}
|
|
|
|
|
2009-11-19 10:38:17 -06:00
|
|
|
int unregister_command(struct command_context *context,
|
|
|
|
struct command *parent, const char *name)
|
2008-02-25 11:48:04 -06:00
|
|
|
{
|
|
|
|
if ((!context) || (!name))
|
|
|
|
return ERROR_INVALID_ARGUMENTS;
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2009-11-19 10:38:17 -06:00
|
|
|
struct command *p = NULL;
|
|
|
|
struct command **head = command_list_for_parent(context, parent);
|
|
|
|
for (struct command *c = *head; NULL != c; p = c, c = c->next)
|
2008-12-16 04:22:24 -06:00
|
|
|
{
|
2009-11-19 10:38:17 -06:00
|
|
|
if (strcmp(name, c->name) != 0)
|
|
|
|
continue;
|
2008-12-16 04:22:24 -06:00
|
|
|
|
2009-11-19 10:38:17 -06:00
|
|
|
if (p)
|
|
|
|
p->next = c->next;
|
|
|
|
else
|
|
|
|
*head = c->next;
|
|
|
|
|
|
|
|
command_free(c);
|
|
|
|
return ERROR_OK;
|
2008-02-25 11:48:04 -06:00
|
|
|
}
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2008-02-25 11:48:04 -06:00
|
|
|
return ERROR_OK;
|
|
|
|
}
|
|
|
|
|
2009-11-13 15:25:47 -06:00
|
|
|
void command_output_text(struct command_context *context, const char *data)
|
2008-07-06 14:56:15 -05:00
|
|
|
{
|
2009-06-23 17:49:06 -05:00
|
|
|
if (context && context->output_handler && data) {
|
2009-06-23 17:47:42 -05:00
|
|
|
context->output_handler(context, data);
|
2008-07-06 14:56:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-11-13 15:25:47 -06:00
|
|
|
void command_print_sameline(struct command_context *context, const char *format, ...)
|
2008-02-25 11:48:04 -06:00
|
|
|
{
|
2008-02-29 05:16:38 -06:00
|
|
|
char *string;
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2008-02-29 05:16:38 -06:00
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
|
2008-03-07 04:55:59 -06:00
|
|
|
string = alloc_vprintf(format, ap);
|
2008-02-29 05:16:38 -06:00
|
|
|
if (string != NULL)
|
2008-02-25 11:48:04 -06:00
|
|
|
{
|
2008-07-17 07:02:55 -05:00
|
|
|
/* we want this collected in the log + we also want to pick it up as a tcl return
|
|
|
|
* value.
|
2008-10-09 03:08:29 -05:00
|
|
|
*
|
2008-07-17 07:02:55 -05:00
|
|
|
* The latter bit isn't precisely neat, but will do for now.
|
|
|
|
*/
|
|
|
|
LOG_USER_N("%s", string);
|
2008-12-13 06:44:39 -06:00
|
|
|
/* We already printed it above */
|
|
|
|
/* command_output_text(context, string); */
|
2008-02-29 05:16:38 -06:00
|
|
|
free(string);
|
2008-02-25 11:48:04 -06:00
|
|
|
}
|
|
|
|
|
2008-02-28 02:11:18 -06:00
|
|
|
va_end(ap);
|
|
|
|
}
|
2008-02-25 11:48:04 -06:00
|
|
|
|
2009-11-13 15:25:47 -06:00
|
|
|
void command_print(struct command_context *context, const char *format, ...)
|
2008-02-28 02:11:18 -06:00
|
|
|
{
|
2008-02-29 05:16:38 -06:00
|
|
|
char *string;
|
|
|
|
|
2008-02-28 02:11:18 -06:00
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
2008-02-29 05:16:38 -06:00
|
|
|
|
2008-03-07 04:55:59 -06:00
|
|
|
string = alloc_vprintf(format, ap);
|
2008-02-29 05:16:38 -06:00
|
|
|
if (string != NULL)
|
|
|
|
{
|
2008-03-07 04:55:59 -06:00
|
|
|
strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
|
2008-07-17 07:02:55 -05:00
|
|
|
/* we want this collected in the log + we also want to pick it up as a tcl return
|
|
|
|
* value.
|
2008-10-09 03:08:29 -05:00
|
|
|
*
|
2008-07-17 07:02:55 -05:00
|
|
|
* The latter bit isn't precisely neat, but will do for now.
|
|
|
|
*/
|
|
|
|
LOG_USER_N("%s", string);
|
2008-12-13 06:44:39 -06:00
|
|
|
/* We already printed it above */
|
|
|
|
/* command_output_text(context, string); */
|
2008-02-29 05:16:38 -06:00
|
|
|
free(string);
|
|
|
|
}
|
|
|
|
|
2008-02-28 02:11:18 -06:00
|
|
|
va_end(ap);
|
2008-02-25 11:48:04 -06:00
|
|
|
}
|
|
|
|
|
2009-11-13 15:30:50 -06:00
|
|
|
static char *__command_name(struct command *c, char delim, unsigned extra)
|
2009-11-11 07:26:17 -06:00
|
|
|
{
|
|
|
|
char *name;
|
|
|
|
unsigned len = strlen(c->name);
|
|
|
|
if (NULL == c->parent) {
|
|
|
|
// allocate enough for the name, child names, and '\0'
|
|
|
|
name = malloc(len + extra + 1);
|
|
|
|
strcpy(name, c->name);
|
|
|
|
} else {
|
|
|
|
// parent's extra must include both the space and name
|
|
|
|
name = __command_name(c->parent, delim, 1 + len + extra);
|
|
|
|
char dstr[2] = { delim, 0 };
|
|
|
|
strcat(name, dstr);
|
|
|
|
strcat(name, c->name);
|
|
|
|
}
|
|
|
|
return name;
|
|
|
|
}
|
2009-11-13 15:30:50 -06:00
|
|
|
char *command_name(struct command *c, char delim)
|
2009-11-11 07:26:17 -06:00
|
|
|
{
|
|
|
|
return __command_name(c, delim, 0);
|
|
|
|
}
|
|
|
|
|
2009-11-13 15:25:47 -06:00
|
|
|
static int run_command(struct command_context *context,
|
2009-11-13 15:30:50 -06:00
|
|
|
struct command *c, const char *words[], unsigned num_words)
|
2008-06-27 01:58:45 -05:00
|
|
|
{
|
2009-06-23 17:47:42 -05:00
|
|
|
if (!((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode)))
|
2008-06-27 01:58:45 -05:00
|
|
|
{
|
2008-07-17 03:34:14 -05:00
|
|
|
/* Config commands can not run after the config stage */
|
2009-05-24 15:57:53 -05:00
|
|
|
LOG_ERROR("Command '%s' only runs during configuration stage", c->name);
|
2008-07-17 03:34:14 -05:00
|
|
|
return ERROR_FAIL;
|
2008-06-27 01:58:45 -05:00
|
|
|
}
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2009-11-15 06:21:17 -06:00
|
|
|
struct command_invocation cmd = {
|
|
|
|
.ctx = context,
|
2009-11-16 07:46:15 -06:00
|
|
|
.name = c->name,
|
2009-11-16 17:45:17 -06:00
|
|
|
.argc = num_words - 1,
|
|
|
|
.argv = words + 1,
|
2009-11-15 06:21:17 -06:00
|
|
|
};
|
|
|
|
int retval = c->handler(&cmd);
|
2008-06-27 01:58:45 -05:00
|
|
|
if (retval == ERROR_COMMAND_SYNTAX_ERROR)
|
|
|
|
{
|
2008-07-16 03:49:05 -05:00
|
|
|
/* Print help for command */
|
2009-11-11 07:26:17 -06:00
|
|
|
char *full_name = command_name(c, ' ');
|
|
|
|
if (NULL != full_name) {
|
|
|
|
command_run_linef(context, "help %s", full_name);
|
|
|
|
free(full_name);
|
|
|
|
} else
|
|
|
|
retval = -ENOMEM;
|
2008-06-27 01:58:45 -05:00
|
|
|
}
|
|
|
|
else if (retval == ERROR_COMMAND_CLOSE_CONNECTION)
|
|
|
|
{
|
|
|
|
/* just fall through for a shutdown request */
|
|
|
|
}
|
|
|
|
else if (retval != ERROR_OK)
|
|
|
|
{
|
|
|
|
/* we do not print out an error message because the command *should*
|
|
|
|
* have printed out an error
|
|
|
|
*/
|
2008-10-09 03:08:29 -05:00
|
|
|
LOG_DEBUG("Command failed with error code %d", retval);
|
2008-06-27 01:58:45 -05:00
|
|
|
}
|
2008-10-09 03:08:29 -05:00
|
|
|
|
|
|
|
return retval;
|
2008-02-25 11:48:04 -06:00
|
|
|
}
|
|
|
|
|
2009-11-13 15:25:47 -06:00
|
|
|
int command_run_line(struct command_context *context, char *line)
|
2008-02-25 11:48:04 -06:00
|
|
|
{
|
2008-07-17 03:34:14 -05:00
|
|
|
/* all the parent commands have been registered with the interpreter
|
|
|
|
* so, can just evaluate the line as a script and check for
|
|
|
|
* results
|
|
|
|
*/
|
|
|
|
/* run the line thru a script engine */
|
2009-06-23 17:42:54 -05:00
|
|
|
int retval = ERROR_FAIL;
|
2008-07-17 03:34:14 -05:00
|
|
|
int retcode;
|
2008-07-24 05:16:44 -05:00
|
|
|
/* Beware! This code needs to be reentrant. It is also possible
|
|
|
|
* for OpenOCD commands to be invoked directly from Tcl. This would
|
|
|
|
* happen when the Jim Tcl interpreter is provided by eCos for
|
|
|
|
* instance.
|
|
|
|
*/
|
|
|
|
Jim_DeleteAssocData(interp, "context");
|
2008-07-17 03:34:14 -05:00
|
|
|
retcode = Jim_SetAssocData(interp, "context", NULL, context);
|
2008-07-24 05:16:44 -05:00
|
|
|
if (retcode == JIM_OK)
|
|
|
|
{
|
|
|
|
/* associated the return value */
|
|
|
|
Jim_DeleteAssocData(interp, "retval");
|
|
|
|
retcode = Jim_SetAssocData(interp, "retval", NULL, &retval);
|
|
|
|
if (retcode == JIM_OK)
|
|
|
|
{
|
2009-06-23 17:47:42 -05:00
|
|
|
retcode = Jim_Eval_Named(interp, line, __THIS__FILE__, __LINE__);
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2008-07-24 05:16:44 -05:00
|
|
|
Jim_DeleteAssocData(interp, "retval");
|
2008-10-09 03:08:29 -05:00
|
|
|
}
|
2008-07-24 05:16:44 -05:00
|
|
|
Jim_DeleteAssocData(interp, "context");
|
|
|
|
}
|
2008-07-17 03:34:14 -05:00
|
|
|
if (retcode == JIM_ERR) {
|
2009-06-23 17:38:12 -05:00
|
|
|
if (retval != ERROR_COMMAND_CLOSE_CONNECTION)
|
2008-07-17 07:02:55 -05:00
|
|
|
{
|
|
|
|
/* We do not print the connection closed error message */
|
|
|
|
Jim_PrintErrorMessage(interp);
|
|
|
|
}
|
2009-06-23 17:42:03 -05:00
|
|
|
if (retval == ERROR_OK)
|
2008-07-17 07:02:55 -05:00
|
|
|
{
|
|
|
|
/* It wasn't a low level OpenOCD command that failed */
|
2008-10-09 03:08:29 -05:00
|
|
|
return ERROR_FAIL;
|
2008-07-17 07:02:55 -05:00
|
|
|
}
|
|
|
|
return retval;
|
2008-07-17 03:34:14 -05:00
|
|
|
} else if (retcode == JIM_EXIT) {
|
|
|
|
/* ignore. */
|
|
|
|
/* exit(Jim_GetExitCode(interp)); */
|
|
|
|
} else {
|
|
|
|
const char *result;
|
|
|
|
int reslen;
|
|
|
|
|
|
|
|
result = Jim_GetString(Jim_GetResult(interp), &reslen);
|
2009-06-23 17:45:47 -05:00
|
|
|
if (reslen > 0)
|
2008-12-16 04:22:24 -06:00
|
|
|
{
|
2009-05-05 07:06:26 -05:00
|
|
|
int i;
|
2009-06-23 17:44:17 -05:00
|
|
|
char buff[256 + 1];
|
2009-05-05 07:06:26 -05:00
|
|
|
for (i = 0; i < reslen; i += 256)
|
|
|
|
{
|
|
|
|
int chunk;
|
|
|
|
chunk = reslen - i;
|
|
|
|
if (chunk > 256)
|
|
|
|
chunk = 256;
|
2009-06-23 17:44:17 -05:00
|
|
|
strncpy(buff, result + i, chunk);
|
2009-05-05 07:06:26 -05:00
|
|
|
buff[chunk] = 0;
|
|
|
|
LOG_USER_N("%s", buff);
|
|
|
|
}
|
|
|
|
LOG_USER_N("%s", "\n");
|
2008-07-17 03:34:14 -05:00
|
|
|
}
|
2009-06-23 17:42:54 -05:00
|
|
|
retval = ERROR_OK;
|
2008-07-06 18:49:05 -05:00
|
|
|
}
|
2008-02-25 11:48:04 -06:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2009-11-13 15:25:47 -06:00
|
|
|
int command_run_linef(struct command_context *context, const char *format, ...)
|
2008-07-04 02:21:31 -05:00
|
|
|
{
|
2009-06-23 17:42:54 -05:00
|
|
|
int retval = ERROR_FAIL;
|
2008-07-04 02:21:31 -05:00
|
|
|
char *string;
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, format);
|
|
|
|
string = alloc_vprintf(format, ap);
|
2009-06-23 17:38:12 -05:00
|
|
|
if (string != NULL)
|
2008-07-04 02:21:31 -05:00
|
|
|
{
|
2009-06-23 17:42:54 -05:00
|
|
|
retval = command_run_line(context, string);
|
2008-07-04 02:21:31 -05:00
|
|
|
}
|
|
|
|
va_end(ap);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2009-11-13 15:25:47 -06:00
|
|
|
void command_set_output_handler(struct command_context* context,
|
2009-11-10 20:42:45 -06:00
|
|
|
command_output_handler_t output_handler, void *priv)
|
2008-02-25 11:48:04 -06:00
|
|
|
{
|
|
|
|
context->output_handler = output_handler;
|
|
|
|
context->output_handler_priv = priv;
|
|
|
|
}
|
|
|
|
|
2009-11-13 15:25:47 -06:00
|
|
|
struct command_context* copy_command_context(struct command_context* context)
|
2008-02-25 11:48:04 -06:00
|
|
|
{
|
2009-11-13 15:25:47 -06:00
|
|
|
struct command_context* copy_context = malloc(sizeof(struct command_context));
|
2008-02-25 11:48:04 -06:00
|
|
|
|
|
|
|
*copy_context = *context;
|
2008-07-17 03:34:14 -05:00
|
|
|
|
2008-02-25 11:48:04 -06:00
|
|
|
return copy_context;
|
|
|
|
}
|
|
|
|
|
2009-11-13 15:25:47 -06:00
|
|
|
int command_done(struct command_context *context)
|
2008-02-25 11:48:04 -06:00
|
|
|
{
|
|
|
|
free(context);
|
|
|
|
context = NULL;
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2008-02-25 11:48:04 -06:00
|
|
|
return ERROR_OK;
|
|
|
|
}
|
|
|
|
|
2008-07-18 01:24:57 -05:00
|
|
|
/* 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);
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2008-07-18 01:24:57 -05:00
|
|
|
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 */
|
2008-07-24 09:50:41 -05:00
|
|
|
LOG_USER_N("%s", ptr);
|
2008-07-18 01:24:57 -05:00
|
|
|
return strlen(ptr);
|
|
|
|
}
|
|
|
|
/* GRR we must chunk - not null terminated */
|
|
|
|
while (nbytes) {
|
2009-06-23 17:44:17 -05:00
|
|
|
char chunk[128 + 1];
|
2008-07-18 01:24:57 -05:00
|
|
|
int x;
|
|
|
|
|
|
|
|
x = nbytes;
|
|
|
|
if (x > 128) {
|
|
|
|
x = 128;
|
|
|
|
}
|
|
|
|
/* copy it */
|
|
|
|
memcpy(chunk, ptr, x);
|
|
|
|
/* terminate it */
|
|
|
|
chunk[n] = 0;
|
|
|
|
/* output it */
|
2008-07-24 09:50:41 -05:00
|
|
|
LOG_USER_N("%s", chunk);
|
2008-07-18 01:24:57 -05:00
|
|
|
ptr += x;
|
|
|
|
nbytes -= x;
|
|
|
|
}
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2008-07-18 01:24:57 -05:00
|
|
|
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)
|
|
|
|
{
|
2008-07-24 09:50:41 -05:00
|
|
|
LOG_USER_N("%s", cp);
|
2008-07-18 01:24:57 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2008-10-16 07:21:02 -05:00
|
|
|
static int jim_capture(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
|
|
|
|
{
|
|
|
|
if (argc != 2)
|
|
|
|
return JIM_ERR;
|
|
|
|
int retcode;
|
|
|
|
const char *str = Jim_GetString(argv[1], NULL);
|
|
|
|
|
|
|
|
/* capture log output and return it */
|
|
|
|
Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
|
|
|
|
/* a garbage collect can happen, so we need a reference count to this object */
|
|
|
|
Jim_IncrRefCount(tclOutput);
|
|
|
|
|
|
|
|
log_add_callback(tcl_output, tclOutput);
|
|
|
|
|
2009-06-23 17:47:42 -05:00
|
|
|
retcode = Jim_Eval_Named(interp, str, __THIS__FILE__, __LINE__);
|
2008-10-16 07:21:02 -05:00
|
|
|
|
|
|
|
log_remove_callback(tcl_output, tclOutput);
|
|
|
|
|
|
|
|
/* We dump output into this local variable */
|
|
|
|
Jim_SetResult(interp, tclOutput);
|
|
|
|
Jim_DecrRefCount(interp, tclOutput);
|
|
|
|
|
|
|
|
return retcode;
|
|
|
|
}
|
|
|
|
|
2009-11-19 08:48:37 -06:00
|
|
|
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)
|
|
|
|
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);
|
|
|
|
|
|
|
|
static COMMAND_HELPER(command_help_show_list, struct command *head, unsigned n)
|
|
|
|
{
|
|
|
|
for (struct command *c = head; NULL != c; c = c->next)
|
|
|
|
CALL_COMMAND_HANDLER(command_help_show, c, n);
|
|
|
|
return ERROR_OK;
|
|
|
|
}
|
|
|
|
static COMMAND_HELPER(command_help_show, struct command *c, unsigned n)
|
|
|
|
{
|
|
|
|
command_run_linef(CMD_CTX, "cmd_help {%s} {%s} %d", command_name(c, ' '),
|
|
|
|
c->help ? : "no help available", n);
|
|
|
|
|
|
|
|
if (++n >= 2)
|
|
|
|
return ERROR_OK;
|
|
|
|
|
|
|
|
return CALL_COMMAND_HANDLER(command_help_show_list, c->children, n);
|
|
|
|
}
|
|
|
|
COMMAND_HANDLER(handle_help_command)
|
|
|
|
{
|
|
|
|
struct command *c = CMD_CTX->commands;
|
|
|
|
|
|
|
|
if (0 == CMD_ARGC)
|
|
|
|
return CALL_COMMAND_HANDLER(command_help_show_list, c, 0);
|
|
|
|
|
|
|
|
int retval = CALL_COMMAND_HANDLER(command_help_find, c, &c);
|
|
|
|
if (ERROR_OK != retval)
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
return CALL_COMMAND_HANDLER(command_help_show, c, 0);
|
|
|
|
}
|
|
|
|
|
2009-11-19 09:23:25 -06:00
|
|
|
|
|
|
|
int help_add_command(struct command_context *cmd_ctx, struct command *parent,
|
|
|
|
const char *cmd_name, const char *help_text)
|
|
|
|
{
|
|
|
|
struct command **head = command_list_for_parent(cmd_ctx, parent);
|
|
|
|
struct command *nc = command_find(*head, cmd_name);
|
|
|
|
if (NULL == nc)
|
|
|
|
{
|
|
|
|
// add a new command with help text
|
|
|
|
nc = register_command(cmd_ctx, parent, cmd_name,
|
|
|
|
NULL, COMMAND_ANY, help_text);
|
|
|
|
if (NULL == nc)
|
|
|
|
{
|
|
|
|
LOG_ERROR("failed to add '%s' help text", cmd_name);
|
|
|
|
return ERROR_FAIL;
|
|
|
|
}
|
|
|
|
LOG_DEBUG("added '%s' help text", cmd_name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
bool replaced = false;
|
|
|
|
if (nc->help)
|
|
|
|
{
|
|
|
|
free((void *)nc->help);
|
|
|
|
replaced = true;
|
|
|
|
}
|
|
|
|
nc->help = strdup(help_text);
|
|
|
|
|
|
|
|
if (replaced)
|
|
|
|
LOG_INFO("replaced existing '%s' help", cmd_name);
|
|
|
|
else
|
|
|
|
LOG_DEBUG("added '%s' help text", cmd_name);
|
|
|
|
}
|
|
|
|
return ERROR_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
COMMAND_HANDLER(handle_help_add_command)
|
|
|
|
{
|
|
|
|
if (CMD_ARGC < 2)
|
|
|
|
{
|
|
|
|
LOG_ERROR("%s: insufficient arguments", CMD_NAME);
|
|
|
|
return ERROR_INVALID_ARGUMENTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// save help text and remove it from argument list
|
|
|
|
const char *help_text = CMD_ARGV[--CMD_ARGC];
|
|
|
|
// likewise for the leaf command name
|
|
|
|
const char *cmd_name = CMD_ARGV[--CMD_ARGC];
|
|
|
|
|
|
|
|
struct command *c = NULL;
|
|
|
|
if (CMD_ARGC > 0)
|
|
|
|
{
|
|
|
|
c = CMD_CTX->commands;
|
|
|
|
int retval = CALL_COMMAND_HANDLER(command_help_find, c, &c);
|
|
|
|
if (ERROR_OK != retval)
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
return help_add_command(CMD_CTX, c, cmd_name, help_text);
|
|
|
|
}
|
|
|
|
|
2009-11-09 23:23:38 -06:00
|
|
|
/* sleep command sleeps for <n> miliseconds
|
|
|
|
* this is useful in target startup scripts
|
|
|
|
*/
|
2009-11-10 01:56:52 -06:00
|
|
|
COMMAND_HANDLER(handle_sleep_command)
|
2009-11-09 23:23:38 -06:00
|
|
|
{
|
|
|
|
bool busy = false;
|
2009-11-15 06:57:12 -06:00
|
|
|
if (CMD_ARGC == 2)
|
2009-11-09 23:23:38 -06:00
|
|
|
{
|
2009-11-15 10:15:59 -06:00
|
|
|
if (strcmp(CMD_ARGV[1], "busy") == 0)
|
2009-11-09 23:23:38 -06:00
|
|
|
busy = true;
|
|
|
|
else
|
|
|
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
|
|
|
}
|
2009-11-15 06:57:12 -06:00
|
|
|
else if (CMD_ARGC < 1 || CMD_ARGC > 2)
|
2009-11-09 23:23:38 -06:00
|
|
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
|
|
|
|
|
|
|
unsigned long duration = 0;
|
2009-11-15 10:15:59 -06:00
|
|
|
int retval = parse_ulong(CMD_ARGV[0], &duration);
|
2009-11-09 23:23:38 -06:00
|
|
|
if (ERROR_OK != retval)
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
if (!busy)
|
|
|
|
{
|
|
|
|
long long then = timeval_ms();
|
|
|
|
while (timeval_ms() - then < (long long)duration)
|
|
|
|
{
|
|
|
|
target_call_timer_callbacks_now();
|
|
|
|
usleep(1000);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
busy_sleep(duration);
|
|
|
|
|
|
|
|
return ERROR_OK;
|
|
|
|
}
|
|
|
|
|
2009-11-17 11:15:09 -06:00
|
|
|
struct command_context* command_init(const char *startup_tcl)
|
2008-02-25 11:48:04 -06:00
|
|
|
{
|
2009-11-13 15:25:47 -06:00
|
|
|
struct command_context* context = malloc(sizeof(struct command_context));
|
2009-03-07 09:19:21 -06:00
|
|
|
const char *HostOs;
|
2008-07-18 01:24:57 -05:00
|
|
|
|
2008-02-25 11:48:04 -06:00
|
|
|
context->mode = COMMAND_EXEC;
|
|
|
|
context->commands = NULL;
|
|
|
|
context->current_target = 0;
|
|
|
|
context->output_handler = NULL;
|
|
|
|
context->output_handler_priv = NULL;
|
2008-07-18 01:24:57 -05:00
|
|
|
|
2009-05-15 15:35:53 -05:00
|
|
|
#if !BUILD_ECOSBOARD
|
2008-07-18 01:24:57 -05:00
|
|
|
Jim_InitEmbedded();
|
|
|
|
/* Create an interpreter */
|
|
|
|
interp = Jim_CreateInterp();
|
|
|
|
/* Add all the Jim core commands */
|
|
|
|
Jim_RegisterCoreCommands(interp);
|
|
|
|
#endif
|
|
|
|
|
2009-06-23 17:47:42 -05:00
|
|
|
#if defined(_MSC_VER)
|
2009-03-07 09:19:21 -06:00
|
|
|
/* WinXX - is generic, the forward
|
|
|
|
* looking problem is this:
|
2009-05-05 07:06:26 -05:00
|
|
|
*
|
2009-03-07 09:19:21 -06:00
|
|
|
* "win32" or "win64"
|
|
|
|
*
|
|
|
|
* "winxx" is generic.
|
|
|
|
*/
|
|
|
|
HostOs = "winxx";
|
2009-11-09 11:40:55 -06:00
|
|
|
#elif defined(__linux__)
|
2009-03-07 09:19:21 -06:00
|
|
|
HostOs = "linux";
|
2009-06-23 17:47:42 -05:00
|
|
|
#elif defined(__DARWIN__)
|
2009-03-07 09:19:21 -06:00
|
|
|
HostOs = "darwin";
|
2009-06-23 17:47:42 -05:00
|
|
|
#elif defined(__CYGWIN__)
|
2009-03-07 09:19:21 -06:00
|
|
|
HostOs = "cygwin";
|
2009-06-23 17:47:42 -05:00
|
|
|
#elif defined(__MINGW32__)
|
2009-03-07 09:19:21 -06:00
|
|
|
HostOs = "mingw32";
|
2009-11-10 01:36:19 -06:00
|
|
|
#elif defined(__ECOS)
|
|
|
|
HostOs = "ecos";
|
2009-03-07 09:19:21 -06:00
|
|
|
#else
|
2009-11-09 11:40:55 -06:00
|
|
|
#warn unrecognized host OS...
|
2009-03-07 09:19:21 -06:00
|
|
|
HostOs = "other";
|
|
|
|
#endif
|
2009-11-09 11:40:55 -06:00
|
|
|
Jim_SetGlobalVariableStr(interp, "ocd_HOSTOS",
|
|
|
|
Jim_NewStringObj(interp, HostOs , strlen(HostOs)));
|
2009-03-07 09:19:21 -06:00
|
|
|
|
2008-07-20 12:04:58 -05:00
|
|
|
Jim_CreateCommand(interp, "ocd_find", jim_find, NULL, NULL);
|
2008-07-18 01:24:57 -05:00
|
|
|
Jim_CreateCommand(interp, "echo", jim_echo, NULL, NULL);
|
2008-10-16 07:21:02 -05:00
|
|
|
Jim_CreateCommand(interp, "capture", jim_capture, NULL, NULL);
|
2008-07-18 01:24:57 -05:00
|
|
|
|
|
|
|
/* Set Jim's STDIO */
|
|
|
|
interp->cookie_stdin = interp;
|
|
|
|
interp->cookie_stdout = interp;
|
|
|
|
interp->cookie_stderr = interp;
|
|
|
|
interp->cb_fwrite = openocd_jim_fwrite;
|
|
|
|
interp->cb_fread = openocd_jim_fread ;
|
|
|
|
interp->cb_vfprintf = openocd_jim_vfprintf;
|
|
|
|
interp->cb_fflush = openocd_jim_fflush;
|
|
|
|
interp->cb_fgets = openocd_jim_fgets;
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2009-11-19 09:23:25 -06:00
|
|
|
register_command(context, NULL, "add_help_text",
|
|
|
|
handle_help_add_command, COMMAND_ANY,
|
|
|
|
"<command> [...] <help_text>] - "
|
|
|
|
"add new command help text");
|
|
|
|
|
2009-05-15 15:35:53 -05:00
|
|
|
#if !BUILD_ECOSBOARD
|
2008-08-07 11:37:20 -05:00
|
|
|
Jim_EventLoopOnLoad(interp);
|
2008-08-11 03:27:04 -05:00
|
|
|
#endif
|
2009-11-20 11:11:39 -06:00
|
|
|
Jim_SetAssocData(interp, "context", NULL, context);
|
2009-06-23 17:42:03 -05:00
|
|
|
if (Jim_Eval_Named(interp, startup_tcl, "embedded:startup.tcl",1) == JIM_ERR)
|
2008-07-18 01:24:57 -05:00
|
|
|
{
|
2009-11-09 11:40:55 -06:00
|
|
|
LOG_ERROR("Failed to run startup.tcl (embedded into OpenOCD)");
|
2008-07-18 01:24:57 -05:00
|
|
|
Jim_PrintErrorMessage(interp);
|
|
|
|
exit(-1);
|
|
|
|
}
|
2009-11-20 11:11:39 -06:00
|
|
|
Jim_DeleteAssocData(interp, "context");
|
2008-07-18 01:24:57 -05:00
|
|
|
|
2009-11-09 11:40:55 -06:00
|
|
|
register_command(context, NULL, "sleep",
|
|
|
|
handle_sleep_command, COMMAND_ANY,
|
|
|
|
"<n> [busy] - sleep for n milliseconds. "
|
|
|
|
"\"busy\" means busy wait");
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2009-11-19 08:48:37 -06:00
|
|
|
register_command(context, NULL, "help",
|
|
|
|
&handle_help_command, COMMAND_ANY,
|
|
|
|
"[<command_name> ...] - show built-in command help");
|
|
|
|
|
2008-02-25 11:48:04 -06:00
|
|
|
return context;
|
|
|
|
}
|
|
|
|
|
2009-11-13 15:25:47 -06:00
|
|
|
int command_context_mode(struct command_context *cmd_ctx, enum command_mode mode)
|
2008-07-17 03:34:14 -05:00
|
|
|
{
|
|
|
|
if (!cmd_ctx)
|
|
|
|
return ERROR_INVALID_ARGUMENTS;
|
|
|
|
|
|
|
|
cmd_ctx->mode = mode;
|
|
|
|
return ERROR_OK;
|
|
|
|
}
|
|
|
|
|
2008-10-09 03:08:29 -05:00
|
|
|
void process_jim_events(void)
|
2008-08-07 11:37:20 -05:00
|
|
|
{
|
2009-05-15 15:35:53 -05:00
|
|
|
#if !BUILD_ECOSBOARD
|
2008-08-07 11:37:20 -05:00
|
|
|
static int recursion = 0;
|
|
|
|
|
2008-10-09 03:08:29 -05:00
|
|
|
if (!recursion)
|
2008-08-07 11:37:20 -05:00
|
|
|
{
|
|
|
|
recursion++;
|
2009-06-23 17:45:15 -05:00
|
|
|
Jim_ProcessEvents (interp, JIM_ALL_EVENTS | JIM_DONT_WAIT);
|
2008-08-07 11:37:20 -05:00
|
|
|
recursion--;
|
|
|
|
}
|
2008-08-11 03:27:04 -05:00
|
|
|
#endif
|
2008-08-07 11:37:20 -05:00
|
|
|
}
|
|
|
|
|
2009-11-15 06:05:33 -06:00
|
|
|
void register_jim(struct command_context *cmd_ctx, const char *name,
|
|
|
|
Jim_CmdProc cmd, const char *help)
|
2008-07-18 01:24:57 -05:00
|
|
|
{
|
|
|
|
Jim_CreateCommand(interp, name, cmd, NULL, NULL);
|
|
|
|
|
2009-06-23 17:42:54 -05:00
|
|
|
Jim_Obj *cmd_list = Jim_NewListObj(interp, NULL, 0);
|
2009-11-11 10:20:09 -06:00
|
|
|
Jim_ListAppendElement(interp, cmd_list,
|
|
|
|
Jim_NewStringObj(interp, name, -1));
|
2008-10-09 03:08:29 -05:00
|
|
|
|
2009-11-19 09:23:25 -06:00
|
|
|
help_add_command(cmd_ctx, NULL, name, help);
|
2008-07-18 01:24:57 -05:00
|
|
|
}
|
2008-10-31 08:40:02 -05:00
|
|
|
|
2009-06-16 19:30:29 -05:00
|
|
|
#define DEFINE_PARSE_NUM_TYPE(name, type, func, min, max) \
|
2009-06-16 19:29:56 -05:00
|
|
|
int parse##name(const char *str, type *ul) \
|
|
|
|
{ \
|
2009-06-16 19:30:29 -05:00
|
|
|
if (!*str) \
|
2009-07-06 02:33:38 -05:00
|
|
|
{ \
|
|
|
|
LOG_ERROR("Invalid command argument"); \
|
2009-06-16 19:30:44 -05:00
|
|
|
return ERROR_COMMAND_ARGUMENT_INVALID; \
|
2009-07-06 02:33:38 -05:00
|
|
|
} \
|
2009-06-16 19:29:56 -05:00
|
|
|
char *end; \
|
|
|
|
*ul = func(str, &end, 0); \
|
2009-06-16 19:30:29 -05:00
|
|
|
if (*end) \
|
2009-07-06 02:33:38 -05:00
|
|
|
{ \
|
2009-07-06 03:01:22 -05:00
|
|
|
LOG_ERROR("Invalid command argument"); \
|
2009-06-16 19:30:44 -05:00
|
|
|
return ERROR_COMMAND_ARGUMENT_INVALID; \
|
2009-07-06 02:33:38 -05:00
|
|
|
} \
|
2009-06-16 19:31:06 -05:00
|
|
|
if ((max == *ul) && (ERANGE == errno)) \
|
2009-07-06 02:33:38 -05:00
|
|
|
{ \
|
2009-07-06 03:01:22 -05:00
|
|
|
LOG_ERROR("Argument overflow"); \
|
2009-06-16 19:30:44 -05:00
|
|
|
return ERROR_COMMAND_ARGUMENT_OVERFLOW; \
|
2009-07-06 02:33:38 -05:00
|
|
|
} \
|
2009-06-16 19:31:06 -05:00
|
|
|
if (min && (min == *ul) && (ERANGE == errno)) \
|
2009-07-06 02:33:38 -05:00
|
|
|
{ \
|
2009-07-06 03:01:22 -05:00
|
|
|
LOG_ERROR("Argument underflow"); \
|
2009-06-16 19:30:44 -05:00
|
|
|
return ERROR_COMMAND_ARGUMENT_UNDERFLOW; \
|
2009-07-06 02:33:38 -05:00
|
|
|
} \
|
2009-06-16 19:30:29 -05:00
|
|
|
return ERROR_OK; \
|
2009-06-16 19:29:56 -05:00
|
|
|
}
|
2009-06-16 19:30:29 -05:00
|
|
|
DEFINE_PARSE_NUM_TYPE(_ulong, unsigned long , strtoul, 0, ULONG_MAX)
|
|
|
|
DEFINE_PARSE_NUM_TYPE(_ullong, unsigned long long, strtoull, 0, ULLONG_MAX)
|
|
|
|
DEFINE_PARSE_NUM_TYPE(_long, long , strtol, LONG_MIN, LONG_MAX)
|
|
|
|
DEFINE_PARSE_NUM_TYPE(_llong, long long, strtoll, LLONG_MIN, LLONG_MAX)
|
2009-06-16 19:30:36 -05:00
|
|
|
|
|
|
|
#define DEFINE_PARSE_WRAPPER(name, type, min, max, functype, funcname) \
|
2009-06-16 19:30:52 -05:00
|
|
|
int parse##name(const char *str, type *ul) \
|
2009-06-16 19:30:36 -05:00
|
|
|
{ \
|
|
|
|
functype n; \
|
|
|
|
int retval = parse##funcname(str, &n); \
|
|
|
|
if (ERROR_OK != retval) \
|
|
|
|
return retval; \
|
|
|
|
if (n > max) \
|
2009-06-16 19:30:44 -05:00
|
|
|
return ERROR_COMMAND_ARGUMENT_OVERFLOW; \
|
2009-06-16 19:30:36 -05:00
|
|
|
if (min) \
|
2009-06-16 19:30:44 -05:00
|
|
|
return ERROR_COMMAND_ARGUMENT_UNDERFLOW; \
|
2009-06-16 19:30:36 -05:00
|
|
|
*ul = n; \
|
|
|
|
return ERROR_OK; \
|
2009-06-23 17:50:08 -05:00
|
|
|
}
|
2009-06-16 19:30:36 -05:00
|
|
|
|
|
|
|
#define DEFINE_PARSE_ULONG(name, type, min, max) \
|
|
|
|
DEFINE_PARSE_WRAPPER(name, type, min, max, unsigned long, _ulong)
|
2009-06-16 19:30:52 -05:00
|
|
|
DEFINE_PARSE_ULONG(_uint, unsigned, 0, UINT_MAX)
|
|
|
|
DEFINE_PARSE_ULONG(_u32, uint32_t, 0, UINT32_MAX)
|
|
|
|
DEFINE_PARSE_ULONG(_u16, uint16_t, 0, UINT16_MAX)
|
|
|
|
DEFINE_PARSE_ULONG(_u8, uint8_t, 0, UINT8_MAX)
|
2009-06-16 19:30:36 -05:00
|
|
|
|
|
|
|
#define DEFINE_PARSE_LONG(name, type, min, max) \
|
|
|
|
DEFINE_PARSE_WRAPPER(name, type, min, max, long, _long)
|
2009-06-16 19:30:52 -05:00
|
|
|
DEFINE_PARSE_LONG(_int, int, n < INT_MIN, INT_MAX)
|
|
|
|
DEFINE_PARSE_LONG(_s32, int32_t, n < INT32_MIN, INT32_MAX)
|
|
|
|
DEFINE_PARSE_LONG(_s16, int16_t, n < INT16_MIN, INT16_MAX)
|
|
|
|
DEFINE_PARSE_LONG(_s8, int8_t, n < INT8_MIN, INT8_MAX)
|
2009-11-18 08:58:27 -06:00
|
|
|
|
2009-11-18 14:41:20 -06:00
|
|
|
static int command_parse_bool(const char *in, bool *out,
|
2009-11-18 08:58:27 -06:00
|
|
|
const char *on, const char *off)
|
|
|
|
{
|
|
|
|
if (strcasecmp(in, on) == 0)
|
|
|
|
*out = true;
|
|
|
|
else if (strcasecmp(in, off) == 0)
|
|
|
|
*out = false;
|
|
|
|
else
|
|
|
|
return ERROR_COMMAND_SYNTAX_ERROR;
|
|
|
|
return ERROR_OK;
|
|
|
|
}
|
|
|
|
|
2009-11-18 14:41:20 -06:00
|
|
|
int command_parse_bool_arg(const char *in, bool *out)
|
2009-11-18 08:58:27 -06:00
|
|
|
{
|
|
|
|
if (command_parse_bool(in, out, "on", "off") == ERROR_OK)
|
|
|
|
return ERROR_OK;
|
|
|
|
if (command_parse_bool(in, out, "enable", "disable") == ERROR_OK)
|
|
|
|
return ERROR_OK;
|
|
|
|
if (command_parse_bool(in, out, "true", "false") == ERROR_OK)
|
|
|
|
return ERROR_OK;
|
|
|
|
if (command_parse_bool(in, out, "yes", "no") == ERROR_OK)
|
|
|
|
return ERROR_OK;
|
|
|
|
if (command_parse_bool(in, out, "1", "0") == ERROR_OK)
|
|
|
|
return ERROR_OK;
|
|
|
|
return ERROR_INVALID_ARGUMENTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
COMMAND_HELPER(handle_command_parse_bool, bool *out, const char *label)
|
|
|
|
{
|
|
|
|
switch (CMD_ARGC) {
|
|
|
|
case 1: {
|
|
|
|
const char *in = CMD_ARGV[0];
|
2009-11-18 14:41:20 -06:00
|
|
|
if (command_parse_bool_arg(in, out) != ERROR_OK)
|
2009-11-18 08:58:27 -06:00
|
|
|
{
|
|
|
|
LOG_ERROR("%s: argument '%s' is not valid", CMD_NAME, in);
|
|
|
|
return ERROR_INVALID_ARGUMENTS;
|
|
|
|
}
|
|
|
|
// fall through
|
|
|
|
}
|
|
|
|
case 0:
|
|
|
|
LOG_INFO("%s is %s", label, *out ? "enabled" : "disabled");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return ERROR_INVALID_ARGUMENTS;
|
|
|
|
}
|
|
|
|
return ERROR_OK;
|
|
|
|
}
|