src: add loader src description
[openocd.git] / src / helper / jim-eventloop.c
index c3c49d4ded4265efecd819f31f2f055dd53c092c..fe2d685bcec9507984eb64052e43a78531fe065f 100644 (file)
-/* Jim - A small embeddable Tcl interpreter\r
- *\r
- * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>\r
- * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>\r
- * Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net> \r
- * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com\r
- * Copyright 2008 Andrew Lunn <andrew@lunn.ch>\r
- * Copyright 2008 Duane Ellis <openocd@duaneellis.com>\r
- * Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de>\r
- * \r
- * The FreeBSD license\r
- * \r
- * Redistribution and use in source and binary forms, with or without\r
- * modification, are permitted provided that the following conditions\r
- * are met:\r
- * \r
- * 1. Redistributions of source code must retain the above copyright\r
- *    notice, this list of conditions and the following disclaimer.\r
- * 2. Redistributions in binary form must reproduce the above\r
- *    copyright notice, this list of conditions and the following\r
- *    disclaimer in the documentation and/or other materials\r
- *    provided with the distribution.\r
- * \r
- * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY\r
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,\r
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\r
- * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE\r
- * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\r
- * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\r
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS\r
- * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\r
- * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\r
- * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\r
- * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\r
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
- * \r
- * The views and conclusions contained in the software and documentation\r
- * are those of the authors and should not be interpreted as representing\r
- * official policies, either expressed or implied, of the Jim Tcl Project.\r
- **/\r
-/* TODO:\r
- *\r
- *  - to really use flags in Jim_ProcessEvents()\r
- *  - more complete [after] command with [after info] and other subcommands.\r
- *  - Win32 port\r
- */\r
-\r
-#define JIM_EXTENSION\r
-#define __JIM_EVENTLOOP_CORE__\r
-#ifdef __ECOS\r
-#include <pkgconf/jimtcl.h>\r
-#endif\r
-#ifdef __ECOS\r
-#include <cyg/jimtcl/jim.h>\r
-#include <cyg/jimtcl/jim-eventloop.h>\r
-#else\r
-#include "jim.h"\r
-#include "jim-eventloop.h"\r
-#endif\r
-\r
-/* POSIX includes */\r
-#include <sys/time.h>\r
-#include <sys/types.h>\r
-#include <unistd.h>\r
-#include <sys/select.h>\r
-#include <errno.h>\r
-       extern int errno;\r
-/* --- */\r
-\r
-/* File event structure */\r
-typedef struct Jim_FileEvent {\r
-    void *handle;\r
-    int mask; /* one of JIM_EVENT_(READABLE|WRITABLE|EXCEPTION) */\r
-    Jim_FileProc *fileProc;\r
-    Jim_EventFinalizerProc *finalizerProc;\r
-    void *clientData;\r
-    struct Jim_FileEvent *next;\r
-} Jim_FileEvent;\r
-\r
-/* Time event structure */\r
-typedef struct Jim_TimeEvent {\r
-    jim_wide id; /* time event identifier. */\r
-    int mode;  /* restart, repetitive .. UK */\r
-    long initialms; /* initial relativ timer value UK */\r
-    long when_sec; /* seconds */\r
-    long when_ms; /* milliseconds */\r
-    Jim_TimeProc *timeProc;\r
-    Jim_EventFinalizerProc *finalizerProc;\r
-    void *clientData;\r
-    struct Jim_TimeEvent *next;\r
-} Jim_TimeEvent;\r
-\r
-/* Per-interp stucture containing the state of the event loop */\r
-typedef struct Jim_EventLoop {\r
-    jim_wide timeEventNextId;\r
-    Jim_FileEvent *fileEventHead;\r
-    Jim_TimeEvent *timeEventHead;\r
-} Jim_EventLoop;\r
-\r
-void Jim_CreateFileHandler(Jim_Interp *interp, void *handle, int mask,\r
-        Jim_FileProc *proc, void *clientData,\r
-        Jim_EventFinalizerProc *finalizerProc)\r
-{\r
-    Jim_FileEvent *fe;\r
-    Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");\r
-\r
-       // fprintf(stderr,"rein\n");\r
-    fe = Jim_Alloc(sizeof(*fe));\r
-    fe->handle = handle;\r
-    fe->mask = mask;\r
-    fe->fileProc = proc;\r
-    fe->finalizerProc = finalizerProc;\r
-    fe->clientData = clientData;\r
-    fe->next = eventLoop->fileEventHead;\r
-    eventLoop->fileEventHead = fe;\r
-       // fprintf(stderr,"raus\n");\r
-}\r
-\r
-void Jim_DeleteFileHandler(Jim_Interp *interp, void *handle)\r
-{\r
-    Jim_FileEvent *fe, *prev = NULL;\r
-    Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");\r
-\r
-    fe = eventLoop->fileEventHead;\r
-    while(fe) {\r
-        if (fe->handle == handle) {\r
-            if (prev == NULL)\r
-                eventLoop->fileEventHead = fe->next;\r
-            else\r
-                prev->next = fe->next;\r
-            if (fe->finalizerProc)\r
-                fe->finalizerProc(interp, fe->clientData);\r
-            Jim_Free(fe);\r
-            return;\r
-        }\r
-        prev = fe;\r
-        fe = fe->next;\r
-    }\r
-}\r
-\r
-// The same for signals.\r
-void Jim_CreateSignalHandler(Jim_Interp *interp, int signum, \r
-        Jim_FileProc *proc, void *clientData,\r
-        Jim_EventFinalizerProc *finalizerProc)\r
-{\r
-}\r
-void Jim_DeleteSignalHandler(Jim_Interp *interp, int signum) \r
-{\r
-}\r
-\r
-/* That's another part of this extension that needs to be ported\r
- * to WIN32. */\r
-static void JimGetTime(long *seconds, long *milliseconds)\r
-{\r
-    struct timeval tv;\r
-\r
-    gettimeofday(&tv, NULL);\r
-    *seconds = tv.tv_sec;\r
-    *milliseconds = tv.tv_usec/1000;\r
-}\r
-\r
-jim_wide Jim_CreateTimeHandler(Jim_Interp *interp, jim_wide milliseconds,\r
-        Jim_TimeProc *proc, void *clientData,\r
-        Jim_EventFinalizerProc *finalizerProc)\r
-{\r
-    Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");\r
-    jim_wide id = eventLoop->timeEventNextId++;\r
-    Jim_TimeEvent *te;\r
-    long cur_sec, cur_ms;\r
-\r
-    JimGetTime(&cur_sec, &cur_ms);\r
-\r
-    te = Jim_Alloc(sizeof(*te));\r
-    te->id = id;\r
-    te->mode = 0;\r
-    te->initialms = milliseconds;\r
-    te->when_sec = cur_sec + milliseconds/1000;\r
-    te->when_ms = cur_ms + milliseconds%1000;\r
-    if (te->when_ms >= 1000) {\r
-        te->when_sec ++;\r
-        te->when_ms -= 1000;\r
-    }\r
-    te->timeProc = proc;\r
-    te->finalizerProc = finalizerProc;\r
-    te->clientData = clientData;\r
-    te->next = eventLoop->timeEventHead;\r
-    eventLoop->timeEventHead = te;\r
-    return id;\r
-}\r
-\r
-jim_wide Jim_DeleteTimeHandler(Jim_Interp *interp, jim_wide id)\r
-{\r
-    Jim_TimeEvent *te, *prev = NULL;\r
-    Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");\r
-    long cur_sec, cur_ms;\r
-    jim_wide remain ;\r
-\r
-    JimGetTime(&cur_sec, &cur_ms);\r
-\r
-    te = eventLoop->timeEventHead;\r
-    if (id >= eventLoop->timeEventNextId) \r
-       return -2; /* wrong event ID */\r
-    while(te) {\r
-        if (te->id == id) {\r
-            remain  = (te->when_sec - cur_sec) * 1000;\r
-            remain += (te->when_ms  - cur_ms) ;\r
-           remain = (remain < 0) ? 0 : remain ;\r
-\r
-            if (prev == NULL)\r
-                eventLoop->timeEventHead = te->next;\r
-            else\r
-                prev->next = te->next;\r
-            if (te->finalizerProc)\r
-                te->finalizerProc(interp, te->clientData);\r
-            Jim_Free(te);\r
-            return remain;\r
-        }\r
-        prev = te;\r
-        te = te->next;\r
-    }\r
-    return -1; /* NO event with the specified ID found */\r
-}\r
-\r
-/* Search the first timer to fire.\r
- * This operation is useful to know how many time the select can be\r
- * put in sleep without to delay any event.\r
- * If there are no timers NULL is returned. */\r
-static Jim_TimeEvent *JimSearchNearestTimer(Jim_EventLoop *eventLoop)\r
-{\r
-    Jim_TimeEvent *te = eventLoop->timeEventHead;\r
-    Jim_TimeEvent *nearest = NULL;\r
-\r
-    while(te) {\r
-        if (!nearest || te->when_sec < nearest->when_sec ||\r
-                (te->when_sec == nearest->when_sec &&\r
-                 te->when_ms < nearest->when_ms))\r
-            nearest = te;\r
-        te = te->next;\r
-    }\r
-    return nearest;\r
-}\r
-\r
-/* Process every pending time event, then every pending file event\r
- * (that may be registered by time event callbacks just processed).\r
- * Without special flags the function sleeps until some file event\r
- * fires, or when the next time event occurrs (if any).\r
- *\r
- * If flags is 0, the function does nothing and returns.\r
- * if flags has JIM_ALL_EVENTS set, all the kind of events are processed.\r
- * if flags has JIM_FILE_EVENTS set, file events are processed.\r
- * if flags has JIM_TIME_EVENTS set, time events are processed.\r
- * if flags has JIM_DONT_WAIT set the function returns ASAP until all\r
- * the events that's possible to process without to wait are processed.\r
- *\r
- * The function returns the number of events processed. */\r
-int Jim_ProcessEvents(Jim_Interp *interp, int flags)\r
-{\r
-    int maxfd = 0, numfd = 0, processed = 0;\r
-    fd_set rfds, wfds, efds;\r
-    Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");\r
-    Jim_FileEvent *fe = eventLoop->fileEventHead;\r
-    Jim_TimeEvent *te;\r
-    jim_wide maxId;\r
-    JIM_NOTUSED(flags);\r
-\r
-    FD_ZERO(&rfds);\r
-    FD_ZERO(&wfds);\r
-    FD_ZERO(&efds);\r
-\r
-    /* Check file events */\r
-    while (fe != NULL) {\r
-        int fd = fileno(fe->handle);\r
-\r
-        if (fe->mask & JIM_EVENT_READABLE) \r
-               FD_SET(fd, &rfds);\r
-        if (fe->mask & JIM_EVENT_WRITABLE) FD_SET(fd, &wfds);\r
-        if (fe->mask & JIM_EVENT_EXCEPTION) FD_SET(fd, &efds);\r
-        if (maxfd < fd) maxfd = fd;\r
-        numfd++;\r
-        fe = fe->next;\r
-    }\r
-    /* Note that we want call select() even if there are no\r
-     * file events to process as long as we want to process time\r
-     * events, in order to sleep until the next time event is ready\r
-     * to fire. */\r
-    if (numfd || ((flags & JIM_TIME_EVENTS) && !(flags & JIM_DONT_WAIT))) {\r
-        int retval;\r
-        Jim_TimeEvent *shortest;\r
-        struct timeval tv, *tvp;\r
-       jim_wide dt;\r
-\r
-        shortest = JimSearchNearestTimer(eventLoop);\r
-        if (shortest) {\r
-            long now_sec, now_ms;\r
-\r
-            /* Calculate the time missing for the nearest\r
-             * timer to fire. */\r
-            JimGetTime(&now_sec, &now_ms);\r
-            tvp = &tv;\r
-           dt   = 1000 * (shortest->when_sec - now_sec);\r
-           dt  += ( shortest->when_ms  - now_ms);\r
-            if (dt < 0) {\r
-               dt = 1;\r
-           }\r
-           tvp->tv_sec  = dt / 1000;\r
-           tvp->tv_usec = dt % 1000;\r
-           // fprintf(stderr,"Next %d.% 8d\n",(int)tvp->tv_sec,(int)tvp->tv_usec);\r
-        } else {\r
-            tvp = NULL; /* wait forever */\r
-               // fprintf(stderr,"No Event\n");\r
-        }\r
-\r
-        retval = select(maxfd+1, &rfds, &wfds, &efds, tvp);\r
-        if (retval < 0) {\r
-          switch (errno) {\r
-              case EINTR:   fprintf(stderr,"select EINTR\n"); break;\r
-              case EINVAL:  fprintf(stderr,"select EINVAL\n"); break;\r
-              case ENOMEM:  fprintf(stderr,"select ENOMEM\n"); break;\r
-          }\r
-       } else if (retval > 0) {\r
-            fe = eventLoop->fileEventHead;\r
-            while(fe != NULL) {\r
-                int fd = fileno(fe->handle);\r
-\r
-               // fprintf(stderr,"fd: %d mask: %02x \n",fd,fe->mask);\r
-\r
-                if ((fe->mask & JIM_EVENT_READABLE && FD_ISSET(fd, &rfds)) ||\r
-                    (fe->mask & JIM_EVENT_WRITABLE && FD_ISSET(fd, &wfds)) ||\r
-                    (fe->mask & JIM_EVENT_EXCEPTION && FD_ISSET(fd, &efds)))\r
-                {\r
-                    int mask = 0;\r
-\r
-                    if (fe->mask & JIM_EVENT_READABLE && FD_ISSET(fd, &rfds)) {\r
-                        mask |= JIM_EVENT_READABLE;\r
-                       if ((fe->mask & JIM_EVENT_FEOF) && feof((FILE *)fe->handle))\r
-                               mask |= JIM_EVENT_FEOF;\r
-                   }\r
-                    if (fe->mask & JIM_EVENT_WRITABLE && FD_ISSET(fd, &wfds))\r
-                        mask |= JIM_EVENT_WRITABLE;\r
-                    if (fe->mask & JIM_EVENT_EXCEPTION && FD_ISSET(fd, &efds))\r
-                        mask |= JIM_EVENT_EXCEPTION;\r
-                    if (fe->fileProc(interp, fe->clientData, mask) == JIM_ERR) {\r
-                        /* Remove the element on handler error */\r
-                        Jim_DeleteFileHandler(interp, fe->handle);\r
-                    }\r
-                    processed++;\r
-                    /* After an event is processed our file event list\r
-                     * may no longer be the same, so what we do\r
-                     * is to clear the bit for this file descriptor and\r
-                     * restart again from the head. */\r
-                    fe = eventLoop->fileEventHead;\r
-                    FD_CLR(fd, &rfds);\r
-                    FD_CLR(fd, &wfds);\r
-                    FD_CLR(fd, &efds);\r
-                } else {\r
-                    fe = fe->next;\r
-                }\r
-            }\r
-        }\r
-    }\r
-    /* Check time events */\r
-    te = eventLoop->timeEventHead;\r
-    maxId = eventLoop->timeEventNextId-1;\r
-    while(te) {\r
-        long now_sec, now_ms;\r
-        jim_wide id;\r
-\r
-        if (te->id > maxId) {\r
-            te = te->next;\r
-            continue;\r
-        }\r
-        JimGetTime(&now_sec, &now_ms);\r
-        if (now_sec > te->when_sec ||\r
-            (now_sec == te->when_sec && now_ms >= te->when_ms))\r
-        {\r
-            id = te->id;\r
-            te->timeProc(interp, te->clientData);\r
-            /* After an event is processed our time event list may\r
-             * no longer be the same, so we restart from head.\r
-             * Still we make sure to don't process events registered\r
-             * by event handlers itself in order to don't loop forever\r
-             * even in case an [after 0] that continuously register\r
-             * itself. To do so we saved the max ID we want to handle. */\r
-            Jim_DeleteTimeHandler(interp, id);\r
-            te = eventLoop->timeEventHead;\r
-        } else {\r
-            te = te->next;\r
-        }\r
-    }\r
-\r
-    return processed;\r
-}\r
-/* ---------------------------------------------------------------------- */\r
-\r
-void JimELAssocDataDeleProc(Jim_Interp *interp, void *data)\r
-{\r
-    void *next;\r
-    Jim_FileEvent *fe;\r
-    Jim_TimeEvent *te;\r
-    Jim_EventLoop *eventLoop = data;\r
-\r
-    fe = eventLoop->fileEventHead;\r
-    while(fe) {\r
-        next = fe->next;\r
-        if (fe->finalizerProc)\r
-            fe->finalizerProc(interp, fe->clientData);\r
-        Jim_Free(fe);\r
-        fe = next;\r
-    }\r
-\r
-    te = eventLoop->timeEventHead;\r
-    while(te) {\r
-        next = te->next;\r
-        if (te->finalizerProc)\r
-            te->finalizerProc(interp, te->clientData);\r
-        Jim_Free(te);\r
-        te = next;\r
-    }\r
-    Jim_Free(data);\r
-}\r
-\r
-static int JimELVwaitCommand(Jim_Interp *interp, int argc, \r
-        Jim_Obj *const *argv)\r
-{\r
-    Jim_Obj *oldValue;\r
-\r
-    if (argc != 2) {\r
-        Jim_WrongNumArgs(interp, 1, argv, "name");\r
-        return JIM_ERR;\r
-    }\r
-    oldValue = Jim_GetGlobalVariable(interp, argv[1], JIM_NONE);\r
-    if (oldValue) Jim_IncrRefCount(oldValue);\r
-    while (1) {\r
-        Jim_Obj *currValue;\r
-\r
-        Jim_ProcessEvents(interp, JIM_ALL_EVENTS);\r
-        currValue = Jim_GetGlobalVariable(interp, argv[1], JIM_NONE);\r
-        /* Stop the loop if the vwait-ed variable changed value,\r
-         * or if was unset and now is set (or the contrary). */\r
-        if ((oldValue && !currValue) ||\r
-            (!oldValue && currValue) ||\r
-            (oldValue && currValue &&\r
-             !Jim_StringEqObj(oldValue, currValue, JIM_CASESENS)))\r
-            break;\r
-    }\r
-    if (oldValue) Jim_DecrRefCount(interp, oldValue);\r
-    return JIM_OK;\r
-}\r
-\r
-void JimAfterTimeHandler(Jim_Interp *interp, void *clientData)\r
-{\r
-    Jim_Obj *objPtr = clientData;\r
-\r
-    Jim_EvalObjBackground(interp, objPtr);\r
-}\r
-\r
-void JimAfterTimeEventFinalizer(Jim_Interp *interp, void *clientData)\r
-{\r
-    Jim_Obj *objPtr = clientData;\r
-\r
-    Jim_DecrRefCount(interp, objPtr);\r
-}\r
-\r
-static int JimELAfterCommand(Jim_Interp *interp, int argc, \r
-        Jim_Obj *const *argv)\r
-{\r
-    jim_wide ms, id;\r
-    Jim_Obj *objPtr, *idObjPtr;\r
-    const char *options[] = {\r
-       "info", "cancel", "restart", "expire", NULL\r
-    };\r
-    enum {INFO, CANCEL, RESTART, EXPIRE, CREATE };\r
-    int option = CREATE ;\r
-\r
-    if (argc < 3) {\r
-        Jim_WrongNumArgs(interp, 1, argv, "<after milliseconds> script");\r
-        return JIM_ERR;\r
-    }\r
-    if (Jim_GetWide(interp, argv[1], &ms) != JIM_OK)\r
-        if (Jim_GetEnum(interp, argv[1], options, &option, "after options",\r
-                    JIM_ERRMSG) != JIM_OK)\r
-            return JIM_ERR;\r
-    switch (option) {\r
-    case CREATE:\r
-        Jim_IncrRefCount(argv[2]);\r
-        id = Jim_CreateTimeHandler(interp, ms, JimAfterTimeHandler, argv[2],\r
-                JimAfterTimeEventFinalizer);\r
-        objPtr = Jim_NewStringObj(interp, NULL, 0);\r
-        Jim_AppendString(interp, objPtr, "after#", -1);\r
-        idObjPtr = Jim_NewIntObj(interp, id);\r
-        Jim_IncrRefCount(idObjPtr);\r
-        Jim_AppendObj(interp, objPtr, idObjPtr);\r
-        Jim_DecrRefCount(interp, idObjPtr);\r
-        Jim_SetResult(interp, objPtr);\r
-        return JIM_OK;\r
-    case CANCEL:\r
-       {\r
-       int tlen ;\r
-       jim_wide remain = 0;\r
-       const char *tok = Jim_GetString(argv[2], &tlen);\r
-       if ( sscanf(tok,"after#%lld",&id) == 1) {\r
-               remain =  Jim_DeleteTimeHandler(interp, id);\r
-               if (remain > -2)  {\r
-                       Jim_SetResult(interp, Jim_NewIntObj(interp, remain));\r
-                       return JIM_OK;\r
-               }\r
-       }\r
-        Jim_SetResultString(interp, "invalid event" , -1);\r
-        return JIM_ERR;\r
-       }\r
-    default:\r
-       fprintf(stderr,"unserviced option to after %d\n",option);\r
-    } \r
-    return JIM_OK;\r
-}\r
-\r
-/* This extension is not dynamically loaded, instead it's linked statically,\r
-   which is why we shouldn't use the unspecific 'Jim_OnLoad' name */\r
-#define Jim_OnLoad Jim_EventLoopOnLoad\r
-\r
-int Jim_OnLoad(Jim_Interp *interp)\r
-{\r
-    Jim_EventLoop *eventLoop;\r
-\r
-    Jim_InitExtension(interp);\r
-    if (Jim_PackageProvide(interp, "eventloop", "1.0", JIM_ERRMSG) != JIM_OK)\r
-        return JIM_ERR;\r
-\r
-    eventLoop = Jim_Alloc(sizeof(*eventLoop));\r
-    eventLoop->fileEventHead = NULL;\r
-    eventLoop->timeEventHead = NULL;\r
-    eventLoop->timeEventNextId = 1;\r
-    Jim_SetAssocData(interp, "eventloop", JimELAssocDataDeleProc, eventLoop);\r
-\r
-    Jim_CreateCommand(interp, "vwait", JimELVwaitCommand, NULL, NULL);\r
-    Jim_CreateCommand(interp, "after", JimELAfterCommand, NULL, NULL);\r
-\r
-    /* Export events API */\r
-    Jim_RegisterApi(interp, "Jim_CreateFileHandler", Jim_CreateFileHandler);\r
-    Jim_RegisterApi(interp, "Jim_DeleteFileHandler", Jim_DeleteFileHandler);\r
-    Jim_RegisterApi(interp, "Jim_CreateTimeHandler", Jim_CreateTimeHandler);\r
-    Jim_RegisterApi(interp, "Jim_DeleteTimeHandler", Jim_DeleteTimeHandler);\r
-    Jim_RegisterApi(interp, "Jim_ProcessEvents", Jim_ProcessEvents);\r
-    return JIM_OK;\r
-}\r
+/* Jim - A small embeddable Tcl interpreter
+ *
+ * Copyright 2005 Salvatore Sanfilippo <antirez@invece.org>
+ * Copyright 2005 Clemens Hintze <c.hintze@gmx.net>
+ * Copyright 2005 patthoyts - Pat Thoyts <patthoyts@users.sf.net>
+ * Copyright 2008 oharboe - Øyvind Harboe - oyvind.harboe@zylin.com
+ * Copyright 2008 Andrew Lunn <andrew@lunn.ch>
+ * Copyright 2008 Duane Ellis <openocd@duaneellis.com>
+ * Copyright 2008 Uwe Klein <uklein@klein-messgeraete.de>
+ *
+ * The FreeBSD license
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE JIM TCL PROJECT ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * JIM TCL PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * The views and conclusions contained in the software and documentation
+ * are those of the authors and should not be interpreted as representing
+ * official policies, either expressed or implied, of the Jim Tcl Project.
+ **/
+/* TODO:
+ *
+ *  - to really use flags in Jim_ProcessEvents()
+ *  - more complete [after] command with [after info] and other subcommands.
+ *  - Win32 port
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#define JIM_EXTENSION
+#define __JIM_EVENTLOOP_CORE__
+#ifdef __ECOS
+#include <pkgconf/jimtcl.h>
+#include <sys/time.h>
+#include <cyg/jimtcl/jim.h>
+#include <cyg/jimtcl/jim-eventloop.h>
+#else
+#include "jim.h"
+#include "jim-eventloop.h"
+#endif
+
+/* File event structure */
+typedef struct Jim_FileEvent {
+    void *handle;
+    int mask; /* one of JIM_EVENT_(READABLE | WRITABLE | EXCEPTION) */
+    Jim_FileProc *fileProc;
+    Jim_EventFinalizerProc *finalizerProc;
+    void *clientData;
+    struct Jim_FileEvent *next;
+} Jim_FileEvent;
+
+/* Time event structure */
+typedef struct Jim_TimeEvent {
+    jim_wide id; /* time event identifier. */
+    int mode;  /* restart, repetitive .. UK */
+    long initialms; /* initial relativ timer value UK */
+    long when_sec; /* seconds */
+    long when_ms; /* milliseconds */
+    Jim_TimeProc *timeProc;
+    Jim_EventFinalizerProc *finalizerProc;
+    void *clientData;
+    struct Jim_TimeEvent *next;
+} Jim_TimeEvent;
+
+/* Per-interp stucture containing the state of the event loop */
+typedef struct Jim_EventLoop {
+    jim_wide timeEventNextId;
+    Jim_FileEvent *fileEventHead;
+    Jim_TimeEvent *timeEventHead;
+} Jim_EventLoop;
+
+static void Jim_CreateFileHandler(Jim_Interp *interp, void *handle, int mask,
+        Jim_FileProc *proc, void *clientData,
+        Jim_EventFinalizerProc *finalizerProc)
+{
+    Jim_FileEvent *fe;
+    Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
+
+       // fprintf(stderr,"rein\n");
+    fe = Jim_Alloc(sizeof(*fe));
+    fe->handle = handle;
+    fe->mask = mask;
+    fe->fileProc = proc;
+    fe->finalizerProc = finalizerProc;
+    fe->clientData = clientData;
+    fe->next = eventLoop->fileEventHead;
+    eventLoop->fileEventHead = fe;
+       // fprintf(stderr,"raus\n");
+}
+
+static void Jim_DeleteFileHandler(Jim_Interp *interp, void *handle)
+{
+    Jim_FileEvent *fe, *prev = NULL;
+    Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
+
+    fe = eventLoop->fileEventHead;
+    while (fe) {
+        if (fe->handle == handle) {
+            if (prev == NULL)
+                eventLoop->fileEventHead = fe->next;
+            else
+                prev->next = fe->next;
+            if (fe->finalizerProc)
+                fe->finalizerProc(interp, fe->clientData);
+            Jim_Free(fe);
+            return;
+        }
+        prev = fe;
+        fe = fe->next;
+    }
+}
+
+/* That's another part of this extension that needs to be ported
+ * to WIN32. */
+static void JimGetTime(long *seconds, long *milliseconds)
+{
+    struct timeval tv;
+
+    gettimeofday(&tv, NULL);
+    *seconds = tv.tv_sec;
+    *milliseconds = tv.tv_usec/1000;
+}
+
+static jim_wide Jim_CreateTimeHandler(Jim_Interp *interp, jim_wide milliseconds,
+        Jim_TimeProc *proc, void *clientData,
+        Jim_EventFinalizerProc *finalizerProc)
+{
+    Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
+    jim_wide id = eventLoop->timeEventNextId++;
+    Jim_TimeEvent *te;
+    long cur_sec, cur_ms;
+
+    JimGetTime(&cur_sec, &cur_ms);
+
+    te = Jim_Alloc(sizeof(*te));
+    te->id = id;
+    te->mode = 0;
+    te->initialms = milliseconds;
+    te->when_sec = cur_sec + milliseconds/1000;
+    te->when_ms = cur_ms + milliseconds%1000;
+    if (te->when_ms >= 1000) {
+        te->when_sec ++;
+        te->when_ms -= 1000;
+    }
+    te->timeProc = proc;
+    te->finalizerProc = finalizerProc;
+    te->clientData = clientData;
+    te->next = eventLoop->timeEventHead;
+    eventLoop->timeEventHead = te;
+    return id;
+}
+
+static jim_wide Jim_DeleteTimeHandler(Jim_Interp *interp, jim_wide id)
+{
+    Jim_TimeEvent *te, *prev = NULL;
+    Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
+    long cur_sec, cur_ms;
+    jim_wide remain ;
+
+    JimGetTime(&cur_sec, &cur_ms);
+
+    te = eventLoop->timeEventHead;
+    if (id >= eventLoop->timeEventNextId)
+       return -2; /* wrong event ID */
+    while (te) {
+        if (te->id == id) {
+            remain  = (te->when_sec - cur_sec) * 1000;
+            remain += (te->when_ms  - cur_ms) ;
+           remain = (remain < 0) ? 0 : remain ;
+
+            if (prev == NULL)
+                eventLoop->timeEventHead = te->next;
+            else
+                prev->next = te->next;
+            if (te->finalizerProc)
+                te->finalizerProc(interp, te->clientData);
+            Jim_Free(te);
+            return remain;
+        }
+        prev = te;
+        te = te->next;
+    }
+    return -1; /* NO event with the specified ID found */
+}
+
+/* Search the first timer to fire.
+ * This operation is useful to know how many time the select can be
+ * put in sleep without to delay any event.
+ * If there are no timers NULL is returned. */
+static Jim_TimeEvent *JimSearchNearestTimer(Jim_EventLoop *eventLoop)
+{
+    Jim_TimeEvent *te = eventLoop->timeEventHead;
+    Jim_TimeEvent *nearest = NULL;
+
+    while (te) {
+        if (!nearest || te->when_sec < nearest->when_sec ||
+                (te->when_sec == nearest->when_sec &&
+                 te->when_ms < nearest->when_ms))
+            nearest = te;
+        te = te->next;
+    }
+    return nearest;
+}
+
+/* --- POSIX version of Jim_ProcessEvents, for now the only available --- */
+#define JIM_FILE_EVENTS 1
+#define JIM_TIME_EVENTS 2
+#define JIM_ALL_EVENTS (JIM_FILE_EVENTS | JIM_TIME_EVENTS)
+#define JIM_DONT_WAIT 4
+
+/* Process every pending time event, then every pending file event
+ * (that may be registered by time event callbacks just processed).
+ * Without special flags the function sleeps until some file event
+ * fires, or when the next time event occurrs (if any).
+ *
+ * If flags is 0, the function does nothing and returns.
+ * if flags has JIM_ALL_EVENTS set, all the kind of events are processed.
+ * if flags has JIM_FILE_EVENTS set, file events are processed.
+ * if flags has JIM_TIME_EVENTS set, time events are processed.
+ * if flags has JIM_DONT_WAIT set the function returns ASAP until all
+ * the events that's possible to process without to wait are processed.
+ *
+ * The function returns the number of events processed. */
+int Jim_ProcessEvents(Jim_Interp *interp, int flags)
+{
+    int maxfd = 0, numfd = 0, processed = 0;
+    fd_set rfds, wfds, efds;
+    Jim_EventLoop *eventLoop = Jim_GetAssocData(interp, "eventloop");
+    Jim_FileEvent *fe = eventLoop->fileEventHead;
+    Jim_TimeEvent *te;
+    jim_wide maxId;
+    JIM_NOTUSED(flags);
+
+    FD_ZERO(&rfds);
+    FD_ZERO(&wfds);
+    FD_ZERO(&efds);
+
+    /* Check file events */
+    while (fe != NULL) {
+        int fd = fileno((FILE*)fe->handle);
+
+        if (fe->mask & JIM_EVENT_READABLE)
+               FD_SET(fd, &rfds);
+        if (fe->mask & JIM_EVENT_WRITABLE) FD_SET(fd, &wfds);
+        if (fe->mask & JIM_EVENT_EXCEPTION) FD_SET(fd, &efds);
+        if (maxfd < fd) maxfd = fd;
+        numfd++;
+        fe = fe->next;
+    }
+    /* Note that we want call select() even if there are no
+     * file events to process as long as we want to process time
+     * events, in order to sleep until the next time event is ready
+     * to fire. */
+    if (numfd || ((flags & JIM_TIME_EVENTS) && !(flags & JIM_DONT_WAIT))) {
+        int retval;
+        Jim_TimeEvent *shortest;
+        struct timeval tv, *tvp;
+       jim_wide dt;
+
+        shortest = JimSearchNearestTimer(eventLoop);
+        if (shortest) {
+            long now_sec, now_ms;
+
+            /* Calculate the time missing for the nearest
+             * timer to fire. */
+            JimGetTime(&now_sec, &now_ms);
+            tvp = &tv;
+           dt   = 1000 * (shortest->when_sec - now_sec);
+           dt  += (shortest->when_ms  - now_ms);
+            if (dt < 0) {
+               dt = 1;
+           }
+           tvp->tv_sec  = dt / 1000;
+           tvp->tv_usec = dt % 1000;
+           // fprintf(stderr,"Next %d.% 8d\n",(int)tvp->tv_sec,(int)tvp->tv_usec);
+        } else {
+            tvp = NULL; /* wait forever */
+               // fprintf(stderr,"No Event\n");
+        }
+
+        retval = select(maxfd + 1, &rfds, &wfds, &efds, tvp);
+        if (retval < 0) {
+          switch (errno) {
+              case EINTR:   fprintf(stderr,"select EINTR\n"); break;
+              case EINVAL:  fprintf(stderr,"select EINVAL\n"); break;
+              case ENOMEM:  fprintf(stderr,"select ENOMEM\n"); break;
+          }
+       } else if (retval > 0) {
+            fe = eventLoop->fileEventHead;
+            while (fe != NULL) {
+                int fd = fileno((FILE*)fe->handle);
+
+               // fprintf(stderr,"fd: %d mask: %02x \n",fd,fe->mask);
+
+                if ((fe->mask & JIM_EVENT_READABLE && FD_ISSET(fd, &rfds)) ||
+                    (fe->mask & JIM_EVENT_WRITABLE && FD_ISSET(fd, &wfds)) ||
+                    (fe->mask & JIM_EVENT_EXCEPTION && FD_ISSET(fd, &efds)))
+                {
+                    int mask = 0;
+
+                    if (fe->mask & JIM_EVENT_READABLE && FD_ISSET(fd, &rfds)) {
+                        mask |= JIM_EVENT_READABLE;
+                       if ((fe->mask & JIM_EVENT_FEOF) && feof((FILE *)fe->handle))
+                               mask |= JIM_EVENT_FEOF;
+                   }
+                    if (fe->mask & JIM_EVENT_WRITABLE && FD_ISSET(fd, &wfds))
+                        mask |= JIM_EVENT_WRITABLE;
+                    if (fe->mask & JIM_EVENT_EXCEPTION && FD_ISSET(fd, &efds))
+                        mask |= JIM_EVENT_EXCEPTION;
+                    if (fe->fileProc(interp, fe->clientData, mask) == JIM_ERR) {
+                        /* Remove the element on handler error */
+                        Jim_DeleteFileHandler(interp, fe->handle);
+                    }
+                    processed++;
+                    /* After an event is processed our file event list
+                     * may no longer be the same, so what we do
+                     * is to clear the bit for this file descriptor and
+                     * restart again from the head. */
+                    fe = eventLoop->fileEventHead;
+                    FD_CLR(fd, &rfds);
+                    FD_CLR(fd, &wfds);
+                    FD_CLR(fd, &efds);
+                } else {
+                    fe = fe->next;
+                }
+            }
+        }
+    }
+    /* Check time events */
+    te = eventLoop->timeEventHead;
+    maxId = eventLoop->timeEventNextId-1;
+    while (te) {
+        long now_sec, now_ms;
+        jim_wide id;
+
+        if (te->id > maxId) {
+            te = te->next;
+            continue;
+        }
+        JimGetTime(&now_sec, &now_ms);
+        if (now_sec > te->when_sec ||
+            (now_sec == te->when_sec && now_ms >= te->when_ms))
+        {
+            id = te->id;
+            te->timeProc(interp, te->clientData);
+            /* After an event is processed our time event list may
+             * no longer be the same, so we restart from head.
+             * Still we make sure to don't process events registered
+             * by event handlers itself in order to don't loop forever
+             * even in case an [after 0] that continuously register
+             * itself. To do so we saved the max ID we want to handle. */
+            Jim_DeleteTimeHandler(interp, id);
+            te = eventLoop->timeEventHead;
+        } else {
+            te = te->next;
+        }
+    }
+
+    return processed;
+}
+/* ---------------------------------------------------------------------- */
+
+static void JimELAssocDataDeleProc(Jim_Interp *interp, void *data)
+{
+    void *next;
+    Jim_FileEvent *fe;
+    Jim_TimeEvent *te;
+    Jim_EventLoop *eventLoop = data;
+
+    fe = eventLoop->fileEventHead;
+    while (fe) {
+        next = fe->next;
+        if (fe->finalizerProc)
+            fe->finalizerProc(interp, fe->clientData);
+        Jim_Free(fe);
+        fe = next;
+    }
+
+    te = eventLoop->timeEventHead;
+    while (te) {
+        next = te->next;
+        if (te->finalizerProc)
+            te->finalizerProc(interp, te->clientData);
+        Jim_Free(te);
+        te = next;
+    }
+    Jim_Free(data);
+}
+
+static int JimELVwaitCommand(Jim_Interp *interp, int argc,
+        Jim_Obj *const *argv)
+{
+    Jim_Obj *oldValue;
+
+    if (argc != 2) {
+        Jim_WrongNumArgs(interp, 1, argv, "name");
+        return JIM_ERR;
+    }
+    oldValue = Jim_GetGlobalVariable(interp, argv[1], JIM_NONE);
+    if (oldValue) Jim_IncrRefCount(oldValue);
+    while (1) {
+        Jim_Obj *currValue;
+
+        Jim_ProcessEvents(interp, JIM_ALL_EVENTS);
+        currValue = Jim_GetGlobalVariable(interp, argv[1], JIM_NONE);
+        /* Stop the loop if the vwait-ed variable changed value,
+         * or if was unset and now is set (or the contrary). */
+        if ((oldValue && !currValue) ||
+            (!oldValue && currValue) ||
+            (oldValue && currValue &&
+             !Jim_StringEqObj(oldValue, currValue, JIM_CASESENS)))
+            break;
+    }
+    if (oldValue) Jim_DecrRefCount(interp, oldValue);
+    return JIM_OK;
+}
+
+static void JimAfterTimeHandler(Jim_Interp *interp, void *clientData)
+{
+    Jim_Obj *objPtr = clientData;
+
+    Jim_EvalObjBackground(interp, objPtr);
+}
+
+static void JimAfterTimeEventFinalizer(Jim_Interp *interp, void *clientData)
+{
+    Jim_Obj *objPtr = clientData;
+
+    Jim_DecrRefCount(interp, objPtr);
+}
+
+static int JimELAfterCommand(Jim_Interp *interp, int argc,
+        Jim_Obj *const *argv)
+{
+    jim_wide ms, id;
+    Jim_Obj *objPtr, *idObjPtr;
+    const char *options[] = {
+       "info", "cancel", "restart", "expire", NULL
+    };
+    enum {INFO, CANCEL, RESTART, EXPIRE, CREATE };
+    int option = CREATE ;
+
+    if (argc < 3) {
+        Jim_WrongNumArgs(interp, 1, argv, "<after milliseconds> script");
+        return JIM_ERR;
+    }
+    if (Jim_GetWide(interp, argv[1], &ms) != JIM_OK)
+        if (Jim_GetEnum(interp, argv[1], options, &option, "after options",
+                    JIM_ERRMSG) != JIM_OK)
+            return JIM_ERR;
+    switch (option) {
+    case CREATE:
+        Jim_IncrRefCount(argv[2]);
+        id = Jim_CreateTimeHandler(interp, ms, JimAfterTimeHandler, argv[2],
+                JimAfterTimeEventFinalizer);
+        objPtr = Jim_NewStringObj(interp, NULL, 0);
+        Jim_AppendString(interp, objPtr, "after#", -1);
+        idObjPtr = Jim_NewIntObj(interp, id);
+        Jim_IncrRefCount(idObjPtr);
+        Jim_AppendObj(interp, objPtr, idObjPtr);
+        Jim_DecrRefCount(interp, idObjPtr);
+        Jim_SetResult(interp, objPtr);
+        return JIM_OK;
+    case CANCEL:
+       {
+       int tlen ;
+       jim_wide remain = 0;
+       const char *tok = Jim_GetString(argv[2], &tlen);
+       if (sscanf(tok,"after#%" JIM_WIDE_MODIFIER, &id) == 1) {
+               remain =  Jim_DeleteTimeHandler(interp, id);
+               if (remain > -2)  {
+                       Jim_SetResult(interp, Jim_NewIntObj(interp, remain));
+                       return JIM_OK;
+               }
+       }
+        Jim_SetResultString(interp, "invalid event" , -1);
+        return JIM_ERR;
+       }
+    default:
+       fprintf(stderr,"unserviced option to after %d\n",option);
+    }
+    return JIM_OK;
+}
+
+/* This extension is not dynamically loaded, instead it's linked statically,
+   which is why we shouldn't use the unspecific 'Jim_OnLoad' name */
+int Jim_EventLoopOnLoad(Jim_Interp *interp)
+{
+    Jim_EventLoop *eventLoop;
+
+    Jim_InitExtension(interp);
+    if (Jim_PackageProvide(interp, "eventloop", "1.0", JIM_ERRMSG) != JIM_OK)
+        return JIM_ERR;
+
+    eventLoop = Jim_Alloc(sizeof(*eventLoop));
+    eventLoop->fileEventHead = NULL;
+    eventLoop->timeEventHead = NULL;
+    eventLoop->timeEventNextId = 1;
+    Jim_SetAssocData(interp, "eventloop", JimELAssocDataDeleProc, eventLoop);
+
+    Jim_CreateCommand(interp, "vwait", JimELVwaitCommand, NULL, NULL);
+    Jim_CreateCommand(interp, "after", JimELAfterCommand, NULL, NULL);
+
+    /* Export events API */
+    Jim_RegisterApi(interp, "Jim_CreateFileHandler", Jim_CreateFileHandler);
+    Jim_RegisterApi(interp, "Jim_DeleteFileHandler", Jim_DeleteFileHandler);
+    Jim_RegisterApi(interp, "Jim_CreateTimeHandler", Jim_CreateTimeHandler);
+    Jim_RegisterApi(interp, "Jim_DeleteTimeHandler", Jim_DeleteTimeHandler);
+    Jim_RegisterApi(interp, "Jim_ProcessEvents", Jim_ProcessEvents);
+    return JIM_OK;
+}

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)