#endif
// @todo the inclusion of target.h here is a layering violation
+#include <jtag/jtag.h>
#include <target/target.h>
#include "command.h"
#include "configuration.h"
return state;
}
-static void command_log_capture_finish(struct log_capture_state *state)
+/* Classic openocd commands provide progress output which we
+ * will capture and return as a Tcl return value.
+ *
+ * However, if a non-openocd command has been invoked, then it
+ * makes sense to return the tcl return value from that command.
+ *
+ * The tcl return value is empty for openocd commands that provide
+ * progress output.
+ *
+ * Therefore we set the tcl return value only if we actually
+ * captured output.
+ */
+static void command_log_capture_finish(struct log_capture_state *state)
{
if (NULL == state)
return;
log_remove_callback(tcl_output, state);
- Jim_SetResult(state->interp, state->output);
+ int length;
+ Jim_GetString(state->output, &length);
+
+ if (length > 0)
+ {
+ Jim_SetResult(state->interp, state->output);
+ } else
+ {
+ /* No output captured, use tcl return value (which could
+ * be empty too). */
+ }
Jim_DecrRefCount(state->interp, state->output);
free(state);
extern struct command_context *global_cmd_ctx;
+/* dump a single line to the log for the command.
+ * Do nothing in case we are not at debug level 3 */
void script_debug(Jim_Interp *interp, const char *name,
unsigned argc, Jim_Obj *const *argv)
{
- LOG_DEBUG("command - %s", name);
+ if (debug_level < LOG_LVL_DEBUG)
+ return;
+
+ char * dbg = alloc_printf("command - %s", name);
for (unsigned i = 0; i < argc; i++)
{
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);
+ char * t = alloc_printf("%s %s", dbg, w);
+ free (dbg);
+ dbg = t;
}
+ LOG_DEBUG("%s", dbg);
+ free(dbg);
}
static void script_command_args_free(const char **words, unsigned nwords)
{
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)
{
return words;
}
-static struct command_context *current_command_context(Jim_Interp *interp)
+struct command_context *current_command_context(Jim_Interp *interp)
{
/* grab the command context from the associated data */
struct command_context *cmd_ctx = Jim_GetAssocData(interp, "context");
if (NULL == cmd_ctx)
{
/* Tcl can invoke commands directly instead of via command_run_line(). This would
- * happen when the Jim Tcl interpreter is provided by eCos.
+ * happen when the Jim Tcl interpreter is provided by eCos or if we are running
+ * commands in a startup script.
+ *
+ * A telnet or gdb server would provide a non-default command context to
+ * handle piping of error output, have a separate current target, etc.
*/
cmd_ctx = global_cmd_ctx;
}
}
if (c->name)
- free(c->name);
+ free((void *)c->name);
if (c->help)
free((void*)c->help);
if (c->usage)
if (NULL == override_name)
return JIM_ERR;
- retval = Jim_Eval_Named(interp, override_name, __FILE__, __LINE__);
+ retval = Jim_Eval_Named(interp, override_name, 0, 0);
free((void *)override_name);
return retval;
struct command *c = command_find(*head, name);
if (NULL != c)
{
- LOG_ERROR("command '%s' is already registered in '%s' context",
+ /* TODO: originally we treated attempting to register a cmd twice as an error
+ * Sometimes we need this behaviour, such as with flash banks.
+ * http://www.mail-archive.com/openocd-development@lists.berlios.de/msg11152.html */
+ LOG_DEBUG("command '%s' is already registered in '%s' context",
name, parent ? parent->name : "<global>");
return c;
}
retcode = Jim_SetAssocData(interp, "retval", NULL, &retval);
if (retcode == JIM_OK)
{
- retcode = Jim_Eval_Named(interp, line, __THIS__FILE__, __LINE__);
+ retcode = Jim_Eval_Named(interp, line, 0, 0);
Jim_DeleteAssocData(interp, "retval");
}
if (retval != ERROR_COMMAND_CLOSE_CONNECTION)
{
/* We do not print the connection closed error message */
- Jim_PrintErrorMessage(interp);
+ Jim_MakeErrorMessage(interp);
+ LOG_USER("%s", Jim_GetString(Jim_GetResult(interp), NULL));
}
if (retval == ERROR_OK)
{
buff[chunk] = 0;
LOG_USER_N("%s", buff);
}
- LOG_USER_N("%s", "\n");
+ LOG_USER_N("\n");
}
retval = ERROR_OK;
}
if (string != NULL)
{
retval = command_run_line(context, string);
+ free(string);
}
va_end(ap);
return retval;
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)
+COMMAND_HANDLER(jim_echo)
{
- 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)
+ if (CMD_ARGC == 2 && !strcmp(CMD_ARGV[0], "-n"))
{
- LOG_USER_N("%s", cp);
- n = strlen(cp);
- free(cp);
+ LOG_USER_N("%s", CMD_ARGV[1]);
+ return JIM_OK;
}
- 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;
+ if (CMD_ARGC != 1)
+ return JIM_ERR;
+ LOG_USER("%s", CMD_ARGV[0]);
+ return JIM_OK;
}
+/* Capture progress output and return as tcl return value. If the
+ * progress output was empty, return tcl return value.
+ */
static int jim_capture(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
{
if (argc != 2)
struct log_capture_state *state = command_log_capture_start(interp);
+ /* disable polling during capture. This avoids capturing output
+ * from polling.
+ *
+ * This is necessary in order to avoid accidentially getting a non-empty
+ * string for tcl fn's.
+ */
+ bool save_poll = jtag_poll_get_enabled();
+
+ jtag_poll_set_enabled(false);
+
const char *str = Jim_GetString(argv[1], NULL);
int retcode = Jim_Eval_Named(interp, str, __THIS__FILE__, __LINE__);
+ jtag_poll_set_enabled(save_poll);
+
command_log_capture_finish(state);
return retcode;
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");
+ LOG_USER("%.*s", (int)(cp - last), last);
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;
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);
if (is_match && show_help)
{
- const char *stage_msg;
- switch (c->mode) {
- case COMMAND_CONFIG: stage_msg = "CONFIG"; break;
- case COMMAND_EXEC: stage_msg = "EXEC"; break;
- case COMMAND_ANY: stage_msg = "CONFIG or EXEC"; break;
- default: stage_msg = "***UNKNOWN***"; break;
- }
- char *msg = alloc_printf("%s%sValid Modes: %s",
- c->help ? : "", c->help ? " " : "", stage_msg);
+ 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);
COMMAND_HANDLER(handle_help_command)
{
bool full = strcmp(CMD_NAME, "help") == 0;
-
+ int retval;
struct command *c = CMD_CTX->commands;
+ char *match = NULL;
- const char *match = "";
if (CMD_ARGC == 0)
match = "";
- else if (CMD_ARGC == 1)
- match = CMD_ARGV[0];
- else
+ 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;
-
- return CALL_COMMAND_HANDLER(command_help_show_list, c, 0, full, match);
+
+ 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_context *cmd_ctx = current_command_context(interp);
enum command_mode mode;
+
if (argc > 1)
{
struct command *c = cmd_ctx->commands;
return help_add_command(CMD_CTX, c, cmd_name, help, usage);
}
-/* sleep command sleeps for <n> miliseconds
+/* sleep command sleeps for <n> milliseconds
* this is useful in target startup scripts
*/
COMMAND_HANDLER(handle_sleep_command)
{
.name = "mode",
.mode = COMMAND_ANY,
- .jim_handler = &jim_command_mode,
- .usage = "[<name> ...]",
+ .jim_handler = jim_command_mode,
+ .usage = "[command_name ...]",
.help = "Returns the command modes allowed by a command:"
"'any', 'config', or 'exec'. If no command is"
- "specified, returns the current command mode.",
+ "specified, returns the current command mode. "
+ "Returns 'unknown' if an unknown command is given. "
+ "Command can be multiple tokens.",
},
{
.name = "type",
.mode = COMMAND_ANY,
- .jim_handler = &jim_command_type,
- .usage = "<name> ...",
+ .jim_handler = jim_command_type,
+ .usage = "command_name [...]",
.help = "Returns the type of built-in command:"
- "'native', 'simple', 'group', or 'unknown'",
+ "'native', 'simple', 'group', or 'unknown'. "
+ "Command can be multiple tokens.",
},
COMMAND_REGISTRATION_DONE
};
static const struct command_registration command_builtin_handlers[] = {
+ {
+ .name = "echo",
+ .handler = jim_echo,
+ .mode = COMMAND_ANY,
+ .help = "Logs a message at \"user\" priority. "
+ "Output message to stdout. "
+ "Option \"-n\" suppresses trailing newline",
+ .usage = "[-n] string",
+ },
{
.name = "add_help_text",
- .handler = &handle_help_add_command,
+ .handler = handle_help_add_command,
.mode = COMMAND_ANY,
- .help = "add new command help text",
- .usage = "<command> [...] <help_text>]",
+ .help = "Add new command help text; "
+ "Command can be multiple tokens.",
+ .usage = "command_name helptext_string",
},
{
.name = "add_usage_text",
- .handler = &handle_help_add_command,
+ .handler = handle_help_add_command,
.mode = COMMAND_ANY,
- .help = "add new command usage text",
- .usage = "<command> [...] <usage_text>]",
+ .help = "Add new command usage text; "
+ "command can be multiple tokens.",
+ .usage = "command_name usage_string",
},
{
.name = "sleep",
- .handler = &handle_sleep_command,
+ .handler = handle_sleep_command,
.mode = COMMAND_ANY,
- .help = "sleep for n milliseconds. "
- "\"busy\" will busy wait",
- .usage = "<n> [busy]",
+ .help = "Sleep for specified number of milliseconds. "
+ "\"busy\" will busy wait instead (avoid this).",
+ .usage = "milliseconds ['busy']",
},
{
.name = "help",
- .handler = &handle_help_command,
+ .handler = handle_help_command,
.mode = COMMAND_ANY,
- .help = "show full command help",
- .usage = "[<command> ...]",
+ .help = "Show full command help; "
+ "command can be multiple tokens.",
+ .usage = "[command_name]",
},
{
.name = "usage",
- .handler = &handle_help_command,
+ .handler = handle_help_command,
.mode = COMMAND_ANY,
- .help = "show basic command usage",
- .usage = "[<command> ...]",
+ .help = "Show basic command usage; "
+ "command can be multiple tokens.",
+ .usage = "[command_name]",
},
{
.name = "command",
/* Create a jim interpreter if we were not handed one */
if (interp == NULL)
{
- Jim_InitEmbedded();
/* Create an interpreter */
interp = Jim_CreateInterp();
/* Add all the Jim core commands */
Jim_RegisterCoreCommands(interp);
+ Jim_InitStaticExtensions(interp);
}
#endif
context->interp = interp;
+ /* Stick to lowercase for HostOS strings. */
#if defined(_MSC_VER)
/* WinXX - is generic, the forward
* looking problem is this:
HostOs = "winxx";
#elif defined(__linux__)
HostOs = "linux";
-#elif defined(__DARWIN__)
+#elif defined(__APPLE__) || defined(__DARWIN__)
HostOs = "darwin";
#elif defined(__CYGWIN__)
HostOs = "cygwin";
HostOs = "mingw32";
#elif defined(__ECOS)
HostOs = "ecos";
+#elif defined(__FreeBSD__)
+ HostOs = "freebsd";
#else
-#warn unrecognized host OS...
+#warning "Unrecognized host OS..."
HostOs = "other";
#endif
Jim_SetGlobalVariableStr(interp, "ocd_HOSTOS",
Jim_NewStringObj(interp, HostOs , strlen(HostOs)));
Jim_CreateCommand(interp, "ocd_find", jim_find, NULL, NULL);
- Jim_CreateCommand(interp, "echo", jim_echo, NULL, NULL);
Jim_CreateCommand(interp, "capture", jim_capture, NULL, NULL);
- /* 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;
-
register_commands(context, NULL, command_builtin_handlers);
-#if !BUILD_ECOSBOARD
- Jim_EventLoopOnLoad(interp);
-#endif
Jim_SetAssocData(interp, "context", NULL, context);
if (Jim_Eval_Named(interp, startup_tcl, "embedded:startup.tcl",1) == JIM_ERR)
{
LOG_ERROR("Failed to run startup.tcl (embedded into OpenOCD)");
- Jim_PrintErrorMessage(interp);
+ Jim_MakeErrorMessage(interp);
+ LOG_USER_N("%s", Jim_GetString(Jim_GetResult(interp), NULL));
exit(-1);
}
Jim_DeleteAssocData(interp, "context");