remove extra newline before prompt
[openocd.git] / src / helper / command.c
index 81eadd3fad41191565ac2d9211c6e2bffcea4d7c..ecab9bdc25ee1f55b9139d8a5ad71e15667f0b86 100644 (file)
@@ -2,6 +2,12 @@
  *   Copyright (C) 2005 by Dominic Rath                                    *
  *   Dominic.Rath@gmx.de                                                   *
  *                                                                         *
+ *   Copyright (C) 2007,2008 Ã˜yvind Harboe                                 *
+ *   oyvind.harboe@zylin.com                                               *
+ *                                                                         *
+ *   Copyright (C) 2008, Duane Ellis                                       *
+ *   openocd@duaneeellis.com                                               *
+ *                                                                         *
  *   part of this file is taken from libcli (libcli.sourceforge.net)       *
  *   Copyright (C) David Parrish (david@dparrish.com)                      *
  *                                                                         *
@@ -31,6 +37,7 @@
 
 #include "log.h"
 #include "time_support.h"
+#include "jim-eventloop.h"
 
 #include <stdlib.h>
 #include <string.h>
@@ -49,40 +56,52 @@ int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **arg
 int run_command(command_context_t *context, command_t *c, char *words[], int num_words);
 
 static void tcl_output(void *privData, const char *file, int line, const char *function, const char *string)
-{              
+{
        Jim_Obj *tclOutput=(Jim_Obj *)privData;
 
        Jim_AppendString(interp, tclOutput, string, strlen(string));
 }
 
+extern command_context_t *global_cmd_ctx;
+
 static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 {
        /* the private data is stashed in the interp structure */
        command_t *c;
        command_context_t *context;
-       int *retval;
+       int retval;
        int i;
        int nwords;
        char **words;
 
-       target_call_timer_callbacks_now();
-       LOG_USER_N("%s", ""); /* Keep GDB connection alive*/ 
-       
+       /* 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;
+       if (c==NULL)
+       {
+               LOG_ERROR("BUG: interp->cmdPrivData==NULL");
+               return JIM_ERR;
+       }
+       target_call_timer_callbacks_now();
+       LOG_USER_N("%s", ""); /* Keep GDB connection alive*/
+
        LOG_DEBUG("script_command - %s", c->name);
 
        words = malloc(sizeof(char *) * argc);
        for (i = 0; i < argc; i++)
        {
                int len;
-               char *w=Jim_GetString(argv[i], &len);
+               const char *w=Jim_GetString(argv[i], &len);
                if (*w=='#')
                {
                        /* hit an end of line comment */
                        break;
                }
                words[i] = strdup(w);
-               if (words[i] == NULL) 
+               if (words[i] == NULL)
                {
                        return JIM_ERR;
                }
@@ -92,37 +111,54 @@ static int script_command(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
 
        /* grab the command context from the associated data */
        context = Jim_GetAssocData(interp, "context");
-       retval = Jim_GetAssocData(interp, "retval"); 
-       if (context != NULL && retval != NULL)
+       if (context == NULL)
        {
-               /* capture log output and return it */
-               Jim_Obj *tclOutput = Jim_NewStringObj(interp, "", 0);
-               log_add_callback(tcl_output, tclOutput);
-               
-               *retval = run_command(context, c, words, nwords);
-               
-               log_remove_callback(tcl_output, tclOutput);
-               
-               /* We dump output into this local variable */
-               Jim_SetVariableStr(interp, "ocd_output", tclOutput);
+               /* 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;
        }
 
+       /* 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);
+
+       retval = run_command(context, c, words, nwords);
+
+       log_remove_callback(tcl_output, tclOutput);
+
+       /* We dump output into this local variable */
+       Jim_SetResult(interp, tclOutput);
+       Jim_DecrRefCount(interp, tclOutput);
+
        for (i = 0; i < nwords; i++)
                free(words[i]);
        free(words);
 
-       return (*retval==ERROR_OK)?JIM_OK:JIM_ERR;
+       int *return_retval = Jim_GetAssocData(interp, "retval");
+       if (return_retval != NULL)
+       {
+               *return_retval = retval;
+       }
+
+       return (retval==ERROR_OK)?JIM_OK:JIM_ERR;
 }
 
+/* nice short description of source file */
+#define __THIS__FILE__ "command.c"
+
 command_t* register_command(command_context_t *context, command_t *parent, char *name, int (*handler)(struct command_context_s *context, char* name, char** args, int argc), enum command_mode mode, char *help)
 {
        command_t *c, *p;
-       
+
        if (!context || !name)
                return NULL;
-                               
+
        c = malloc(sizeof(command_t));
-       
+
        c->name = strdup(name);
        c->parent = parent;
        c->children = NULL;
@@ -131,7 +167,7 @@ command_t* register_command(command_context_t *context, command_t *parent, char
        if (!help)
                help="";
        c->next = NULL;
-       
+
        /* place command in tree */
        if (parent)
        {
@@ -161,14 +197,14 @@ command_t* register_command(command_context_t *context, command_t *parent, char
                        context->commands = c;
                }
        }
-       
+
        /* just a placeholder, no handler */
        if (c->handler==NULL)
                return c;
 
        /* If this is a two level command, e.g. "flash banks", then the
         * "unknown" proc in startup.tcl must redirect to  this command.
-        * 
+        *
         * "flash banks" is translated by "unknown" to "flash_banks"
         * if such a proc exists
         */
@@ -186,27 +222,27 @@ command_t* register_command(command_context_t *context, command_t *parent, char
        const char *full_name=alloc_printf("ocd_%s%s%s", t1, t2, t3);
        Jim_CreateCommand(interp, full_name, script_command, c, NULL);
        free((void *)full_name);
-       
+
        /* we now need to add an overrideable proc */
-       const char *override_name=alloc_printf("proc %s%s%s {args} {return [eval \"ocd_%s%s%s $args\"]}", t1, t2, t3, t1, t2, t3);
-       Jim_Eval(interp, override_name);        
+       const char *override_name=alloc_printf("proc %s%s%s {args} {if {[catch {eval \"ocd_%s%s%s $args\"}]==0} {return \"\"} else { return -code error }", t1, t2, t3, t1, t2, t3);
+       Jim_Eval_Named(interp, override_name, __THIS__FILE__, __LINE__ );
        free((void *)override_name);
-       
+
        /* accumulate help text in Tcl helptext list.  */
-    Jim_Obj *helptext=Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
-    if (Jim_IsShared(helptext))
-        helptext = Jim_DuplicateObj(interp, helptext);
+       Jim_Obj *helptext=Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
+       if (Jim_IsShared(helptext))
+               helptext = Jim_DuplicateObj(interp, helptext);
        Jim_Obj *cmd_entry=Jim_NewListObj(interp, NULL, 0);
-       
+
        Jim_Obj *cmd_list=Jim_NewListObj(interp, NULL, 0);
 
        /* maximum of two levels :-) */
        if (c->parent!=NULL)
        {
                Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->parent->name, -1));
-       } 
+       }
        Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, c->name, -1));
-       
+
        Jim_ListAppendElement(interp, cmd_entry, cmd_list);
        Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
        Jim_ListAppendElement(interp, helptext, cmd_entry);
@@ -216,14 +252,14 @@ command_t* register_command(command_context_t *context, command_t *parent, char
 int unregister_all_commands(command_context_t *context)
 {
        command_t *c, *c2;
-       
+
        if (context == NULL)
                return ERROR_OK;
-       
+
        while(NULL != context->commands)
        {
                c = context->commands;
-               
+
                while(NULL != c->children)
                {
                        c2 = c->children;
@@ -233,27 +269,29 @@ int unregister_all_commands(command_context_t *context)
                        free(c2);
                        c2 = NULL;
                }
-               
+
                context->commands = context->commands->next;
-               
+
                free(c->name);
                c->name = NULL;
                free(c);
-               c = NULL;               
+               c = NULL;
        }
-       
+
        return ERROR_OK;
 }
 
 int unregister_command(command_context_t *context, char *name)
 {
        command_t *c, *p = NULL, *c2;
-       
+
        if ((!context) || (!name))
                return ERROR_INVALID_ARGUMENTS;
-       
+
        /* find command */
-       for (c = context->commands; c; c = c->next)
+       c = context->commands;
+
+       while(NULL != c)
        {
                if (strcmp(name, c->name) == 0)
                {
@@ -264,28 +302,34 @@ int unregister_command(command_context_t *context, char *name)
                        }
                        else
                        {
+                               /* first element in command list */
                                context->commands = c->next;
                        }
-                       
+
                        /* unregister children */
-                       if (c->children)
+                       while(NULL != c->children)
                        {
-                               for (c2 = c->children; c2; c2 = c2->next)
-                               {
-                                       free(c2->name);
-                                       free(c2);
-                               }
+                               c2 = c->children;
+                               c->children = c->children->next;
+                               free(c2->name);
+                               c2->name = NULL;
+                               free(c2);
+                               c2 = NULL;
                        }
-                       
+
                        /* delete command */
                        free(c->name);
+                       c->name = NULL;
                        free(c);
+                       c = NULL;
+                       return ERROR_OK;
                }
-               
+
                /* remember the last command for unlinking */
                p = c;
+               c = c->next;
        }
-       
+
        return ERROR_OK;
 }
 
@@ -299,7 +343,7 @@ void command_output_text(command_context_t *context, const char *data)
 void command_print_n(command_context_t *context, char *format, ...)
 {
        char *string;
-       
+
        va_list ap;
        va_start(ap, format);
 
@@ -308,12 +352,12 @@ void command_print_n(command_context_t *context, char *format, ...)
        {
                /* we want this collected in the log + we also want to pick it up as a tcl return
                 * value.
-                * 
+                *
                 * The latter bit isn't precisely neat, but will do for now.
                 */
                LOG_USER_N("%s", string);
-               // We already printed it above
-               //command_output_text(context, string);
+               /* We already printed it above */
+               /* command_output_text(context, string); */
                free(string);
        }
 
@@ -333,12 +377,12 @@ void command_print(command_context_t *context, char *format, ...)
                strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
                /* we want this collected in the log + we also want to pick it up as a tcl return
                 * value.
-                * 
+                *
                 * The latter bit isn't precisely neat, but will do for now.
                 */
                LOG_USER_N("%s", string);
-               // We already printed it above
-               //command_output_text(context, string);
+               /* We already printed it above */
+               /* command_output_text(context, string); */
                free(string);
        }
 
@@ -354,7 +398,7 @@ int run_command(command_context_t *context, command_t *c, char *words[], int num
                LOG_ERROR("Illegal mode for command");
                return ERROR_FAIL;
        }
-       
+
        int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
        if (retval == ERROR_COMMAND_SYNTAX_ERROR)
        {
@@ -380,10 +424,10 @@ int run_command(command_context_t *context, command_t *c, char *words[], int num
                /* we do not print out an error message because the command *should*
                 * have printed out an error
                 */
-               LOG_DEBUG("Command failed with error code %d", retval); 
+               LOG_DEBUG("Command failed with error code %d", retval);
        }
-       
-       return retval; 
+
+       return retval;
 }
 
 int command_run_line(command_context_t *context, char *line)
@@ -393,21 +437,28 @@ int command_run_line(command_context_t *context, char *line)
         * results
         */
        /* run the line thru a script engine */
-       int retval;
+       int retval=ERROR_FAIL;
        int retcode;
-       Jim_DeleteAssocData(interp, "context"); /* remove existing */
+       /* 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");
        retcode = Jim_SetAssocData(interp, "context", NULL, context);
-       if (retcode != JIM_OK)
-               return ERROR_FAIL;
-
-       /* associated the return value */
-       retval = ERROR_OK;
-       Jim_DeleteAssocData(interp, "retval"); /* remove existing */
-       retcode = Jim_SetAssocData(interp, "retval", NULL, &retval);
-       if (retcode != JIM_OK)
-               return ERROR_FAIL;
+       if (retcode == JIM_OK)
+       {
+               /* associated the return value */
+               Jim_DeleteAssocData(interp, "retval");
+               retcode = Jim_SetAssocData(interp, "retval", NULL, &retval);
+               if (retcode == JIM_OK)
+               {
+                       retcode = Jim_Eval_Named(interp, line, __THIS__FILE__, __LINE__ );
 
-       retcode = Jim_Eval(interp, line);       
+                       Jim_DeleteAssocData(interp, "retval");
+               }
+               Jim_DeleteAssocData(interp, "context");
+       }
        if (retcode == JIM_ERR) {
                if (retval!=ERROR_COMMAND_CLOSE_CONNECTION)
                {
@@ -417,7 +468,7 @@ int command_run_line(command_context_t *context, char *line)
                if (retval==ERROR_OK)
                {
                        /* It wasn't a low level OpenOCD command that failed */
-                       return ERROR_FAIL; 
+                       return ERROR_FAIL;
                }
                return retval;
        } else if (retcode == JIM_EXIT) {
@@ -428,7 +479,8 @@ int command_run_line(command_context_t *context, char *line)
                int reslen;
 
                result = Jim_GetString(Jim_GetResult(interp), &reslen);
-               if (reslen) {
+               if (reslen>0)
+               {
                        int i;
                        char buff[256+1];
                        for (i = 0; i < reslen; i += 256)
@@ -438,11 +490,12 @@ int command_run_line(command_context_t *context, char *line)
                                if (chunk > 256)
                                        chunk = 256;
                                strncpy(buff, result+i, chunk);
-                               buff[chunk] = 0; 
+                               buff[chunk] = 0;
                                LOG_USER_N("%s", buff);
                        }
                        LOG_USER_N("%s", "\n");
                }
+               retval=ERROR_OK;
        }
        return retval;
 }
@@ -481,7 +534,7 @@ int command_done(command_context_t *context)
 {
        free(context);
        context = NULL;
-       
+
        return ERROR_OK;
 }
 
@@ -496,7 +549,7 @@ static int jim_find(Jim_Interp *interp, int argc, Jim_Obj *const *argv)
                return JIM_ERR;
        Jim_Obj *result = Jim_NewStringObj(interp, full_path, strlen(full_path));
        free(full_path);
-       
+
        Jim_SetResult(interp, result);
        return JIM_OK;
 }
@@ -515,7 +568,6 @@ static size_t openocd_jim_fwrite(const void *_ptr, size_t size, size_t n, void *
        size_t nbytes;
        const char *ptr;
        Jim_Interp *interp;
-       command_context_t *context;
 
        /* make it a char easier to read code */
        ptr = _ptr;
@@ -525,19 +577,11 @@ static size_t openocd_jim_fwrite(const void *_ptr, size_t size, size_t n, void *
                return 0;
        }
 
-       context = Jim_GetAssocData(interp, "context");
-       if (context == NULL)
-       {
-               LOG_ERROR("openocd_jim_fwrite: no command context");
-               /* TODO: Where should this go? */               
-               return n;
-       }
-
        /* do we have to chunk it? */
        if (ptr[nbytes] == 0)
        {
                /* no it is a C style string */
-               command_output_text(context, ptr);
+               LOG_USER_N("%s", ptr);
                return strlen(ptr);
        }
        /* GRR we must chunk - not null terminated */
@@ -554,11 +598,11 @@ static size_t openocd_jim_fwrite(const void *_ptr, size_t size, size_t n, void *
                /* terminate it */
                chunk[n] = 0;
                /* output it */
-               command_output_text(context, chunk);
+               LOG_USER_N("%s", chunk);
                ptr += x;
                nbytes -= x;
        }
-       
+
        return n;
 }
 
@@ -573,24 +617,16 @@ static int openocd_jim_vfprintf(void *cookie, const char *fmt, va_list ap)
        char *cp;
        int n;
        Jim_Interp *interp;
-       command_context_t *context;
 
        n = -1;
        interp = cookie;
        if (interp == NULL)
                return n;
 
-       context = Jim_GetAssocData(interp, "context");
-       if (context == NULL)
-       {
-               LOG_ERROR("openocd_jim_vfprintf: no command context");
-               return n;
-       }
-
        cp = alloc_vprintf(fmt, ap);
        if (cp)
        {
-               command_output_text(context, cp);
+               LOG_USER_N("%s", cp);
                n = strlen(cp);
                free(cp);
        }
@@ -610,10 +646,36 @@ static char* openocd_jim_fgets(char *s, int size, void *cookie)
        return NULL;
 }
 
+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);
+
+       retcode = Jim_Eval_Named(interp, str, __THIS__FILE__, __LINE__ );
+
+       log_remove_callback(tcl_output, tclOutput);
+
+       /* We dump output into this local variable */
+       Jim_SetResult(interp, tclOutput);
+       Jim_DecrRefCount(interp, tclOutput);
+
+       return retcode;
+}
+
 command_context_t* command_init()
 {
        command_context_t* context = malloc(sizeof(command_context_t));
-       extern unsigned const char startup_tcl[];
+       extern const char startup_tcl[];
+       const char *HostOs;
 
        context->mode = COMMAND_EXEC;
        context->commands = NULL;
@@ -629,8 +691,31 @@ command_context_t* command_init()
        Jim_RegisterCoreCommands(interp);
 #endif
 
+#if defined( _MSC_VER )
+       /* WinXX - is generic, the forward
+        * looking problem is this:
+        *
+        *   "win32" or "win64"
+        *
+        * "winxx" is generic.
+        */
+       HostOs = "winxx";
+#elif defined( __LINUX__)
+       HostOs = "linux";
+#elif defined( __DARWIN__ )
+       HostOs = "darwin";
+#elif defined( __CYGWIN__ )
+       HostOs = "cygwin";
+#elif defined( __MINGW32__ )
+       HostOs = "mingw32";
+#else
+       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;
@@ -641,10 +726,13 @@ command_context_t* command_init()
        interp->cb_vfprintf = openocd_jim_vfprintf;
        interp->cb_fflush = openocd_jim_fflush;
        interp->cb_fgets = openocd_jim_fgets;
-       
+
        add_default_dirs();
 
-       if (Jim_Eval(interp, startup_tcl)==JIM_ERR)
+#ifdef JIM_EMBEDDED
+       Jim_EventLoopOnLoad(interp);
+#endif
+       if (Jim_Eval_Named(interp, startup_tcl, "embedded:startup.tcl",1)==JIM_ERR)
        {
                LOG_ERROR("Failed to run startup.tcl (embedded into OpenOCD compile time)");
                Jim_PrintErrorMessage(interp);
@@ -652,11 +740,11 @@ command_context_t* command_init()
        }
 
        register_command(context, NULL, "sleep", handle_sleep_command,
-                                        COMMAND_ANY, "sleep for <n> milliseconds");
-       
+                                        COMMAND_ANY, "<n> [busy] - sleep for n milliseconds. \"busy\" means busy wait");
+
        register_command(context, NULL, "fast", handle_fast_command,
                                         COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
-       
+
        return context;
 }
 
@@ -675,11 +763,34 @@ int command_context_mode(command_context_t *cmd_ctx, enum command_mode mode)
 int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
 {
        unsigned long duration = 0;
-       
-       if (argc == 1)
+       int busy = 0;
+
+       if (argc==1)
+       {
+
+       } else if (argc==2)
        {
-               duration = strtoul(args[0], NULL, 0);
-               usleep(duration * 1000);
+               if (strcmp(args[1], "busy")!=0)
+                       return ERROR_COMMAND_SYNTAX_ERROR;
+               busy = 1;
+       } else
+       {
+               return ERROR_COMMAND_SYNTAX_ERROR;
+       }
+
+       duration = strtoul(args[0], NULL, 0);
+
+       if (busy)
+       {
+               busy_sleep(duration);
+       } else
+       {
+               long long then=timeval_ms();
+               while ((timeval_ms()-then)<(long long)duration)
+               {
+                       target_call_timer_callbacks_now();
+                       usleep(1000);
+               }
        }
 
        return ERROR_OK;
@@ -689,28 +800,54 @@ int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **arg
 {
        if (argc!=1)
                return ERROR_COMMAND_SYNTAX_ERROR;
-       
+
        fast_and_dangerous = strcmp("enable", args[0])==0;
-       
+
        return ERROR_OK;
 }
 
+void process_jim_events(void)
+{
+#ifdef JIM_EMBEDDED
+       static int recursion = 0;
+
+       if (!recursion)
+       {
+               recursion++;
+               Jim_ProcessEvents (interp, JIM_ALL_EVENTS|JIM_DONT_WAIT);
+               recursion--;
+       }
+#endif
+}
+
 void register_jim(struct command_context_s *cmd_ctx, const char *name, int (*cmd)(Jim_Interp *interp, int argc, Jim_Obj *const *argv), const char *help)
 {
        Jim_CreateCommand(interp, name, cmd, NULL, NULL);
 
-       /* FIX!!! it would be prettier to invoke add_help_text... 
-          accumulate help text in Tcl helptext list.  */
+       /* FIX!!! it would be prettier to invoke add_help_text...
+        * accumulate help text in Tcl helptext list.  */
        Jim_Obj *helptext=Jim_GetGlobalVariableStr(interp, "ocd_helptext", JIM_ERRMSG);
        if (Jim_IsShared(helptext))
                helptext = Jim_DuplicateObj(interp, helptext);
-    
+
        Jim_Obj *cmd_entry=Jim_NewListObj(interp, NULL, 0);
-       
+
        Jim_Obj *cmd_list=Jim_NewListObj(interp, NULL, 0);
        Jim_ListAppendElement(interp, cmd_list, Jim_NewStringObj(interp, name, -1));
-       
+
        Jim_ListAppendElement(interp, cmd_entry, cmd_list);
        Jim_ListAppendElement(interp, cmd_entry, Jim_NewStringObj(interp, help, -1));
        Jim_ListAppendElement(interp, helptext, cmd_entry);
 }
+
+/* return global variable long value or 0 upon failure */
+long jim_global_long(const char *variable)
+{
+       Jim_Obj *objPtr=Jim_GetGlobalVariableStr(interp, variable, JIM_ERRMSG);
+       long t;
+       if (Jim_GetLong(interp, objPtr, &t)==JIM_OK)
+       {
+               return t;
+       }
+       return 0;
+}

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)