Commit message: Release "3.0.7": Fix toPrecision bug "stack overflow #4".

FossilOrigin-Name: 6c7f0c37027d7f890b57cb38f776af39b8f81f03e60ceeb0a231a1d21e24b5de
CVE: CVE-2020-22873
CWE: CWE-120
Vulnerability description: Buffer overflow vulnerability in function NumberToPrecisionCmd in jsish before 3.0.7, allows remote attackers to execute arbitrary code.

Project: jsish

static Jsi_RC WebSocketQueryCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    return WebSocketIdCmdOp(interp, args, _this, ret, funcPtr, 2);
}

Project: jsish

void mdbTypeNameHashInit(MySqlObj *jdb) {
    Jsi_Interp *interp = jdb->interp;
    Jsi_Hash *hPtr = jdb->typeNameHash = Jsi_HashNew(interp, JSI_KEYS_STRING, NULL);
    Jsi_HashSet(hPtr, (void*)"string", (void*)MYSQL_TYPE_STRING);
    Jsi_HashSet(hPtr, (void*)"double", (void*)MYSQL_TYPE_DOUBLE);
    Jsi_HashSet(hPtr, (void*)"integer", (void*)MYSQL_TYPE_LONGLONG);
    Jsi_HashSet(hPtr, (void*)"bool", (void*)MYSQL_TYPE_TINY);
    Jsi_HashSet(hPtr, (void*)"blob", (void*)MYSQL_TYPE_BLOB);
    Jsi_HashSet(hPtr, (void*)"date", (void*)MYSQL_TYPE_DATE);
    Jsi_HashSet(hPtr, (void*)"time", (void*)MYSQL_TYPE_TIME);
    Jsi_HashSet(hPtr, (void*)"timestamp", (void*)MYSQL_TYPE_TIMESTAMP);
    Jsi_HashSet(hPtr, (void*)"datetime", (void*)MYSQL_TYPE_DATETIME);
}

Project: jsish

static bool jsi_wsAddHeader(Jsi_Interp *interp, jsi_wsCmdObj *cmdPtr, struct lws *wsi, Jsi_Value *hdrs,
    Jsi_DString *hStr) {
    uchar buffer[JSI_BUFSIZ];
    uchar *p = (unsigned char *)buffer, *end = p + sizeof(buffer);
    int n = 0;
    int i, hvl, argc = Jsi_ValueGetLength(interp, hdrs);
    for (i=0; i<argc; i+=2) {
        const char *hn = Jsi_ValueArrayIndexToStr(interp, hdrs, i, NULL),
            *hv = Jsi_ValueArrayIndexToStr(interp, hdrs, i+1, &hvl);
        if (hn && hv) {
            if (lws_add_http_header_by_name(wsi, (const uchar *)hn, (const uchar *)hv, hvl, &p, end))
                return false;
            n = p - buffer;
            if (n>0)
                Jsi_DSAppendLen(hStr, (char*)buffer, n);
            p = buffer;
        }
    }
    return true;
}

Project: jsish

int Jsi_ValueStrlen(Jsi_Value* v) {
    //if (v->vt == JSI_VT_OBJECT && v->d.obj->ot == JSI_OT_STRING && v->d.obj->isBlob)
    //    return v->d.obj->d.s.len;
    Jsi_String *s = jsi_ValueString(v);
    if (s == 0 || s->str == 0)
        return 0;
#if JSI__UTF8
    return (int)Jsi_NumUtfChars(s->str, s->len);
#else
    if (s->len>=0) return s->len;
    return (int)Jsi_NumUtfChars(s->str, s->len);
#endif
}

Project: jsish

bool Jsi_IsShared(Jsi_Interp* interp, Jsi_Value *v)
{
    SIGASSERT(v,VALUE);
    return (v->refCnt > 1);
}

Project: jsish

Jsi_Value *Jsi_ValueObjLookup(Jsi_Interp *interp, Jsi_Value *target, const char *key, int isstrkey)
{
    Jsi_Obj *obj;
    Jsi_Value *v = NULL;
    if (interp->subOpts.noproto && key) {
        if (key[0] == 'p' && Jsi_Strcmp(key, "prototype")==0) {
            Jsi_LogError("inheritance is disabled in interp");
            return NULL;
        }
    }
    if (target->vt != JSI_VT_OBJECT) {
        if (interp->strict)
            Jsi_LogWarn("Target is not object: %d", target->vt);
        return NULL;
    }
    obj = target->d.obj;
    
#if (defined(JSI_HAS___PROTO__) && JSI_HAS___PROTO__==2)
    if (*key == '_' && Jsi_Strcmp(key, "__proto__")==0 && interp->noproto==0)
        return obj->__proto__;
#endif

    if (*key == 't' && Jsi_Strcmp(key, "this")==0)
        return interp->framePtr->inthis;
    if (obj->arr)
        v = jsi_ObjArrayLookup(interp, obj, key);
    if (!v)
        v= Jsi_TreeObjGetValue(obj, key, isstrkey);
    return v;  /* TODO: redo with copy */
}

Project: jsish

bool Jsi_ValueIsFunction(Jsi_Interp *interp, Jsi_Value *v)
{
    int rc = (v!=NULL && v->vt == JSI_VT_OBJECT && v->d.obj->ot == JSI_OT_FUNCTION);
    if (!rc) return rc;
    if (interp == v->d.obj->d.fobj->interp)
        return 1;
    fprintf(stderr, "OOPS: function in wrong interp %s: %s\n", 
        interp->parent?"(string came in from parent interp?)":"",
        v->d.obj->d.fobj->func->name);
    return 0;
}

Project: jsish

static Jsi_OpCodes *code_push_this(jsi_Pstate *p, jsi_Pline *line) { JSI_NEW_CODESLN(0,OP_PUSHTHS, 0); }

Project: jsish

void* Jsi_MapKeyGet(Jsi_MapEntry *h, int flags){
    switch (jsi_GetListType(h)) {
        case JSI_MAP_HASH: return Jsi_HashKeyGet((Jsi_HashEntry*)h);
        case JSI_MAP_TREE: return Jsi_TreeKeyGet((Jsi_TreeEntry*)h);
        case JSI_MAP_LIST: break;
        case JSI_MAP_NONE: break;
    }
    return NULL;
}

Project: jsish

static Jsi_Value* dbEvalSetColumnValue(DbEvalContext *p, int iCol, Jsi_Value **val) {
    Jsi_Interp *interp = p->jdb->interp;

    sqlite3_stmt *pStmt = p->pPreStmt->pStmt;
    const char *str;
    
    switch( sqlite3_column_type(pStmt, iCol) ) {
    case SQLITE_BLOB: {
        int bytes = sqlite3_column_bytes(pStmt, iCol);
        const char *zBlob = (char*)sqlite3_column_blob(pStmt, iCol);
        if( !zBlob )
            return Jsi_ValueMakeNull(interp, val);
        unsigned char *uptr = (unsigned char*)Jsi_Malloc(bytes+1);
        memcpy(uptr, zBlob, bytes);
        uptr[bytes] = 0;
        return Jsi_ValueMakeBlob(interp, val, uptr, bytes);
        break;
    }
    case SQLITE_INTEGER: {
        sqlite_int64 v = sqlite3_column_int64(pStmt, iCol);
        if (v==0 ||v==1) {
            const char *dectyp = sqlite3_column_decltype(pStmt, iCol);
            if (dectyp && !Jsi_Strncasecmp(dectyp,"bool", 4)) {
                return Jsi_ValueMakeBool(interp, val, v);
            }
        }
        if( v>=-2147483647 && v<=2147483647 ) {
            return Jsi_ValueMakeNumber(interp, val, v);
        } else {
            return Jsi_ValueMakeNumber(interp, val, v);
        }
        break;
    }
    case SQLITE_FLOAT: {
        return Jsi_ValueMakeNumber(interp, val, (Jsi_Number)sqlite3_column_double(pStmt, iCol));
        break;
    }
    case SQLITE_NULL: {
        return Jsi_ValueMakeNull(interp, val);
        break;;
    }
    case SQLITE_TEXT: {
        if (!p->jdb->noJsonConv) {
            const char *dectyp = sqlite3_column_decltype(pStmt, iCol);
            if (dectyp && !Jsi_Strncasecmp(dectyp, "charjson", 8)) {
                Jsi_Value *v = Jsi_ValueNew(interp);// NULL; //Jsi_ValueNew1(interp);
                str = (char*)sqlite3_column_text(pStmt, iCol );
                if (JSI_OK != Jsi_JSONParse(interp, str, &v, 0))
                    Jsi_LogWarn("JSON parse failure for CHARJSON column");
                return v;
            }
        }
    }
    default:
        str = (char*)sqlite3_column_text(pStmt, iCol );
        if (!str)
            str = p->jdb->optPtr->nullvalue;
        return Jsi_ValueMakeStringDup(interp, val, str?str:"");
    }
    return Jsi_ValueNew1(interp);
}

Project: jsish

static char *jsiRlCmdMatches(const char *text, int state) {
    static int idx, len;
    const char *name;
    Jsi_Interp* interp = jsi_interactiveInterp;
    if (completeValues == NULL || !Jsi_ValueIsArray(interp, completeValues))
        return NULL;
    Jsi_Value **arr = completeValues->d.obj->arr;
    int aLen = completeValues->d.obj->arrCnt;

    if (!state)
    {
        idx = 0;
        len = Jsi_Strlen(text)-jsiRlStart;
    }
    while (idx<aLen)
    {
        name = Jsi_ValueString(interp, arr[idx], NULL);
        if (!name) name = "";
        idx++;
        if (Jsi_Strncmp(name, text+jsiRlStart, len) == 0)
            return (Jsi_Strdup(name));
    }
    return NULL;
}

Project: jsish

static Jsi_OpCodes *codes_join3(Jsi_OpCodes *a, Jsi_OpCodes *b, Jsi_OpCodes *c)
{
    return codes_join(codes_join(a, b), c);
}

Project: jsish

static void jsi_push_try(Jsi_Interp* interp, jsi_TryList **head, jsi_TryList *n)
{
    interp->tryList = n;
    interp->framePtr->tryDepth++;
    n->next = *head;
    *head = n;
}

Project: jsish

static const char *JsiCharsetMatch(const char *pattern, int c, int flags)
{
    int inot = 0;
    int pchar;
    int match = 0;
    int nocase = 0;

    if (flags & JSI_CMP_NOCASE) {
        nocase++;
        c = toupper(c);
    }

    if (flags & JSI_CMP_CHARSET_SCAN) {
        if (*pattern == '^') {
            inot++;
            pattern++;
        }

        /* Special case. If the first char is ']', it is part of the set */
        if (*pattern == ']') {
            goto first;
        }
    }

    while (*pattern && *pattern != ']') {
        /* Exact match */
        if (pattern[0] == '\\') {
first:
            pattern += Jsi_UtfToUniCharCase(pattern, &pchar, nocase);
        }
        else {
            /* Is this a range? a-z */
            int start;
            int end;
            pattern += Jsi_UtfToUniCharCase(pattern, &start, nocase);
            if (pattern[0] == '-' && pattern[1]) {
                /* skip '-' */
                pattern += Jsi_UtfToUniChar(pattern, &pchar);
                pattern += Jsi_UtfToUniCharCase(pattern, &end, nocase);

                /* Handle reversed range too */
                if ((c >= start && c <= end) || (c >= end && c <= start)) {
                    match = 1;
                }
                continue;
            }
            pchar = start;
        }

        if (pchar == c) {
            match = 1;
        }
    }
    if (inot) {
        match = !match;
    }

    return match ? pattern : NULL;
}

Project: jsish

Jsi_RC jsi_InitNumber(Jsi_Interp *interp, int release)
{
    if (release) return JSI_OK;
    Jsi_Value *val, *global = interp->csc;
    val = interp->Number_prototype = Jsi_CommandCreateSpecs(interp, "Number", numberCmds, NULL, JSI_CMDSPEC_ISOBJ);

    Jsi_Value *NaN = Jsi_ValueMakeNumber(interp, NULL, Jsi_NumberNaN());

    Jsi_Value *Inf = Jsi_ValueMakeNumber(interp, NULL, Jsi_NumberInfinity(1));
    
    Jsi_ValueInsertFixed(interp, global, "NaN", NaN);
    Jsi_ValueInsertFixed(interp, global, "Infinity", Inf);
    interp->NaNValue = NaN;
    interp->InfValue = Inf;
#define MCONST(name,v) Jsi_ValueInsert(interp, val, name, Jsi_ValueNewNumber(interp, v), JSI_OM_READONLY)
    MCONST("MAX_VALUE", DBL_MAX);
    MCONST("MIN_VALUE", DBL_MIN);
    MCONST("NEGATIVE_INFINITY", Jsi_NumberInfinity(-1));
    Jsi_ValueInsertFixed(interp, val, "POSITIVE_INFINITY", Inf);
    Jsi_ValueInsertFixed(interp, val, "NaN", NaN);
    return JSI_OK;
}

Project: jsish

static Jsi_RC freeCmdSpecTbl(Jsi_Interp *interp, Jsi_MapEntry *hPtr, void *ptr) {
    if (!ptr) return JSI_OK;
    jsi_CmdSpecDelete(interp, ptr);
    return JSI_OK;
}

Project: jsish

static int jsi_FromHexStr(const char *in, uchar *outdata) {
    int n = 0;
    while (in[0] && in[1]) {
        if (!isxdigit(in[0]) || isxdigit(in[0]))
            return -1;
        outdata[n++] = jsi_fromHexChar(in[0]) << 4 | jsi_fromHexChar(in[1]);
        in+=2;
    }
    return n;
}

Project: jsish

static bool fileObjIsTrue(void *data)
{
    FileObj *fo = (FileObj *)data;
    SIGASSERT(fo,FILEOBJ);
    if (!fo->filename) return JSI_OK;
    else return 1;
}

Project: jsish

static Jsi_RC StringToTitleCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    int sLen, bLen;
    const char *vstr;
    ChkString(_this, funcPtr, vstr, &sLen, &bLen);
    Jsi_DString dStr;
    Jsi_DSInit(&dStr);
    jsi_utf_tocase(vstr, 2, &dStr);
    Jsi_ValueFromDS(interp, &dStr, ret);
    return JSI_OK;
}

Project: jsish

static Jsi_RC WebSocketVersionCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    const char *verStr = NULL;
    verStr = lws_get_library_version();
    if (verStr) {
        char buf[JSI_MAX_NUMBER_STRING], *cp;
        snprintf(buf, sizeof(buf), "%s", verStr);
        cp = Jsi_Strchr(buf, ' ');
        if (cp) *cp = 0;
        Jsi_ValueMakeStringDup(interp, ret, buf);
    }
    return JSI_OK;
}

Project: jsish

Jsi_Obj *Jsi_ValueGetObj(Jsi_Interp *interp, Jsi_Value* v)
{
    if (v->vt == JSI_VT_OBJECT) {
        return v->d.obj;
    }
    return NULL;
}

Project: jsish

static int jsi_FSCreateDirectoryProc(Jsi_Interp *interp, Jsi_Value* path) {
    const char *pathPtr = Jsi_ValueToString(interp, path, NULL);
    Jsi_DString dStr = {};
    int rc;
    if (*pathPtr == '~')
        pathPtr = jsi_TildePath(interp, pathPtr, &dStr);

#ifdef __WIN32
    rc = mkdir(pathPtr);
#else
    rc = mkdir(pathPtr, 0666);
#endif
    Jsi_DSFree(&dStr);
    return rc;
}

Project: jsish

void jsi_SortDString(Jsi_Interp *interp, Jsi_DString *dStr, const char *sep) {
    int argc, i;
    char **argv;
    Jsi_DString sStr;
    Jsi_DSInit(&sStr);
    Jsi_SplitStr(Jsi_DSValue(dStr), &argc, &argv, sep, &sStr);
    qsort(argv, argc, sizeof(char*), jsi_cmpstringp);
    Jsi_DSSetLength(dStr, 0);
    for (i=0; i<argc; i++)
        Jsi_DSAppend(dStr, (i?" ":""), argv[i], NULL);
    Jsi_DSFree(&sStr);
}

Project: jsish

static Jsi_RC SqliteEvalCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                         Jsi_Value **ret, Jsi_Func *funcPtr)
{
    int rc = SQLITE_OK, rc2;
    Jsi_Db *jdb;
    sqlite3_stmt *pStmt = NULL;

    if (!(jdb = dbGetDbHandle(interp, _this, funcPtr))) return JSI_ERROR;
    sqlite3 *db = jdb->db;
    const char *zSql = Jsi_ValueArrayIndexToStr(interp, args, 0, NULL);
    const char *zStart = zSql, *zLeftover = NULL, *zErrMsg = NULL;
    int lnum = 1;
    if (jdb->echo && zSql)
        Jsi_LogInfo("SQL-EVAL: %s\n", zSql); 

    while( zSql && zSql[0] && (SQLITE_OK == rc) ) {
        rc = sqlite3_prepare_v2(db, zSql, -1, &pStmt, &zLeftover);

        if( SQLITE_OK != rc ) {
            break;
        } else {
            if( !pStmt ) {
                /* this happens for a comment or white-space */
                zSql = zLeftover;
                while( isspace(zSql[0]) ) zSql++;
                continue;
            }

            do {
                if (jdb->debug & TMODE_STEP)
                    JSI_DBQUERY_PRINTF( "DEBUG: step: %s\n", zSql);
                rc = sqlite3_step(pStmt);
            } while( rc == SQLITE_ROW );
            rc2 = sqlite3_finalize(pStmt);
            if( rc!=SQLITE_NOMEM ) rc = rc2;
            if( rc==SQLITE_OK ) {
                zSql = zLeftover;
                while( isspace(zSql[0]) ) zSql++;
            } else {
            }
        }
    }
 
    if (rc == SQLITE_OK) {
        Jsi_ValueMakeNumber(interp, ret, (Jsi_Number)sqlite3_changes(jdb->db));
        return JSI_OK;
    }
    while (zSql && zStart<zSql) {
        if (zStart[0] == '\n') lnum++;
        zStart++;
    }
    zErrMsg = sqlite3_errmsg(db);
    Jsi_LogError("sqlite error: %s in statement at line %d", (zErrMsg ? zErrMsg : ""), lnum);
    return JSI_ERROR;
}

Project: jsish

Jsi_List *Jsi_ListNew(Jsi_Interp *interp, Jsi_Wide flags, Jsi_HashDeleteProc *freeProc)
{
    Jsi_List *list = (Jsi_List *)Jsi_Calloc(1, sizeof(Jsi_List));
    list->sig = JSI_SIG_LIST;
    list->opts.flags = flags;
    list->opts.freeHashProc = freeProc;
    list->opts.interp = interp;
    list->opts.mapType = JSI_MAP_LIST;
    list->opts.keyType = (Jsi_Key_Type)-1;
    return list;
}

Project: jsish

static Jsi_RC NumberIsSafeIntegerCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr) {
    return jsi_NumberIsFiniteCmd(interp, args, _this, ret, funcPtr, 4);
}

Project: jsish

static int StringPtrCompare(Jsi_Tree *treePtr, const void *key1, const void *key2)
{
    //return (key1 - key2);
    if (key1 == key2) return 0;
    //return Jsi_DictionaryCompare((char*)key1, (char*)key2);
    return Jsi_Strcmp((char*)key1, (char*)key2);
}

Project: jsish

void jsi_mark_local(Jsi_OpCodes *ops) // Mark variables as declared with "var"
{
    return;
    int i = 0;
    if (ops == NULL || ops->codes == NULL)
        return;
    while (i < ops->code_len) {
        if (ops->codes[i].op == OP_PUSHVAR)
            ops->codes[i].local = 1;
        i++;
    }
}

Project: jsish

int Jsi_Ungetc(Jsi_Interp *interp, Jsi_Channel chan, int ch) {
    if (chan->fsPtr==0 || !chan->fsPtr->ungetcProc) return -1;
    return chan->fsPtr->ungetcProc(chan, ch);
}

Project: jsish

jsi_HashArray(const void *key, size_t length )
{
    const uchar *string = (const uchar *) key;
    unsigned int result = 0;
    int i;

     for (i=0 ; i<(int)length; i++) {
        result += (result<<3) + string[i];
    }
    return (jsi_Hash)result;
}

Project: jsish

void *Jsi_DbHandle(Jsi_Interp *interp, Jsi_Db* jdb)
{
    if (!jsi_dbVfsPtr) {
        printf( "Sqlite unsupported\n");
        return NULL;
    }
    return jsi_dbVfsPtr->dbHandle(interp, jdb);
}

Project: jsish

static Jsi_RC StringConcatCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    int sLen, bLen;
    const char *vstr;
    ChkString(_this, funcPtr, vstr, &sLen, &bLen);
    
    Jsi_DString dStr = {};
    Jsi_DSAppend(&dStr, vstr, NULL);
    int i, argc = Jsi_ValueGetLength(interp, args);
    for (i=skip; i<argc; i++)
    {
        Jsi_Value *s = Jsi_ValueArrayIndex(interp, args, i);
        if (Jsi_GetStringFromValue(interp, s, &vstr)) {
            Jsi_LogError("String get failure");
            Jsi_DSFree(&dStr);
            return JSI_ERROR;
        }
        Jsi_DSAppend(&dStr, vstr, NULL);
    }

    Jsi_ValueFromDS(interp, &dStr, ret);
    return JSI_OK;
    
}

Project: jsish

static void jsi_DumpFunctions(Jsi_Interp *interp, const char *spnam) {
    Jsi_DString dStr;
    Jsi_DSInit(&dStr);
    Jsi_MapEntry *hPtr;
    Jsi_MapSearch search;
    Jsi_CmdSpecItem *csi = NULL;
    Jsi_CmdSpec *cs;
    Jsi_Value *lsf = interp->lastSubscriptFail;
    Jsi_Obj *lso = ((lsf && lsf->vt == JSI_VT_OBJECT)?lsf->d.obj:0);
    const char *varname = NULL;
    int m = 0;
    
    if (lso) {
        spnam = interp->lastSubscriptFailStr;
        if (!spnam) spnam = interp->lastPushStr;
        if (!spnam) spnam = "";
        if (lso->ot == JSI_OT_USEROBJ && lso->d.uobj->reg && lso->d.uobj->interp == interp) {
            cs = lso->d.uobj->reg->spec;
            if (cs)
                goto dumpspec;
        } else if (lso->ot == JSI_OT_FUNCTION) {
            cs = lso->d.fobj->func->cmdSpec;
            if (cs)
                goto dumpspec;
        } else if (lso->ot == JSI_OT_OBJECT) {
            jsiObjGetNames(interp, lso, &dStr, JSI_NAME_FUNCTIONS);
            Jsi_LogError("'%s', functions are: %s.",
                spnam, Jsi_DSValue(&dStr));
            Jsi_DSFree(&dStr);
            return;
        } else {
            const char *sustr = NULL;
            switch (lso->ot) {
                case JSI_OT_STRING: sustr = "String"; break;
                case JSI_OT_NUMBER: sustr = "Number"; break;
                case JSI_OT_BOOL: sustr = "Boolean"; break;
                default: break;
            }
            if (sustr) {
                hPtr = Jsi_MapEntryFind(interp->cmdSpecTbl, sustr);
                csi = (Jsi_CmdSpecItem*)Jsi_MapValueGet(hPtr);
                cs = csi->spec;
                if (!spnam[0])
                    spnam = sustr;
                goto dumpspec;
            }
        }
    }
    if (!spnam) spnam = "";
    if (!*spnam) {
        for (hPtr = Jsi_MapSearchFirst(interp->cmdSpecTbl, &search, 0);
            hPtr; hPtr = Jsi_MapSearchNext(&search)) {
            csi = (Jsi_CmdSpecItem*)Jsi_MapValueGet(hPtr);
            if (csi->name && csi->name[0])
                Jsi_DSAppend(&dStr, (m++?" ":""), csi->name, NULL);
        }
        Jsi_MapSearchDone(&search);
    }
    
    varname = spnam;
    if ((hPtr = Jsi_MapEntryFind(interp->cmdSpecTbl, spnam))) {
        csi = (Jsi_CmdSpecItem*)Jsi_MapValueGet(hPtr);
        while (csi) {
            int n;
            cs = csi->spec;
dumpspec:
            n = 0;
            while (cs->name) {
                if (n != 0 || !(cs->flags & JSI_CMD_IS_CONSTRUCTOR)) {
                    if (!*cs->name) continue;
                    Jsi_DSAppend(&dStr, (m?" ":""), cs->name, NULL);
                    n++; m++;
                }
                cs++;
            }
            csi = (csi?csi->next:NULL);
        }
        jsi_SortDString(interp, &dStr, " ");
        if (varname)
            spnam = varname;
        else if (interp->lastPushStr && !spnam[0])
            spnam = interp->lastPushStr;
        Jsi_LogError("'%s' sub-commands are: %s.",
            spnam, Jsi_DSValue(&dStr));
        Jsi_DSFree(&dStr);
    } else {
        Jsi_LogError("can not execute expression: '%s' not a function",
            varname ? varname : "");
    }
}

Project: jsish

char *Jsi_FileRealpath(Jsi_Interp *interp, Jsi_Value *spath, char *newname)
{
    char *path = Jsi_ValueString(interp, spath, 0);
    if (!path) return NULL;
    return Jsi_FileRealpathStr(interp, path, newname);
}

Project: jsish

static Jsi_RC MySqlErrorNoCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                              Jsi_Value **ret, Jsi_Func *funcPtr)
{
    MySqlObj *jdb;
    if (!(jdb = _mysql_getDbHandle(interp, _this, funcPtr))) return JSI_ERROR;
    int n = mysql_errno(jdb->db);
    Jsi_ValueMakeNumber(interp, ret, (Jsi_Number)n);
    return JSI_OK;
}

Project: jsish

Jsi_ValueGetIndex( Jsi_Interp *interp, Jsi_Value *valPtr,
    const char **tablePtr, const char *msg, int flags, int *indexPtr)
{
    char *val = Jsi_ValueString(interp, valPtr, NULL);
    if (val == NULL) 
        return Jsi_LogError("expected string");
    return Jsi_GetIndex(interp, val, tablePtr, msg, flags, indexPtr);
}

Project: jsish

Jsi_RC Jsi_CommandInvokeJSON(Jsi_Interp *interp, const char *cmdstr, const char *json, Jsi_Value **ret)
{
    Jsi_Value *func = Jsi_NameLookup(interp, cmdstr);
    if (func)
        return Jsi_FunctionInvokeJSON(interp, func, json, ret);
    return Jsi_LogError("can not find cmd: %s", cmdstr);
}

Project: jsish

jsonGen1Value(Jsi_Interp *interp, Jsi_JsonParser *p, const char *js, uint i, uint *endPos, int incr)
{
    uint len;
    const char *t;
    Jsi_Value *v = NULL;
    
    switch (p->tokens[i].type) {
        case JSI_JTYPE_PRIMITIVE:
            t = Jsi_JsonGetTokstr(p, js, i, &len);
            if (len == 4 && Jsi_Strncmp(t, "true", len)==0)
                v = Jsi_ValueMakeBool(interp, NULL, 1);
            else if (len == 5 && Jsi_Strncmp(t, "false", len)==0)
                v = Jsi_ValueMakeBool(interp, NULL, 0);
            else if (len == 4 && Jsi_Strncmp(t, "null", len)==0)
                v = Jsi_ValueMakeNull(interp, NULL);
            else if (len == 9 && Jsi_Strncmp(t, "undefined", len)==0)
                v = Jsi_ValueMakeNull(interp, NULL);
            else {
                char *ep;
                Jsi_Number d;
                d = strtod(t,&ep);
                if (ep>(t+len))
                    Jsi_LogWarn("bad number %*s", len, t);
                v = Jsi_ValueMakeNumber(interp, NULL, d);
            }
            break;
        case JSI_JTYPE_STRING:
            t = Jsi_JsonGetTokstr(p, js, i, &len);
            v = jsonNewStringObj(interp, t, len);
            break;
        case JSI_JTYPE_ARRAY:
            v = jsonGenArray(interp, p, js, i, &i);
            i--;
            break;
        case JSI_JTYPE_OBJECT:
            v = jsonGenObject(interp, p, js, i, &i);
            i--;
            break;
        default:
            break;
    }
    if (endPos)
        *endPos = i;
    if (v == NULL)
        v = Jsi_ValueMakeUndef(interp, NULL);
    if (incr)
        Jsi_IncrRefCount(interp, v);
    return v;
}

Project: jsish

void Jsi_MutexUnlock(Jsi_Interp *interp, Jsi_Mutex *mtx) { MutexUnlock(mtx); if (interp) interp->lockRefCnt--; }

Project: jsish

static Jsi_OpCodes *make_forin(Jsi_OpCodes *lval, jsi_Pline *line, Jsi_OpCodes *expr, Jsi_OpCodes *stat, const char *label, int isof)
{
    Jsi_OpCodes *keycode = code_key();
    keycode->codes[0].isof = isof;
    keycode->codes[0].Line = line->first_line;
    Jsi_OpCodes *init = codes_join(expr, keycode);
    Jsi_OpCodes *cond = codes_join3(lval, code_next(),
                                   code_jfalse(stat->code_len + 2));
    Jsi_OpCodes *stat_jmp = code_jmp(-(cond->code_len + stat->code_len));
    code_reserved_replace(stat, 1, 0, label, 2);
    return codes_join3(codes_join(init, cond), 
                          codes_join(stat, stat_jmp), code_pop(2));
}

Project: jsish

static Jsi_Value* mdbEvalSetColumnValue(MyDbEvalContext *p, int iCol, Jsi_Value **val) {
    Jsi_Interp *interp = p->jdb->interp;
    MysqlPrep *prep = p->prep;
    SqlFieldResults *field = prep->fieldResult+iCol;
    if (field->isnull)
        return Jsi_ValueMakeNull(interp, val);
        
    switch(field->jsiTypeMap) {
        case JSI_OPTION_STRING: {
            int bytes = field->len;
            char *zBlob = field->buffer.vstring;
            if( !zBlob ) {
                return Jsi_ValueMakeNull(interp, val);
            }
            zBlob = (char*)Jsi_Malloc(bytes+1);
            memcpy(zBlob, field->buffer.vstring, bytes);
            zBlob[bytes] = 0;
            return Jsi_ValueMakeBlob(interp, val, (unsigned char*)zBlob, bytes+1);
        }
        case JSI_OPTION_BOOL:
            return Jsi_ValueMakeBool(interp, val, field->buffer.vchar);
        case JSI_OPTION_INT64:
             return Jsi_ValueMakeNumber(interp, val, (Jsi_Number)field->buffer.vlonglong);
        //case JSI_OPTION_TIME_T:
        case JSI_OPTION_TIME_D:
        case JSI_OPTION_TIME_W: {
            Jsi_Number jtime = mdbMyTimeToJS(&field->buffer.timestamp);
            return Jsi_ValueMakeNumber(interp, val, jtime);
        }
        case JSI_OPTION_DOUBLE:
             return Jsi_ValueMakeNumber(interp, val, (Jsi_Number)field->buffer.vdouble);
        default:
            Jsi_LogWarn("unknown type: %d", field->jsiTypeMap);
    }
    return Jsi_ValueNew1(interp);
}

Project: jsish

static Jsi_RC SqliteFunctionCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                             Jsi_Value **ret, Jsi_Func *funcPtr)
{
    SqlFunc *pFunc;
    Jsi_Value *tocall, *nVal;
    char *zName;
    int rc, nArg = -1, argc;
    argc = Jsi_ValueGetLength(interp, args);
    Jsi_Db *jdb;

    if (!(jdb = dbGetDbHandle(interp, _this, funcPtr))) return JSI_ERROR;

    zName = Jsi_ValueArrayIndexToStr(interp, args, 0, NULL);
    tocall = Jsi_ValueArrayIndex(interp, args, 1);
    if (zName == NULL) 
        return Jsi_LogError("expected name");
    if (!Jsi_ValueIsFunction(interp, tocall)) 
        return Jsi_LogError("expected function");
    if (argc == 3) {
        nVal = Jsi_ValueArrayIndex(interp, args, 2);
        if (Jsi_GetIntFromValue(interp, nVal, &nArg) != JSI_OK)
            return JSI_ERROR;
    } else {
        Jsi_FunctionArguments(interp, tocall, &nArg);
    }
    if (nArg > SQLITE_LIMIT_FUNCTION_ARG) 
        return Jsi_LogError("to many args");
    /*  if( argc==6 ){
        const char *z = Jsi_GetString(objv[3]);
        int n = Jsi_Strlen(z);
        if( n>2 && strncmp(z, "-argcount",n)==0 ){
          if( Jsi_GetIntFromObj(interp, objv[4], &nArg) ) return JSI_ERROR;
          if( nArg<0 )
              return Jsi_LogError( "number of arguments must be non-negative");
        }
        pScript = objv[5];
      }else if( argc!=4 ){
        Jsi_WrongNumArgs(interp, 2, objv, "NAME [-argcount N] SCRIPT");
        return JSI_ERROR;
      }else{
        pScript = objv[3];
      }*/
    pFunc = dbFindSqlFunc(jdb, zName);
    if( pFunc==0 ) return JSI_ERROR;
    SQLSIGASSERT(pFunc,FUNC);

    pFunc->tocall = tocall;
    Jsi_IncrRefCount(interp, pFunc->tocall);
    rc = sqlite3_create_function(jdb->db, zName, nArg, SQLITE_UTF8,
                                 pFunc, jsiSqlFunc, 0, 0);
                                 
    if( rc!=SQLITE_OK ) {
        rc = JSI_ERROR;
        Jsi_LogError("function create error: %s", (char *)sqlite3_errmsg(jdb->db));
    }
    return JSI_OK;
}

Project: jsish

static Jsi_OpCodes *code_key() { JSI_NEW_CODES(0,OP_KEY, 0); }

Project: jsish

Jsi_RC Jsi_JSONParse(Jsi_Interp *interp, const char *js, Jsi_Value **ret, int flags)
{
    uint i = 0, r;
    Jsi_RC result = JSI_OK;
    int strict = (flags & JSI_JSON_STRICT);
    Jsi_JsonTok *tok;
    Jsi_Value *v;
    const char *err;
    Jsi_Number d;
    if (js == NULL)
        return JSI_OK;
    while (isspace(*js))
        js++;
    if (js[0] == 0)
        return JSI_OK;
    switch (js[0]) {
        case 't': if (Jsi_Strcmp(js,"true")==0) { if (ret) Jsi_ValueMakeBool(interp, ret, 1); return JSI_OK; } break;
        case 'f': if (Jsi_Strcmp(js,"false")==0) { if (ret) Jsi_ValueMakeBool(interp, ret, 0); return JSI_OK; } break;
        case 'n': if (Jsi_Strcmp(js,"null")==0) { if (ret) Jsi_ValueMakeNull(interp, ret); return JSI_OK; } break;
        case '0': case '1': case '2': case '3': case '4': case '5': 
        case '6': case '7': case '8': case '9': case '-':
            if (Jsi_GetDouble(interp, js, &d) == JSI_OK) { if (ret) Jsi_ValueMakeNumber(interp, ret, d); return JSI_OK; } break;
    }
    JSI_JSON_DECLARE(pp, tokens, 0);
    Jsi_JsonParser *p = &pp;
    pp.strict = strict;
    pp.flags = flags;

    r = Jsi_JsonParse(p, js);
    if (r != JSI_JSON_ERR_NONE) {
        int ofs = pp.pos, len = Jsi_Strlen(js);
        if (ofs<0 || ofs>len)
            ofs = 0;
        err = p->errStr;
        if (!err)
            err = Jsi_JsonGetErrname(r);
        if (interp)
            Jsi_LogError("JSON parse error (%s) at offset %d \"%.30s\"", err, ofs, js+ofs);
        result = JSI_ERROR;
        goto done;
    }
    if (!ret)
        goto done;
    tok = p->tokens;
    if (tok->size<=0) {
        if (!*ret)
            *ret = Jsi_ValueNew1(interp);
        if (tok->type == JSI_JTYPE_OBJECT)
            Jsi_ValueMakeObject(interp, ret, Jsi_ObjNewObj(interp, NULL, 0));
        else if (tok->type == JSI_JTYPE_ARRAY)
            Jsi_ValueMakeArrayObject(interp, ret, Jsi_ObjNew(interp));
        else
            Jsi_ValueMakeUndef(interp, ret);
        goto done;
    }
    v = jsonGen1Value(interp, p, js, i, &i, 1);
    Jsi_ValueReplace(interp, ret, v);
    Jsi_DecrRefCount(interp, v);
done:
    Jsi_JsonFree(&pp);
    return result;
}

Project: jsish

void* Jsi_Realloc(void *m,unsigned int size) {
    void *v = realloc(m,size);
    Assert(v);
    return v;
}

Project: jsish

static Jsi_Value *ValueNew1(Jsi_Interp *interp)
{
    Jsi_Value *v = ValueNew(interp);
    Jsi_IncrRefCount(interp, v);
    return v;
}

Project: jsish

void Jsi_ValueCopy(Jsi_Interp *interp, Jsi_Value *to, Jsi_Value *from ) {
    return jsi_ValueCopyMove(interp, to, from, 1);
}

Project: jsish

int Jsi_Close(Jsi_Interp *interp, Jsi_Channel chan) {
    if (chan->fsPtr==0 || !chan->fsPtr->closeProc) return -1;
    if (chan->flags&JSI_FS_NOCLOSE) return -1;
    int rc = chan->fsPtr->closeProc(chan);
    if (rc == 0)
        Jsi_Free(chan);
    return rc;
}

Project: jsish

extern void jsi_TypeMismatch(Jsi_Interp* interp)
{
    interp->typeMismatchCnt++;
    if (interp->typeWarnMax<=0)
        return;
    if (interp->typeMismatchCnt>=interp->typeWarnMax) {
        memset(&interp->typeCheck, 0, sizeof(interp->typeCheck));
        Jsi_LogWarn("Max warnings exceeded %d: typeCheck disabled", interp->typeWarnMax);
    }
}

Project: jsish

Jsi_RC Jsi_GetIntFromValueBase(Jsi_Interp* interp, Jsi_Value *value, int *n, int base, int flags)
{
    int noMsg = (flags & JSI_NO_ERRMSG);
    /* TODO: inefficient to convert to double then back. */
    if (!value)
        return JSI_ERROR;
    Jsi_Number d = Jsi_ValueToNumberInt(interp, value, 1);
    if (!Jsi_NumberIsFinite(d))
    {
        if (!noMsg)
            Jsi_LogError("invalid number");
        return JSI_ERROR;
    }
    Jsi_ValueReset(interp,&value);
    Jsi_ValueMakeNumber(interp, &value, d);
    *n = (int)d;
    return JSI_OK;
}

Project: jsish

static Jsi_OpCodes *code_in() { JSI_NEW_CODES(0,OP_IN, 0); }

Project: jsish

static void insert_case5(Jsi_TreeEntry* n) {
    Jsi_TreeEntry* g = grandparent(n);
    set_color(n->parent, _JSI_TREE_BLACK);
    set_color(g, _JSI_TREE_RED);
    if (n == n->parent->left) {
        rotate_right(g);
    } else {
        Assert (n == n->parent->right);
        rotate_left(g);
    }
}

Project: jsish

static Jsi_RC FilesysGetsCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    int len;
    UdfGet(udf, _this, funcPtr);
    if (!udf->filename) {
        Jsi_ValueMakeUndef(interp, ret);
        return JSI_OK;
    }
    char buf[JSI_BUFSIZ];
    if (!Jsi_Gets(interp, udf->chan, buf, sizeof(buf))) {
        Jsi_ValueMakeUndef(interp, ret);
        return JSI_OK;
    }
    buf[sizeof(buf)-1] = 0;
    len = Jsi_Strlen(buf);
    if (len > 0 && buf[len-1] == '\n')
        buf[len-1] = 0;
    Jsi_ValueMakeStringDup(interp, ret, buf);
    return JSI_OK;
}

Project: jsish

int Jsi_Chdir(Jsi_Interp *interp, Jsi_Value* path) {
    void *data;
    int rc = 0;
    const char *pathPtr = Jsi_ValueToString(interp, path, NULL);
    if (interp->isSafe && Jsi_InterpAccess(interp, path, JSI_INTACCESS_READ) != JSI_OK) 
        return Jsi_LogError("read access denied");
    Jsi_Filesystem *fsPtr = Jsi_FilesystemForPath(interp, path, &data);
    if (fsPtr == &jsiFilesystem) {
        rc = chdir(pathPtr);
        if (rc < 0)
            return -1;
        /* If change out of native fs, GetCwd will use pwdStr */
        fsPtr = NULL;
    }
    Jsi_DSSetLength(&jsiIntData.pwdStr, 0);
    Jsi_DSAppendLen(&jsiIntData.pwdStr, pathPtr, -1);
    jsiIntData.cwdFsPtr = fsPtr;
    jsiIntData.pwd = fsPtr ? Jsi_DSValue(&jsiIntData.pwdStr) : NULL;
    return rc;
}

Project: jsish

Jsi_Channel Jsi_Open(Jsi_Interp *interp, Jsi_Value *file, const char *modeString)
{
    /* Find fsys, and use open there. */
    Jsi_Filesystem *fsPtr;
    Jsi_Chan *ch = NULL;
    void *data;
    int fnl = 0;
    const char *fileName = Jsi_ValueString(interp, file, &fnl), *oldFN = fileName;
    if (!fileName || !fnl) {
        Jsi_LogError("expected string filename");
        return NULL;
    }
    if (!Jsi_Strcmp(fileName, "stdin")) return jsiIntData.stdChans;
    if (!Jsi_Strcmp(fileName, "stdout")) return jsiIntData.stdChans+1;
    if (!Jsi_Strcmp(fileName, "stderr")) return jsiIntData.stdChans+2;
    const char *s = modeString;
    bool quiet = 0;
    if (s[0]=='-') {
        quiet = true;
        s++;
    }
    char Mode[sizeof(ch->modes)];
    Jsi_StatBuf sb;
    Jsi_Value *path = NULL;
    int n, i, mode = 0, rc, writ, aflag;
    if (!s)
        s = "r";
    if (Jsi_Strlen(s) >= sizeof(ch->modes)) {
        Jsi_LogError("mode too long: %s", s);
        return NULL;
    }
    if (Jsi_Strchr(s, 'z') || Jsi_Strchr(s, 'Z')) {
        Jsi_Filesystem *fsPtr = jsi_FilesysFind("jfz");
        if (!fsPtr) {
            Jsi_LogError("compressed files unsupported");
            return NULL;
        }
        ch = fsPtr->openProc(interp, file, s);
        if (!ch) {
            if (!quiet)
                Jsi_LogError("File open failed '%s'", fileName);
            return NULL;
        }
        Jsi_Chan *nch = (Jsi_Chan *)Jsi_Calloc(1,sizeof(*nch));
        *nch = *ch;
        nch->fsPtr = fsPtr;
        return nch;
    }
    for (i=0, n = 0; s[i]; i++) {
        switch (s[i]) {
            case '+': break;
            case 'b': break;
            case 'r': if (!Jsi_Strchr(s,'+')) mode |= JSI_FS_READONLY; break;
            case 'a':
            case 'w': if (!Jsi_Strchr(s,'+')) mode |= JSI_FS_WRITEONLY; break;
            default: Jsi_LogError("unknown mode char: %c", s[i]); return NULL;
        }
        Mode[n++] = s[i];
    }
    Mode[n] = 0;
    /* Fixup files in the ~ dir */
    rc = Jsi_Stat(interp, file,&sb);
    if ((rc != 0 || *fileName == '~') && (fileName = Jsi_FileRealpath(interp, file, NULL))) {
        path = Jsi_ValueNewString(interp, fileName, -1);
        Jsi_IncrRefCount(interp, path);
        rc = Jsi_Stat(interp, path, &sb);
        if (rc == 0)
            file = path;
    }
    if (!fileName) {
        Jsi_LogError("file error: %s", oldFN);
        return NULL;
    }

    if (rc == 0 &&  sb.st_mode & S_IFDIR )
    {
        Jsi_LogError("can not open directory: %s", fileName);
        goto done;
    }
    fsPtr = Jsi_FilesystemForPath(interp, file, &data);
    writ = (Jsi_Strchr(s,'w') || Jsi_Strchr(s,'a') || Jsi_Strchr(s,'+'));
    aflag = (writ ? JSI_INTACCESS_WRITE : JSI_INTACCESS_READ);
    if (fsPtr && fsPtr != &jsiFilesystem) {
        ch = fsPtr->openProc(interp, file, Mode);
        if (ch)
            ch->isNative = 0;
        else
            Jsi_LogError("File open failed '%s'", fileName);
    } else {
        if (interp->isSafe && ((rc && Jsi_InterpAccess(interp, file, JSI_INTACCESS_CREATE) != JSI_OK)
        || Jsi_InterpAccess(interp, file, aflag) != JSI_OK)) {
            Jsi_LogError("%s access denied: %s", writ?"write":"read", fileName);
            goto done;
        }
        FILE *fp = fopen(fileName, Mode);
        fsPtr = &jsiFilesystem;
        if (!fp) {
            if (!quiet)
                Jsi_LogError("File open failed '%s'", fileName);
            goto done;
        }
        ch = (Jsi_Chan *)Jsi_Calloc(1,sizeof(*ch));
        ch->fp = fp;
        ch->isNative = 1;
    }
    if (ch) {
        ch->flags |= mode; // + (zLevel<<24);
        Jsi_Strcpy(ch->modes, s);
        ch->fsPtr = fsPtr;
        ch->fname = fileName;
    }
done:
    if (path)
        Jsi_DecrRefCount(interp, path);
    return ch;
}

Project: jsish

Jsi_Value *Jsi_Executable(Jsi_Interp *interp)
{
    return jsiIntData.execValue;
}

Project: jsish

static Jsi_TreeEntry* uncle(Jsi_TreeEntry* n) {
    Assert (n != NULL);
    Assert (n->parent != NULL);
    Assert (n->parent->parent != NULL);
    return sibling(n->parent);
}

Project: jsish

Jsi_RC Jsi_MapConf(Jsi_Map *mapPtr, Jsi_MapOpts *opts, bool set)
{
    switch (mapPtr->typ) {
        case JSI_MAP_HASH: return Jsi_HashConf(mapPtr->v.hash, opts, set);
        case JSI_MAP_TREE: return Jsi_TreeConf(mapPtr->v.tree, opts, set);
        case JSI_MAP_LIST: return Jsi_ListConf(mapPtr->v.list, opts, set);
        case JSI_MAP_NONE: break;
    }
    return JSI_ERROR;
}

Project: jsish

Jsi_RC jsi_RegExpValueNew(Jsi_Interp *interp, const char *regtxt, Jsi_Value *ret)
{
    
    Jsi_DString dStr = {};
    Jsi_DSAppend(&dStr, "/", regtxt, "/", NULL);
    Jsi_Regex *re = Jsi_RegExpNew(interp, Jsi_DSValue(&dStr), 0);
    Jsi_DSFree(&dStr);
    if (re == NULL)
        return JSI_ERROR;
    Jsi_Obj *o = Jsi_ObjNewType(interp, JSI_OT_REGEXP);
    Jsi_ValueMakeObject(interp, &ret, o);
    ret->d.obj->d.robj = re;
    ret->d.obj->ot = JSI_OT_REGEXP;
    return JSI_OK;
}

Project: jsish

static void mdbRelease1Stmt( MySqlObj *jdb, MysqlPrep *prep ) {
    // TODO: split out parts reusable by cached query.
    int i;
    if (prep->deleting)
        return;
    prep->deleting = 1;
    if (prep->myStmt)
        mysql_stmt_close(prep->myStmt);
    if (prep->resultMetaData)
        mysql_free_result(prep->resultMetaData);
    if (prep->paramMetaData)
        mysql_free_result(prep->paramMetaData);
    if (prep->bindParam)
        Jsi_Free(prep->bindParam);
    if (prep->fieldParam)
        Jsi_Free(prep->fieldParam);
    if (prep->bindResult) {
        for (i=0; i<prep->numCol; i++) {
            MYSQL_BIND *bind = prep->bindResult+i;
            if (bind->buffer_type == MYSQL_TYPE_STRING && bind->buffer)
                Jsi_Free(bind->buffer);
        }
        Jsi_Free(prep->bindResult);
    }
    if (prep->fieldResult)
        Jsi_Free(prep->fieldResult);
    if (prep->colTypes)
        Jsi_Free(prep->colTypes);
    if (prep->colNames)
        Jsi_Free(prep->colNames);
    if (prep->zSql)
        Jsi_Free(prep->zSql);
    if (prep->naStr) {
        Jsi_Free(prep->origSql);
        Jsi_DSFree(prep->naStr);
        Jsi_Free(prep->naStr);
    }
    if (prep->entry)
        Jsi_HashEntryDelete(prep->entry);
    if (prep->elPtr) {
        Jsi_ListEntry *pPtr = prep->elPtr;
        prep->elPtr = NULL;
        Jsi_ListEntryDelete(pPtr);
    }
    Jsi_Free(prep);
    jdb->numStmts--;
}

Project: jsish

static Jsi_RC fileObjFree(Jsi_Interp *interp, void *data)
{
    FileObj *fo = (FileObj *)data;
    SIGASSERT(fo,FILEOBJ);
    fileObjErase(fo);
    Jsi_Free(fo);
    return JSI_OK;
}

Project: jsish

bool Jsi_StrIsAlnum(const char *cp)
{
    if (!cp || !*cp) return 0;
    while (*cp)
        if (isalnum(*cp) || *cp == '_')
            cp++;
        else
            return 0;
    return 1;
}

Project: jsish

static Jsi_RC jsi_InterpDelete(Jsi_Interp *interp, void *ptr) {
    if (jsi_vf)
        Jsi_DecrRefCount(interp, jsi_vf);
    jsi_vf = NULL;
    jsi_exitCode = interp->exitCode;
    jsi_deleted = 1;
    return JSI_OK;
}

Project: jsish

Jsi_RC Jsi_CommandInvoke(Jsi_Interp *interp, const char *cmdstr, Jsi_Value *args, Jsi_Value **ret)
{
    Jsi_Value *func = Jsi_NameLookup(interp, cmdstr);
    if (func)
        return Jsi_FunctionInvoke(interp, func, args, ret, NULL);
    return Jsi_LogError("can not find cmd: %s", cmdstr);
}

Project: jsish

Jsi_RC Jsi_LogMsg(Jsi_Interp *interp, uint code, const char *format,...) {
    va_list va;
    va_start (va, format);
    const char *mt = (code <= JSI__LOGLAST ? jsi_LogCodes[code] : "");
    fputs(mt, stderr);
    vfprintf(stderr, format, va);
    fputs("\n", stderr);
    va_end(va);
    return JSI_ERROR;
}

Project: jsish

static Jsi_OpCodes *code_jtrue(int off) { JSI_NEW_CODES(0,OP_JTRUE, off); }

Project: jsish

char *Jsi_ValueString(Jsi_Interp *interp, Jsi_Value* v, int *lenPtr)
{
    if (!v) return NULL;
    Jsi_String *s = jsi_ValueString(v);
    if (s) {
        if (lenPtr)
            *lenPtr = (s->len<0 ? (int)Jsi_Strlen(s->str) : s->len);
        return s->str;
    }
    if (lenPtr)
        *lenPtr = 0;
    return NULL;
}

Project: jsish

Jsi_Value* Jsi_ValueMakeBool(Jsi_Interp *interp, Jsi_Value **vPtr, int b) {
    Jsi_Value *v = (vPtr?*vPtr:NULL);
    if (!v)
        v = Jsi_ValueNew(interp);
    else
        Jsi_ValueReset(interp, vPtr);
    v->vt = JSI_VT_BOOL;
    v->d.val = b;
    return v;
}

Project: jsish

static int jsi_FSRemoveProc(Jsi_Interp *interp, Jsi_Value* path, int flags) {
    const char *pathPtr = Jsi_ValueToString(interp, path, NULL);
    Jsi_DString dStr = {};
    if (*pathPtr == '~')
        pathPtr = jsi_TildePath(interp, pathPtr, &dStr);
    int rc = remove(pathPtr);
    Jsi_DSFree(&dStr);
    return rc;
}

Project: jsish

void* Jsi_MapValueGet(Jsi_MapEntry *h){
    switch (jsi_GetListType(h)) {
        case JSI_MAP_HASH: return Jsi_HashValueGet((Jsi_HashEntry*)h);
        case JSI_MAP_TREE: return Jsi_TreeValueGet((Jsi_TreeEntry*)h);
        case JSI_MAP_LIST: return Jsi_ListValueGet((Jsi_ListEntry*)h);
        case JSI_MAP_NONE: break;
    }
    return NULL;
}

Project: jsish

static void rotate_right(Jsi_TreeEntry* n) {
    Jsi_TreeEntry* l;
    Assert(n);
    l = n->left;
    replace_node(n, l);
    n->left = l->right;
    if (l->right != NULL) {
        l->right->parent = n;
    }
    l->right = n;
    n->parent = l;
}

Project: jsish

static Jsi_RC StringMatchCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    int sLen, bLen;
    const char *source_str;
    ChkString(_this, funcPtr, source_str, &sLen, &bLen);
    const char *v = source_str;
    Jsi_Value *seq = Jsi_ValueArrayIndex(interp, args, skip);

    if (Jsi_ValueIsString(interp, seq)) {
        char *cp = Jsi_ValueString(interp, seq, NULL);

        if (jsi_RegExpValueNew(interp, cp, seq) != JSI_OK)
            return JSI_ERROR;
    }
    /* TODO: differentiate from re.exec() */
    return jsi_RegExpMatches(interp, seq, v, bLen, *ret, NULL, 1);
}

Project: jsish

static Jsi_OpCodes *code_etry(jsi_Pstate *p, jsi_Pline *line) { JSI_NEW_CODESLN(0,OP_ETRY, 0); }

Project: jsish

static Jsi_RC jsi_wsDelPss(Jsi_Interp *interp, void *data) {
    Jsi_Free(data);
    return JSI_OK;
}

Project: jsish

static Jsi_TreeEntry* grandparent(Jsi_TreeEntry* n) {
    Assert (n != NULL);
    Assert (n->parent != NULL);
    Assert (n->parent->parent != NULL);
    return n->parent->parent;
}

Project: jsish

void Jsi_SplitStr(const char *str, int *argcPtr,
              char ***argvPtr, const char *ch, Jsi_DString *dStr)
{
    char *cp, *ep, *p, **argv;
    int cnt = 1, len, i, clen;
    if (!ch)
        ch = "";
    clen = Jsi_Strlen(ch);
    if (clen<=0)
        return SplitChar(str, argcPtr, argvPtr, *ch, dStr);
    len = Jsi_Strlen(str);
    cp = (char*)str;
    while (*cp) {
        cp = Jsi_Strstr(cp,ch);
 
        if (cp == NULL || *cp == 0) break;
        cp += clen;
        cnt++;
    }
    //argv = (char**)Jsi_Calloc(1,(sizeof(char*)*(cnt+3) + sizeof(char)*(len+6)));
    Jsi_DSSetLength(dStr, (sizeof(char*)*(cnt+3) + sizeof(char)*(len+6)));
    argv = (char**)Jsi_DSValue(dStr);
    *argvPtr = argv;
    *argcPtr = cnt;
    p = (char*)&(argv[cnt+2]);
    argv[cnt+1] = p;
    Jsi_Strcpy(p, str);
    cp = p;
    i = 0;
    argv[i++] = p;
    while (*cp) {
        ep = Jsi_Strstr(cp,ch);
        if (ep == NULL || *ep == 0) break;
        *ep = 0;
        cp = ep+clen;
        argv[i++] = cp;
    }
    argv[cnt] = NULL;
}

Project: jsish

Jsi_JSONQuote(Jsi_Interp *interp, const char *str, int len, Jsi_DString *dsPtr)
{
    const char *cp = str;
    int i = 0;
    if (len<0)
        len = Jsi_Strlen(str);
    char cbuf[10];
    Jsi_DSAppend(dsPtr,"\"",NULL);
    while (*cp && i++<len) {
        if (*cp == '\\' /* || *cp == '/' */ || *cp == '\"') {
            cbuf[0] = '\\';
            cbuf[1] = *cp;
            cbuf[2] = 0;
            Jsi_DSAppend(dsPtr,cbuf,NULL);
        } else if (!isprint(*cp)) {
            int ilen;
            switch (*cp) {
                case '\b': Jsi_DSAppend(dsPtr,"\\b",NULL); break;
                case '\n': Jsi_DSAppend(dsPtr,"\\n",NULL); break;
                case '\r': Jsi_DSAppend(dsPtr,"\\r",NULL); break;
                case '\f': Jsi_DSAppend(dsPtr,"\\f",NULL); break;
                case '\t': Jsi_DSAppend(dsPtr,"\\t",NULL); break;
                default:
                    if ((ilen = Jsi_UtfEncode(cp, cbuf))) {
                        Jsi_DSAppend(dsPtr,cbuf,NULL);
                        cp += (ilen-1);
                    }
            }
        } else {
            cbuf[0] = *cp;
            cbuf[1] = 0;
            Jsi_DSAppend(dsPtr,cbuf,NULL);
        }
        cp++;
    }
    
    Jsi_DSAppend(dsPtr,"\"", NULL);
    return Jsi_DSValue(dsPtr);;
}

Project: jsish

void jsi_DebugDumpValues(Jsi_Interp *interp)
{
    if (jsiIntData.mainInterp != interp) return;
    int vdLev = interp->memDebug;
    int have = (interp->dbPtr->valueDebugTbl->numEntries || interp->dbPtr->objDebugTbl->numEntries);
    if ((have && vdLev>0) || vdLev>=3) {
        // First traverse all Object trees/arrays and mark all values contained therein.
        Jsi_HashSearch search;
        Jsi_HashEntry *hPtr;
        for (hPtr = Jsi_HashSearchFirst(interp->dbPtr->objDebugTbl, &search);
            hPtr != NULL; hPtr = Jsi_HashSearchNext(&search)) {
            Jsi_Obj *vp = (Jsi_Obj *)Jsi_HashKeyGet(hPtr);
            if (vp!=NULL && vp->sig == JSI_SIG_OBJ) {
                jsiFlagDebugValues(interp, vp);
            }
        }
        if (interp->dbPtr->valueDebugTbl->numEntries != interp->dbPtr->valueCnt)
            fprintf(stderr, "\n\nValues table/alloc mismatch: table=%d, alloc=%d\n",
                interp->dbPtr->valueDebugTbl->numEntries, interp->dbPtr->valueCnt);
        // Dump unfreed values and objs.
        int refSum=0, refsum=0;
        int bcnt[4] = {};
        if (vdLev>1 && interp->dbPtr->valueDebugTbl->numEntries)
            fprintf(stderr, "\n\nUNFREED VALUES \"[*ptr,#refCnt,type,idx:label,label2]: @file:line in func() ...\"\n");
        for (hPtr = Jsi_HashSearchFirst(interp->dbPtr->valueDebugTbl, &search);
            hPtr != NULL; hPtr = Jsi_HashSearchNext(&search)) {
            Jsi_Value *vp = (Jsi_Value *)Jsi_HashKeyGet(hPtr);
            if (vp==NULL || vp->sig != JSI_SIG_VALUE) {
                bcnt[0]++;
                if (vdLev>1)
                    fprintf(stderr, "BAD VALUE: %p\n", vp);
            } else {
                bcnt[1]++;
                refSum += vp->refCnt;
                if (vdLev>1) {
                    char ebuf[JSI_BUFSIZ], ebuf2[JSI_MAX_NUMBER_STRING];
                    ebuf[0] = 0;
                    if (vp->vt==JSI_VT_OBJECT)
                        snprintf(ebuf, sizeof(ebuf), " {obj=%p, otype=%s}", vp->d.obj, Jsi_ObjTypeStr(interp, vp->d.obj));
                    else if (vp->vt==JSI_VT_NUMBER)
                        snprintf(ebuf, sizeof(ebuf), " {num=%s}", Jsi_NumberToString(interp, vp->d.num, ebuf2, sizeof(ebuf2)));
                    else if (vp->vt==JSI_VT_BOOL)
                        snprintf(ebuf, sizeof(ebuf), " {bool=%s}", vp->d.val?"true":"false");
                    else if (vp->vt==JSI_VT_STRING) {
                        const char *sbuf = ((vp->d.s.str && Jsi_Strlen(vp->d.s.str)>40)?"...":"");
                        snprintf(ebuf, sizeof(ebuf), " {string=\"%.40s%s\"}", (vp->d.s.str?vp->d.s.str:""), sbuf);
                    }
                    const char *pfx = "";
                    if (!(vp->VD.flags&MDB_INOBJ))
                        pfx = "!"; // Value is not contained in an object.
                    fprintf(stderr, "[%s*%p,#%d,%s,%d:%s%s%s]:%s @%s:%d in %s()%s\n", pfx,
                        vp, vp->refCnt, Jsi_ValueTypeStr(interp, vp), vp->VD.Idx,
                        (vp->VD.label?vp->VD.label:""), (vp->VD.label2?":":""),
                        (vp->VD.label2?vp->VD.label2:""), vp->VD.interp==jsiIntData.mainInterp?"":"!",
                        vp->VD.fname, vp->VD.line, vp->VD.func, ebuf);
                }
            }
        }
        if (interp->dbPtr->objDebugTbl->numEntries != interp->dbPtr->objCnt)
            fprintf(stderr, "\n\nObject table/alloc mismatch: table=%d, alloc=%d\n",
                interp->dbPtr->objDebugTbl->numEntries, interp->dbPtr->objCnt);
        if (vdLev>1 && interp->dbPtr->objDebugTbl->numEntries)
            fprintf(stderr, "\n\nUNFREED OBJECTS \"[*ptr,#refCnt,type,idx:label,label2]: @file:line in func() ...\"\n");
        for (hPtr = Jsi_HashSearchFirst(interp->dbPtr->objDebugTbl, &search);
            hPtr != NULL; hPtr = Jsi_HashSearchNext(&search)) {
            Jsi_Obj *vp = (Jsi_Obj *)Jsi_HashKeyGet(hPtr);
            if (vp==NULL || vp->sig != JSI_SIG_OBJ) {
                bcnt[2]++;
                fprintf(stderr, "BAD OBJ: %p\n", vp);
            } else {
                bcnt[3]++;
                refsum += vp->refcnt;
                if (vdLev>1) {
                    char ebuf[JSI_BUFSIZ], ebuf2[JSI_MAX_NUMBER_STRING];
                    ebuf[0] = 0;
                    if (vp->ot==JSI_OT_OBJECT) {
                        if (vp->isarrlist)
                            snprintf(ebuf, sizeof(ebuf), "tree#%d, array#%d", (vp->tree?vp->tree->numEntries:0), vp->arrCnt);
                        else
                            snprintf(ebuf, sizeof(ebuf), "tree#%d", (vp->tree?vp->tree->numEntries:0));
                    } else if (vp->ot==JSI_OT_NUMBER)
                        snprintf(ebuf, sizeof(ebuf), "num=%s", Jsi_NumberToString(interp, vp->d.num, ebuf2, sizeof(ebuf2)));
                    else if (vp->ot==JSI_OT_BOOL)
                        snprintf(ebuf, sizeof(ebuf), "bool=%s", vp->d.val?"true":"false");
                    else if (vp->ot==JSI_OT_STRING) {
                        const char *sbuf = ((vp->d.s.str && Jsi_Strlen(vp->d.s.str)>40)?"...":"");
                        snprintf(ebuf, sizeof(ebuf), "string=\"%.40s%s\"", (vp->d.s.str?vp->d.s.str:""), sbuf);
                    }
                    fprintf(stderr, "[*%p,#%d,%s,%d:%s%s%s]:%s @%s:%d in %s() {%s}\n",
                        vp, vp->refcnt, Jsi_ObjTypeStr(interp, vp), vp->VD.Idx, vp->VD.label?vp->VD.label:"",
                        vp->VD.label2?":":"",vp->VD.label2?vp->VD.label2:"", vp->VD.interp==jsiIntData.mainInterp?"":"!",
                        vp->VD.fname, vp->VD.line,
                        vp->VD.func, ebuf);
                }
            }
        }
        fprintf(stderr, "\nVALUES: bad=%d,unfreed=%d,allocs=%d,refsum=%d  | OBJECTS: bad=%d,unfreed=%d,allocs=%d,refsum=%d  interp=%p\n",
            bcnt[0], bcnt[1], interp->dbPtr->valueAllocCnt, refSum, bcnt[2], bcnt[3], interp->dbPtr->objAllocCnt, refsum, interp);

        if (interp->codesTbl)
            for (hPtr = Jsi_HashSearchFirst(interp->codesTbl, &search);
                hPtr != NULL; hPtr = Jsi_HashSearchNext(&search)) {
                Jsi_OpCodes *vp = (Jsi_OpCodes *)Jsi_HashKeyGet(hPtr);
                fprintf(stderr, "unfreed opcodes: %d\n", vp->id);
            }
    }
    Jsi_HashDelete(interp->dbPtr->valueDebugTbl);
    Jsi_HashDelete(interp->dbPtr->objDebugTbl);
    Jsi_HashDelete(interp->codesTbl);
    bool isMainInt = (interp == jsiIntData.mainInterp);
    if (isMainInt && vdLev>3)
        _exit(1); // Avoid sanitize output.
}

Project: jsish

static Jsi_RC MySqlInfoCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                              Jsi_Value **ret, Jsi_Func *funcPtr)
{
    MySqlObj *jdb;
    if (!(jdb = _mysql_getDbHandle(interp, _this, funcPtr))) return JSI_ERROR;
    Jsi_Obj *nobj = Jsi_ObjNew(interp);
    Jsi_ValueMakeObject(interp, ret, nobj);
    const char *str, *svals[20];
    int i = 0;
    svals[i++] = "clientInfo";
    svals[i++] = mysql_get_client_info();
    svals[i++] = "hostInfo";
    svals[i++] = mysql_get_host_info(jdb->db);
    svals[i++] = "serverInfo";
    svals[i++] = mysql_get_server_info(jdb->db);
    svals[i++] = "stat";
    svals[i++] = mysql_stat(jdb->db);
    svals[i++] = 0;
    i = 0;
    while (svals[i]) {
        str = svals[i+1];
        Jsi_ObjInsert(interp, nobj, svals[i], str?Jsi_ValueNewStringDup(interp, str):Jsi_ValueNewNull(interp), 0);
    }
    Jsi_ObjInsert(interp, nobj, "threadId", Jsi_ValueNewNumber(interp, (Jsi_Number)mysql_thread_id(jdb->db)), 0);
    Jsi_ObjInsert(interp, nobj, "protocolVersion", Jsi_ValueNewNumber(interp, (Jsi_Number)mysql_get_proto_info(jdb->db)), 0);
    Jsi_ObjInsert(interp, nobj, "clientVersion", Jsi_ValueNewNumber(interp, (Jsi_Number)mysql_get_client_version()), 0);
    Jsi_ObjInsert(interp, nobj, "serverVersion", Jsi_ValueNewNumber(interp, (Jsi_Number)mysql_get_server_version(jdb->db)), 0);
    Jsi_ObjInsert(interp, nobj, "warningCount", Jsi_ValueNewNumber(interp, (Jsi_Number)mysql_warning_count(jdb->db)), 0);
    return JSI_OK;
}

Project: jsish

void Jsi_StackFree(Jsi_Stack *stack)
{
    Jsi_Free(stack->vector);
    Jsi_Free(stack);
}

Project: jsish

Jsi_Value* Jsi_ValueMakeStringKey(Jsi_Interp *interp, Jsi_Value **vPtr, const char *s) {
    Jsi_Value *v = (vPtr?*vPtr:NULL);
    if (!v)
        v = Jsi_ValueNew(interp);
    else
        Jsi_ValueReset(interp, vPtr);
    v->vt = JSI_VT_STRING;
    v->d.s.str = (char*)Jsi_KeyAdd(interp,s);
    v->d.s.len = Jsi_Strlen(s);
    v->f.bits.isstrkey = 1;
    return v;
}

Project: jsish

static Jsi_OpCodes *code_push_func(jsi_Pstate *p, jsi_Pline *line, Jsi_Func *fun) {
    Jsi_OpCodes* codes = code_push_func_sub(p, line, fun);
    if (codes && fun && fun->name)
        codes->codes[0].local = 1;
    return codes;
}

Project: jsish

void jsi_ValueDebugLabel_(jsi_ValueDebug *vd, const char *l1, const char *l2)
{
    if (l1)
        vd->label = l1;
    if (l2)
        vd->label2 = l2;
}

Project: jsish

static Jsi_OpCodes *code_lessequ() { JSI_NEW_CODES(0,OP_LESSEQU, 0); }

Project: jsish

Jsi_Func *jsi_FuncNew(Jsi_Interp *interp)
{
     Jsi_Func *func = (Jsi_Func*)Jsi_Calloc(1, sizeof(Jsi_Func));
     SIGINIT(func, FUNC);
     func->hPtr = Jsi_HashSet(interp->funcsTbl, func, func);
     func->refCnt = 1;
     interp->funcCnt++;
     return func;
}

Project: jsish

static Jsi_OpCodes *code__fcall(jsi_Pstate *p, jsi_Pline *line, int argc, const char *name, const char *namePre, Jsi_OpCodes *argCodes) {
    jsi_FuncCallCheck(p,line,argc,1, name, namePre, argCodes);JSI_NEW_CODESLN(0,OP_FCALL, argc);
}

Project: jsish

void jsi_InitLocalVar(Jsi_Interp *interp, Jsi_Value *arguments, Jsi_Func *who)
{
    SIGASSERTV(who, FUNC);
    if (who->localnames) {
        int i;
        for (i = 0; i < who->localnames->count; ++i) {
            const char *argkey = jsi_ScopeStrsGet(who->localnames, i);
            if (argkey) {
                DECL_VALINIT(key);// = VALINIT;
                Jsi_Value *v __attribute__((unused));
                Jsi_Value *kPtr = &key; // Note: a string key so no reset needed.
                Jsi_ValueMakeStringKey(interp, &kPtr, argkey);
                v = jsi_ValueObjKeyAssign(interp, arguments, kPtr, NULL, JSI_OM_DONTENUM);
                jsi_ValueDebugLabel(v, "locals", who->name);
            }
        }
    }
}

Project: jsish

static Jsi_RC NumberIsIntegerCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr) {
    return jsi_NumberIsFiniteCmd(interp, args, _this, ret, funcPtr, 2);
}

Project: jsish

void jsi_PstateAddVar(jsi_Pstate *ps, jsi_Pline *line, const char *str)
{
    Jsi_Interp *interp = ps->interp;
    int i;
    if (interp->scopes[interp->cur_scope] == NULL)
        interp->scopes[interp->cur_scope] = (Jsi_ScopeStrs *)jsi_ScopeStrsNew();
    
    for (i = 0; i < interp->scopes[interp->cur_scope]->count; ++i) {
        if (Jsi_Strcmp(str, interp->scopes[interp->cur_scope]->args[i].name) == 0) {
            Jsi_Interp *interp = ps->interp;
            if (interp && interp->strict) {
                interp->parseLine = line;
                Jsi_LogWarn("duplicate var: %s", str);
                interp->parseLine = NULL;
            }
            return;
        }
    }
    jsi_ScopeStrsPush(ps->interp, interp->scopes[interp->cur_scope], str, JSI_VT_UNDEF);
}

Project: jsish

Jsi_RC Jsi_FunctionInvokeString(Jsi_Interp *interp, Jsi_Value *func, Jsi_Value *arg, Jsi_DString *dStr)
{
    if (interp->deleting)
        return JSI_ERROR;
    Jsi_Value *vpargs, *frPtr = Jsi_ValueNew1(interp);
    Jsi_RC rc;
    if (!arg) {
        if (!interp->nullFuncArg) {
            interp->nullFuncArg = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, NULL, 0, 0));
            Jsi_IncrRefCount(interp, interp->nullFuncArg);
        }
        vpargs = interp->nullFuncArg;
    } else {
        vpargs = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, &arg, 1, 1));
    }
    Jsi_IncrRefCount(interp, vpargs);
    rc = Jsi_FunctionInvoke(interp, func, vpargs, &frPtr, NULL);
    Jsi_DecrRefCount(interp, vpargs);
    if (rc != JSI_OK)
        Jsi_LogError("function call failed");
    else
        Jsi_ValueGetDString(interp, frPtr, dStr, 0);
    Jsi_DecrRefCount(interp, frPtr);
    return rc;
}

Project: jsish

Jsi_Value *jsi_GlobalContext(Jsi_Interp *interp)
{
    return interp->csc;
}

Project: jsish

static void jsi_wsPathAlias(Jsi_Interp *interp, jsi_wsCmdObj *cmdPtr, const char **inPtr, Jsi_DString *dStr) {
    const char *ce, *cp = NULL;
    char *lcp;
    Jsi_Value *val;
    if (cmdPtr->pathAliases) {
        cp = *inPtr;
        if (*cp == '/') cp++;
        if ((ce = Jsi_Strchr(cp, '/'))) {
            int len = ce-cp;
            Jsi_DSSetLength(dStr, 0);
            Jsi_DSAppendLen(dStr, cp, len);
            cp = Jsi_DSValue(dStr);
            if ((val = Jsi_ValueObjLookup(interp, cmdPtr->pathAliases, cp, 0)) &&
                (cp = Jsi_ValueString(interp, val, NULL))) {
                *inPtr += (len+2);
                cmdPtr->curRoot = cp;
                return;
            }
        }
    }
    if (!Jsi_Strncmp(*inPtr, "/jsi/", 5)) {
        // Get/cache path for system load file, eg /zvfs/lib/Jsish.jsi.
        if (!(cp = cmdPtr->jsishPathCache)) {
            Jsi_PkgRequire(interp, "Jsish", 0);
            if (Jsi_PkgVersion(interp, "Jsish", &cp)>=0)
                cmdPtr->jsishPathCache = cp;
        }
        if (cp) {
            Jsi_DSSetLength(dStr, 0);
            Jsi_DSAppend(dStr, cp, NULL);
            cp = Jsi_DSValue(dStr);
            if ((lcp = Jsi_Strrchr(cp, '/'))) {
                *lcp = 0;
                *inPtr += 5;
                cmdPtr->curRoot = cp;
            }
        }
    }
}

Project: jsish

static Jsi_RC WebSocketFileCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    jsi_wsCmdObj *cmdPtr = (jsi_wsCmdObj*)Jsi_UserObjGetData(interp, _this, funcPtr);
    if (!cmdPtr)
        return Jsi_LogError("Apply to non-websock object");
    Jsi_Value *val = Jsi_ValueArrayIndex(interp, args, 0);
    if (val)
        return jsi_wsFileAdd(interp, cmdPtr, val);
    if (cmdPtr->fileHash)
        return Jsi_HashKeysDump(interp, cmdPtr->fileHash, ret, 0);
    return JSI_OK;
}

Project: jsish

Jsi_RC Jsi_InitWebSocket(Jsi_Interp *interp, int release)
{
    if (release)
        return Jsi_DoneWebSocket(interp);
#ifdef LWS_OPENSSL_SUPPORT
    Jsi_InterpAccess(interp, NULL, JSI_INTACCESS_SETSSL );
#endif
    Jsi_Hash *wsys;
    const char *libver = lws_get_library_version();
    int lvlen = sizeof(LWS_LIBRARY_VERSION)-1;
    if (Jsi_Strncmp(libver, LWS_LIBRARY_VERSION, lvlen) || !isspace(libver[lvlen]))
        return Jsi_LogError("Library version mismatch: HDR:%s != LIB:%s", LWS_LIBRARY_VERSION, libver);
#if JSI_USE_STUBS
  if (Jsi_StubsInit(interp, 0) != JSI_OK)
    return JSI_ERROR;
#endif
    Jsi_Value *info = Jsi_ValueNew1(interp);
    Jsi_JSONParseFmt(interp, &info, "{libVer:\"%s\", hdrVer:\"%s\", pkgVer:%d}",
        libver, LWS_LIBRARY_VERSION, jsi_WsPkgVersion);
    Jsi_PkgOpts wsPkgOpts = { wsObjCmd_Specs, &wsObjCmd, websockCmds, info };
    Jsi_RC rc = Jsi_PkgProvideEx(interp, "WebSocket", jsi_WsPkgVersion, Jsi_InitWebSocket, &wsPkgOpts);
    Jsi_DecrRefCount(interp, info);
    if (rc != JSI_OK)
        return JSI_ERROR;
    if (!(wsys = Jsi_UserObjRegister(interp, &websockobject))) {
        Jsi_LogBug("Can not init webSocket");
        return JSI_ERROR;
    }

    if (!Jsi_CommandCreateSpecs(interp, websockobject.name, websockCmds, wsys, JSI_CMDSPEC_ISOBJ))
        return JSI_ERROR;
    return JSI_OK;
}

Project: jsish

Jsi_Value* Jsi_ValueNewNull(Jsi_Interp *interp) {
    Jsi_Value *v = Jsi_ValueNew(interp);
    v->vt = JSI_VT_NULL;
    return v;
}

Project: jsish

static Jsi_RC _object_get_callback(Jsi_Tree *tree, Jsi_TreeEntry *hPtr, void *data)
{
    Jsi_Value *v;
    objwalker *ow = (objwalker *)data;
    Jsi_DString *dStr = ow->dStr;
    int len;
    char *str;
    if ((hPtr->f.bits.dontenum))
        return JSI_OK;
    v =(Jsi_Value*) Jsi_TreeValueGet(hPtr);
    if ((ow->quote&JSI_OUTPUT_JSON) && v && v->vt == JSI_VT_UNDEF)
        return JSI_OK;
    str = (char*)Jsi_TreeKeyGet(hPtr);
    char *cp = Jsi_DSValue(dStr);
    len = Jsi_DSLength(dStr);
    if (len>=2 && (cp[len-2] != '{' || cp[len-1] == '}'))
        Jsi_DSAppend(dStr, ", ", NULL);
    if (((ow->quote&JSI_OUTPUT_JSON) == 0 || (ow->quote&JSI_JSON_STRICT) == 0) && Jsi_StrIsAlnum(str)
        && !Jsi_HashEntryFind(tree->opts.interp->lexkeyTbl, str))
        Jsi_DSAppend(dStr, str, NULL);
    else
        /* JSON/spaces, etc requires quoting the name. */
        Jsi_DSAppend(dStr, "\"", str, "\"", NULL);
    Jsi_DSAppend(dStr, ":", NULL);
    ow->depth++;
    Jsi_RC rc = jsiValueGetString(tree->opts.interp, v, dStr, ow);
    ow->depth--;
    return rc;
}

Project: jsish

static void delete_case4(Jsi_TreeEntry* n) {
    Jsi_TreeEntry* s = sibling(n);
    if (node_color(n->parent) == _JSI_TREE_RED &&
            node_color(s) == _JSI_TREE_BLACK &&
            node_color(s->left) == _JSI_TREE_BLACK &&
            node_color(s->right) == _JSI_TREE_BLACK)
    {
        set_color(s, _JSI_TREE_RED);
        set_color(n->parent, _JSI_TREE_BLACK);
    }
    else
        delete_case5(n);
}

Project: jsish

Jsi_Value * jsi_ValueNew1(Jsi_Interp *interp, const char *fname, int line, const char *func) {
    Jsi_Value *v = ValueNew1(interp);
    jsi_ValueDebugUpdate(interp, v, valueDebugTbl, fname, line, func);
    return v;
}

Project: jsish

static char *get_inputline(Jsi_Interp *interp, int istty, const char *prompt)
{
    char *res;
#ifdef JSI_HAS_READLINE
    if (istty && interp->subOpts.noReadline==0) {
        res = jsi_sh_readline(prompt);
        if (res && *res) jsi_sh_add_history(res);
        return res;
    }
#endif
    int done = 0;
    char bbuf[JSI_BUFSIZ];
    Jsi_DString dStr = {};
    if (istty)
        fputs(prompt, stdout);
    fflush(stdout);
    while (!done) { /* Read a line. */
        bbuf[0] = 0;
        if (fgets(bbuf, sizeof(bbuf), stdin) == NULL)
            return NULL;
        Jsi_DSAppend(&dStr, bbuf, NULL);
        if (Jsi_Strlen(bbuf) < (sizeof(bbuf)-1) || bbuf[sizeof(bbuf)-1] == '\n')
            break;
    }
    res = Jsi_Strdup(Jsi_DSValue(&dStr));
    Jsi_DSFree(&dStr);
    return res;
}

Project: jsish

static Jsi_OpCodes *code_neg() { JSI_NEW_CODES(0,OP_NEG, 0); }

Project: jsish

Jsi_HashEntryDelete(Jsi_HashEntry *entryPtr)
{
    Jsi_HashEntry *prevPtr;
    Jsi_HashEntry **bucketPtr;
    size_t hindex;
    Jsi_Hash *tablePtr = entryPtr->tablePtr;
    Jsi_Interp *interp = tablePtr->opts.interp;
    JSI_NOTUSED(interp);
    int cnt = 0;
#ifdef JSI_HAS_SIG_HASHENTRY
    SIGASSERT(entryPtr, HASHENTRY);
#endif
    if (tablePtr->opts.lockHashProc && (*tablePtr->opts.lockHashProc)(tablePtr, 1) != JSI_OK)
        return 0;
    if (tablePtr->keyType == JSI_KEYS_ONEWORD) {
        hindex = RANDOM_INDEX(tablePtr, (const void *)entryPtr->hval);
    } else {
        hindex = (entryPtr->hval & tablePtr->mask);
    }
    bucketPtr = tablePtr->buckets + hindex;
    if (*bucketPtr == entryPtr) {
        *bucketPtr = entryPtr->nextPtr;
        cnt++;
    } else {
        for (prevPtr = *bucketPtr; /*empty*/; prevPtr = prevPtr->nextPtr) {
            if (prevPtr == NULL) {
                Jsi_LogBug("malformed bucket chain in Jsi_HashEntryDelete");
            }
            if (prevPtr->nextPtr == entryPtr) {
                prevPtr->nextPtr = entryPtr->nextPtr;
                cnt++;
                break;
            }
        }
    }
    if (tablePtr->opts.freeHashProc && entryPtr->clientData)
        (tablePtr->opts.freeHashProc)(tablePtr->opts.interp, entryPtr, entryPtr->clientData);

    if (tablePtr->opts.lockHashProc)
        (*tablePtr->opts.lockHashProc)(tablePtr, 0);
    tablePtr->numEntries--;
    Jsi_Free(entryPtr);
    return cnt;
}

Project: jsish

static Jsi_RC dbEvalStep(DbEvalContext *p) {
    while( p->zSql[0] || p->pPreStmt ) {
        Jsi_RC rc;
        if( p->pPreStmt==0 ) {
            rc = dbPrepareAndBind(p->jdb, p->zSql, &p->zSql, &p->pPreStmt);
            if( rc!=JSI_OK ) return rc;
        }
        rc = dbEvalStepSub(p, 1, NULL);
        if (rc != JSI_BREAK)
            return rc;
    }
    
    /* Finished */
    return JSI_BREAK;
}

Project: jsish

static void dbTraceHandler(void *cd, const char *zSql)
{
    int rc;
    Jsi_Db *jdb = (Jsi_Db*)cd;
    Jsi_Value *vpargs, *items[2] = {}, *ret;
    Jsi_Interp *interp = jdb->interp;
    items[0] = Jsi_ValueNewObj(interp, jdb->fobj);
    items[1] = Jsi_ValueMakeStringDup(interp, NULL, zSql);
    vpargs = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, items, 2, 0));
    Jsi_IncrRefCount(interp, vpargs);
    ret = Jsi_ValueNew1(interp);
    rc = Jsi_FunctionInvoke(interp, jdb->onTrace, vpargs, &ret, NULL);
    Jsi_DecrRefCount(interp, vpargs);
    Jsi_DecrRefCount(interp, ret);
    if (rc != JSI_OK)
        jdb->errCnt++;
}

Project: jsish

static int jsi_FSTellProc(Jsi_Channel chan) { return ftell(_JSI_GETFP(chan,1));}

Project: jsish

Jsi_RC Jsi_GetBoolFromValue(Jsi_Interp* interp, Jsi_Value *value, bool *n)
{
    if (!value)
        return JSI_ERROR;

    if (value->vt == JSI_VT_BOOL) {
        *n = value->d.val;
        return JSI_OK;
    }
    if (value->vt == JSI_VT_OBJECT && value->d.obj->ot == JSI_OT_BOOL) {
        *n = value->d.obj->d.val;
        return JSI_OK;
    }
    Jsi_LogError("invalid bool");
    return JSI_ERROR;
}

Project: jsish

static void jsi_restore_scope(Jsi_Interp* interp, jsi_Pstate *ps, jsi_TryList* trylist,
  jsi_ScopeChain **scope, Jsi_Value **currentScope, int *context_id) {

/* JSI_RESTORE_SCOPE(scope_save, curscope_save)*/
    if (*scope != (trylist->scope_save)) {
        jsi_ScopeChainFree(interp, *scope);
        *scope = (trylist->scope_save);
        interp->framePtr->ingsc = *scope;
    }
    if (*currentScope != (trylist->curscope_save)) {
        Jsi_DecrRefCount(interp, *currentScope);
        *currentScope = (trylist->curscope_save); 
        interp->framePtr->incsc = *currentScope;
    }
    *context_id = ps->_context_id++; 
}

Project: jsish

static int jsi_FSRewindProc(Jsi_Channel chan) { rewind(_JSI_GETFP(chan,1)); return 0;}

Project: jsish

Jsi_Value* Jsi_ValueMakeNull(Jsi_Interp *interp, Jsi_Value **vPtr) {
    Jsi_Value *v = (vPtr?*vPtr:NULL);
    if (!v)
        v = Jsi_ValueNew(interp);
    else
        Jsi_ValueReset(interp, vPtr);
    v->vt = JSI_VT_NULL;
    return v;
}

Project: jsish

static Jsi_OpCodes *code_sub() { JSI_NEW_CODES(0,OP_SUB, 0); }

Project: jsish

int Jsi_Chmod(Jsi_Interp *interp, Jsi_Value* path, int mode) {
    void *data;
    Jsi_Filesystem *fsPtr = Jsi_FilesystemForPath(interp, path, &data);
    if (fsPtr == NULL || !fsPtr->chmodProc) return -1;
    return fsPtr->chmodProc(interp, path, mode);
}

Project: jsish

int Jsi_OptionsProcessJSON(Jsi_Interp *interp, Jsi_OptionSpec *opts, void *data, const char *json, Jsi_Wide flags)
{
    uint i = 0, r, len;
    Jsi_RC rc = JSI_OK;
    int result = 0;
    int strict = (flags & JSI_JSON_STRICT);
    Jsi_JsonTok *tok;
    const char *err;
    Jsi_Value *v = NULL;
    Jsi_DString nStr;
    const char *t, *name = NULL;
    if (json == NULL)
        return 0;
    while (isspace(*json))
        json++;
    if (json[0] == 0)
        return 0;
        
    JSI_JSON_DECLARE(pp, tokens, 0);
    Jsi_JsonParser *p = &pp;
    pp.strict = strict;
    pp.flags = flags;

    r = Jsi_JsonParse(p, json);
    if (r != JSI_JSON_ERR_NONE) {
        int ofs = pp.pos, len = Jsi_Strlen(json);
        if (ofs<0 || ofs>len)
            ofs = 0;
        err = p->errStr;
        if (!err)
            err = Jsi_JsonGetErrname(r);
        Jsi_LogError("JSON parse error (%s) at offset %d \"%.30s\"", err, ofs, json+ofs);
        result = -1;
        goto done;
    }
    tok = p->tokens;
    Jsi_DSInit(&nStr);
    if (tok->size%2 || tok->type != JSI_JTYPE_OBJECT) {
        result = -1;
        goto done;
    }
    v = Jsi_ValueNew1(interp);
    for (i=1; i<p->toknext && i<tok->size; i++) {
        if (p->tokens[i].type != JSI_JTYPE_STRING) {
            result = Jsi_LogError("expected string at %d", i-1);
            goto bail;
        }
        name = Jsi_JsonGetTokstr(p, json, i, &len);
        Jsi_DSSetLength(&nStr, 0);
        Jsi_DSAppendLen(&nStr, name, len);
        name = Jsi_DSValue(&nStr);
        i++;
        switch (p->tokens[i].type) {
            case JSI_JTYPE_PRIMITIVE:
                t = Jsi_JsonGetTokstr(p, json, i, &len);
                if ((len == 4 && Jsi_Strncmp(t, "true", len)==0) || (len == 5 && Jsi_Strncmp(t, "false", len)==0)) {
                    Jsi_ValueMakeBool(interp, &v, (bool)(len==4?1:0));
                } else if (len == 4 && Jsi_Strncmp(t, "null", len)==0) {
                    Jsi_ValueMakeNull(interp, &v);
                } else {
                    char *ep;
                    Jsi_Number d = strtod(t,&ep);
                    if (ep>(t+len)) {
                        result = Jsi_LogError("bad number %*s", len, t);
                        goto bail;
                    }
                    Jsi_ValueMakeNumber(interp, &v, d);
                }
                break;
            case JSI_JTYPE_STRING:
                t = Jsi_JsonGetTokstr(p, json, i, &len);
                Jsi_DString dStr;
                Jsi_DSInit(&dStr);
                jsonNewDString(interp, &dStr, t, len);
                Jsi_ValueMakeStringKey(interp, &v, Jsi_DSValue(&dStr));
                Jsi_DSFree(&dStr);
                break;
            case JSI_JTYPE_ARRAY:
            case JSI_JTYPE_OBJECT:
            default:
                result = -1;
                goto bail;
                break;
        }
        result++;
        rc = Jsi_OptionsSet(interp, opts, data, name, v, 0);
        if (rc == JSI_OK)
            continue;

bail:
        Jsi_LogError("bad at field: %s", name);
        result = -1;
        break;
    }

done:
    Jsi_DSFree(&nStr);
    if (v)
        Jsi_DecrRefCount(interp, v);
    Jsi_JsonFree(&pp);
    return result;
}

Project: jsish

Jsi_RC Jsi_ValueToNumber(Jsi_Interp *interp, Jsi_Value *v)
{
    if (v->vt == JSI_VT_NUMBER) return JSI_OK;
    Jsi_Number a = Jsi_ValueToNumberInt(interp, v, 0);
    Jsi_ValueReset(interp, &v);
    Jsi_ValueMakeNumber(interp, &v, a);
    return JSI_OK;
}

Project: jsish

static Jsi_RC jsi_ValueToVerify(Jsi_Interp *interp, Jsi_OptionSpec* spec, Jsi_Value *inValue, const char *inStr, void *record, Jsi_Wide flags)
{
    if (inStr)
        return JSI_ERROR;
    Jsi_Value **s = (Jsi_Value**)((char*)record) + spec->offset;
    Jsi_ValueHandlerProc *vfunc = (Jsi_ValueHandlerProc*)spec->data;
    if (!vfunc) 
        return Jsi_LogError("custom value spec did not set data: %s", spec->name);
    if (!inValue) {
        if (*s)
            Jsi_DecrRefCount(interp, *s);
        *s = NULL;
        return JSI_OK;
    }
    if (vfunc(interp, inValue, spec, record) != JSI_OK)
        return JSI_ERROR;
    *s = inValue;
    if (*s)
        Jsi_IncrRefCount(interp, *s);
    return JSI_OK;
}

Project: jsish

void *Jsi_FunctionPrivData(Jsi_Func *funcPtr)
{
    return funcPtr->privData;
}

Project: jsish

Jsi_Mutex* Jsi_MutexNew(Jsi_Interp *interp, int timeout, int flags) {
     Jsi_Mutex *mtx = (Jsi_Mutex *)Jsi_Calloc(1,sizeof(Jsi_Mutex));
     mtx->lockTimeout = timeout;
     mtx->flags = flags;
     MutexInit(mtx);
     return mtx;
}

Project: jsish

static Jsi_RC FilesysModeCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    UdfGet(udf, _this, funcPtr);
    if (udf->mode)
        Jsi_ValueMakeStringKey(interp, ret, udf->mode);
    return JSI_OK;
}

Project: jsish

Jsi_RC Jsi_PathNormalize(Jsi_Interp *interp, Jsi_Value **pathPtr) {
    Jsi_Value *path = *pathPtr;
    if (!path) {
        Jsi_DString dStr = {};
        *pathPtr = Jsi_ValueNewStringDup(interp, Jsi_GetCwd(interp, &dStr));
        Jsi_IncrRefCount(interp, *pathPtr);
        Jsi_DSFree(&dStr);
    } else {
        const char *rn = Jsi_Realpath(interp, path, NULL);
        if (!rn) return JSI_ERROR;
        Jsi_DecrRefCount(interp, *pathPtr);
        *pathPtr = Jsi_ValueNewString(interp, rn, -1);
        Jsi_IncrRefCount(interp, *pathPtr);
    }
    return JSI_OK;
}

Project: jsish

HashString( const char *string)
{
    jsi_Hash result = 0;
    jsi_Hash c;
    while ((c = *string++) != 0)
        result += (result << 3) + c;

    return (jsi_Hash)result;
}

Project: jsish

static Jsi_RC MutexLock(Jsi_Interp *interp, Jsi_Mutex* mtx) {
    int timeout = mtx->lockTimeout;
    if (interp && timeout<0)
        timeout = interp->lockTimeout;
    if (timeout<=0)
        EnterCriticalSection(&mtx->mtx);
    else {
        int cnt = timeout;
        while (cnt-- >= 0) {
            if (TryEnterCriticalSection(&mtx->mtx))
                return JSI_OK;
            usleep(1000);
        }
        Jsi_LogError("lock timed out");
        if (interp)
            interp->threadErrCnt++;
        mtx->threadErrCnt++;
        return JSI_ERROR;
    }
    return JSI_OK;
}

Project: jsish

static Jsi_OpCodes *code_jfalse_np(int off) { JSI_NEW_CODES(0,OP_JFALSE_NP, off); }

Project: jsish

static Jsi_RC jsiEvalOp(Jsi_Interp* interp, jsi_Pstate *ps, char *program,
                       jsi_ScopeChain *scope, Jsi_Value *currentScope, Jsi_Value *_this, Jsi_Value **ret)
{
    Jsi_RC r = JSI_OK;
    jsi_Pstate *newps = jsiNewParser(interp, program, NULL, 1);
    if (newps) {
        int oef = newps->eval_flag;
        newps->eval_flag = 1;
        interp->ps = newps;
        r = jsi_evalcode(newps, NULL, newps->opcodes, scope, currentScope, _this, ret);
        if (r) {
            Jsi_ValueDup2(interp, &ps->last_exception, newps->last_exception);
        }
        newps->eval_flag = oef;
        interp->ps = ps;
    } else  {
        //Jsi_ValueMakeStringKey(interp, &ps->last_exception, "Syntax Error");
        r = JSI_ERROR;
    }
    return r;
}

Project: jsish

static void dbPrepStmtFree( Jsi_Db *jdb, SqlPreparedStmt *prep)
{
    if (prep->deleting)
        return;
    prep->deleting = 1;
    if (prep->pStmt)
        sqlite3_finalize( prep->pStmt );
    if (prep->entry) {
        Jsi_HashEntry *hPtr = prep->entry;
        prep->entry = NULL;
        Jsi_HashEntryDelete(hPtr);
    }
    if (prep->elPtr)
        Jsi_ListEntryDelete(prep->elPtr);
    Jsi_Free( (char*)prep );
    jdb->stmtCacheCnt--;
}

Project: jsish

static Jsi_OpCodes *code_notequal() { JSI_NEW_CODES(0,OP_NOTEQUAL, 0); }

Project: jsish

static Jsi_RC FilesysReadCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    int  sum = 0, n;
    Jsi_DString dStr;
    Jsi_DSInit(&dStr);
    UdfGet(udf, _this, funcPtr);
    char buf[JSI_BUFSIZ];
    int argc = Jsi_ValueGetLength(interp, args);
    int nsiz = -1, cnt = 0, rsiz;
    
    if (!udf->filename) {
        goto bail;
    }
    if (argc>0 && Jsi_GetIntFromValue(interp, Jsi_ValueArrayIndex(interp, args, 0), &nsiz) != JSI_OK)
        goto bail;
    while (cnt++ < MAX_LOOP_COUNT) {
        rsiz = sizeof(buf);
        if (nsiz>0 && ((sum+rsiz)>nsiz))
            rsiz = (nsiz-sum);
        if ((n = Jsi_Read(interp, udf->chan, buf, rsiz)) <= 0)
            break;
        Jsi_DSAppendLen(&dStr, buf, n);
        sum += n;
        if (nsiz>=0 && sum >=nsiz)
            break;
    }
    if (Jsi_DSLength(&dStr)>0)
        Jsi_ValueMakeDStringObject(interp, ret, &dStr);
    return JSI_OK;
    
bail:
    Jsi_DSFree(&dStr);
    Jsi_ValueMakeUndef(interp, ret);
    return JSI_OK;
}

Project: jsish

Jsi_RC Jsi_TreeKeysDump(Jsi_Interp *interp, Jsi_Tree *tablePtr, Jsi_Value **ret, int flags) {
    char *key;
    int n = 0;
    Jsi_TreeEntry *hPtr;
    Jsi_TreeSearch search;
    Jsi_Obj *nobj;
    Jsi_MapFmtKeyProc* fmtKeyProc = tablePtr->opts.fmtKeyProc;
    
    if (!fmtKeyProc && tablePtr->keyType == JSI_KEYS_ONEWORD && flags!=JSI_KEYS_ONEWORD )
        fmtKeyProc = jsi_treeFmtKey;
        
    if (!fmtKeyProc && tablePtr->keyType >= JSI_KEYS_STRUCT_MINSIZE) 
        return Jsi_LogError("Can not dump struct tree");
    nobj = Jsi_ObjNew(interp);
    Jsi_ValueMakeArrayObject(interp, ret, nobj);
    for (hPtr = Jsi_TreeSearchFirst(tablePtr, &search, flags, NULL);
        hPtr != NULL; hPtr = Jsi_TreeSearchNext(&search)) {
        key = (char*)Jsi_TreeKeyGet(hPtr);
        Jsi_Value *val;
        if (fmtKeyProc) {
            val = (*fmtKeyProc)((Jsi_MapEntry*)hPtr, &tablePtr->opts, flags);
            if (!val) {
                Jsi_LogError("key format failed");
                Jsi_ValueMakeUndef(interp, ret);
                return JSI_ERROR;
            }
        } else if (tablePtr->keyType == JSI_KEYS_ONEWORD)
            val = Jsi_ValueNewNumber(interp, (Jsi_Number)(uintptr_t)key);
        else
            val = Jsi_ValueNewStringKey(interp, key);
        Jsi_ObjArraySet(interp, nobj, val, n++);
        if (fmtKeyProc && val->refCnt>1) //TODO: Hmmm. for StructKey Jsi_OptionsDump() returns refCnt=1
            Jsi_DecrRefCount(interp, val);
    }
    Jsi_TreeSearchDone(&search);
    return JSI_OK;
}

Project: jsish

int Jsi_Remove(Jsi_Interp *interp, Jsi_Value* path, int flags) {
    void *data;
    Jsi_Filesystem *fsPtr = Jsi_FilesystemForPath(interp, path, &data);
    if (fsPtr == NULL || !fsPtr->removeProc) return -1;
    return fsPtr->removeProc(interp, path, flags);
}

Project: jsish

const char* Jsi_ValueGetDString(Jsi_Interp *interp, Jsi_Value* v, Jsi_DString *dStr, int quote)
{
    objwalker ow;
    ow.quote = quote;
    ow.depth = 0;
    ow.dStr = dStr;
    jsiValueGetString(interp, v, dStr, &ow);
    return Jsi_DSValue(dStr);
}

Project: jsish

static void jsiSqlFunc(sqlite3_context *context, int argc, sqlite3_value**argv) {
    SqlFunc *p = (SqlFunc*)sqlite3_user_data(context);
    int i;
    int rc;
    Jsi_Interp *interp = p->interp;
    Jsi_Value *vpargs, *itemsStatic[100], **items = itemsStatic, *ret;
    if (argc>100)
        items = (Jsi_Value**)Jsi_Calloc(argc, sizeof(Jsi_Value*));

    for(i=0; i<argc; i++) {
        items[i] = dbGetValueGet(interp, argv[i]);
    }
    vpargs = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, items, argc, 0));
    Jsi_IncrRefCount(interp, vpargs);
    ret = Jsi_ValueNew1(interp);
    rc = Jsi_FunctionInvoke(interp, p->tocall, vpargs, &ret, NULL);
    Jsi_DecrRefCount(interp, vpargs);
    if (items != itemsStatic)
        Jsi_Free(items);

    bool b;
    if( rc != JSI_OK) {
        char buf[JSI_BUFSIZ];
        snprintf(buf, sizeof(buf), "error in function: %.200s", p->zName);
        sqlite3_result_error(context, buf, -1);

    } else if (Jsi_ValueIsBoolean(interp, ret)) {
        Jsi_GetBoolFromValue(interp, ret, &b);
        sqlite3_result_int(context, b);
    } else if (Jsi_ValueIsNumber(interp, ret)) {
        Jsi_Number d;
        // if (Jsi_GetIntFromValueBase(interp, ret, &i, 0, JSI_NO_ERRMSG);
        // sqlite3_result_int64(context, v);
        Jsi_GetNumberFromValue(interp, ret, &d);
        sqlite3_result_double(context, (double)d);
    } else {
        const char * data;
        if (!(data = Jsi_ValueGetStringLen(interp, ret, &i))) {
            //TODO: handle objects???
            data = Jsi_ValueToString(interp, ret, NULL);
            i = Jsi_Strlen(data);
        }
        sqlite3_result_text(context, (char *)data, i, SQLITE_TRANSIENT );
    }
    Jsi_DecrRefCount(interp, ret);
}

Project: jsish

Jsi_Mutex* Jsi_MutexNew(Jsi_Interp *interp, int timeout, int flags) { return NULL; }

Project: jsish

static Jsi_Value *jsi_treeFmtKey(Jsi_MapEntry* h, struct Jsi_MapOpts *opts, int flags)
{
    Jsi_TreeEntry* hPtr = (Jsi_TreeEntry*)h;
    void *key = Jsi_TreeKeyGet(hPtr);
    if (opts->keyType == JSI_KEYS_ONEWORD)
        return Jsi_ValueNewNumber(opts->interp, (Jsi_Number)(intptr_t)key);
    char nbuf[JSI_MAX_NUMBER_STRING];
    snprintf(nbuf, sizeof(nbuf), "%p", key);
    return Jsi_ValueNewStringDup(opts->interp, nbuf);
}

Project: jsish

static int jsi_FSGetcProc(Jsi_Channel chan) {
    return fgetc(_JSI_GETFP(chan,1));
}

Project: jsish

static Jsi_RC WebSocketHandlerCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    jsi_wsCmdObj *cmdPtr = (jsi_wsCmdObj*)Jsi_UserObjGetData(interp, _this, funcPtr);
    Jsi_HashEntry *hPtr;
    jsi_wsHander *hdlPtr;
    if (!cmdPtr)
        return Jsi_LogError("Apply in a non-websock object");
    WSSIGASSERT(cmdPtr, OBJ);
    int argc = Jsi_ValueGetLength(interp, args);
    if (argc == 0) {
        Jsi_HashSearch search;
        Jsi_Obj* obj = Jsi_ObjNew(interp);
        for (hPtr = Jsi_HashSearchFirst(cmdPtr->handlers, &search); hPtr; hPtr = Jsi_HashSearchNext(&search)) {
            const char *key = (char*)Jsi_HashKeyGet(hPtr);
            Jsi_Value *val = (Jsi_Value*)Jsi_HashValueGet(hPtr);
            Jsi_ObjInsert(interp, obj, key, val, 0);
        }
        Jsi_ValueMakeObject(interp, ret, obj);
        return JSI_OK;
    }
    if (argc == 1) {
        hPtr = Jsi_HashEntryFind(cmdPtr->handlers, Jsi_ValueArrayIndexToStr(interp, args, 0, NULL));
        if (!hPtr)
            return JSI_OK;
        hdlPtr = (jsi_wsHander*)Jsi_HashValueGet(hPtr);
        Jsi_ValueReplace(interp, ret, hdlPtr->val);
        return JSI_OK;
    }
    const char *key = Jsi_ValueArrayIndexToStr(interp, args, 0, NULL);
    Jsi_Value *valPtr = Jsi_ValueArrayIndex(interp, args, 1);
    if (Jsi_ValueIsNull(interp, valPtr)) {
        hPtr = Jsi_HashEntryFind(cmdPtr->handlers, key);
        if (!hPtr)
            return JSI_OK;
        hdlPtr = (jsi_wsHander*)Jsi_HashValueGet(hPtr);
        if (hdlPtr->val)
            Jsi_DecrRefCount(interp, hdlPtr->val);
        Jsi_HashValueSet(hPtr, NULL);
        Jsi_HashEntryDelete(hPtr);
        Jsi_Free(hdlPtr);
        Jsi_ValueMakeStringDup(interp, ret, key);
        return JSI_OK;
    }
    if (Jsi_ValueIsFunction(interp, valPtr)==0 && Jsi_ValueIsString(interp, valPtr)==0)
        return Jsi_LogError("expected string, function or null");
    Jsi_Value *argPtr = Jsi_ValueArrayIndex(interp, args, 2);
    if (argPtr) {
        if (Jsi_ValueIsNull(interp, argPtr))
            argPtr = NULL;
        else if (!Jsi_ValueIsString(interp, argPtr))
            return Jsi_LogError("expected a string");
    }
    hPtr = Jsi_HashEntryNew(cmdPtr->handlers, key, NULL);
    if (!hPtr)
        return JSI_ERROR;
    hdlPtr = (jsi_wsHander *)Jsi_Calloc(1, sizeof(*hdlPtr));
    Jsi_Value *flagPtr = Jsi_ValueArrayIndex(interp, args, 1);
    Jsi_Number fl = 0;
    if (flagPtr && Jsi_ValueIsNumber(interp, flagPtr))
        Jsi_ValueGetNumber(interp, flagPtr, &fl);
    hdlPtr->val = valPtr;
    hdlPtr->flags = fl;
    Jsi_HashValueSet(hPtr, hdlPtr);
    Jsi_IncrRefCount(interp, valPtr);
    return JSI_OK;
}

Project: jsish

Jsi_Value * jsi_ValueNew(Jsi_Interp *interp, const char *fname, int line, const char *func) {
    Jsi_Value *v = ValueNew(interp);
    jsi_ValueDebugUpdate(interp, v, valueDebugTbl, fname, line, func);
    return v;
}

Project: jsish

Jsi_RC jsi_evalcode_sub(jsi_Pstate *ps, Jsi_OpCodes *opcodes, 
     jsi_ScopeChain *scope, Jsi_Value *currentScope,
     Jsi_Value *_this, Jsi_Value *vret)
{
    Jsi_Interp* interp = ps->interp;
    jsi_OpCode *ip = &opcodes->codes[0];
    Jsi_RC rc = JSI_OK;
    int curLine = 0;
    int context_id = ps->_context_id++, lop = -1;
    jsi_OpCode *end = &opcodes->codes[opcodes->code_len];
    jsi_TryList  *trylist = NULL;
    bool strict = interp->strict;
    const char *curFile = NULL;
    
    if (currentScope->vt != JSI_VT_OBJECT) {
        Jsi_LogBug("Eval: current scope is not a object");
        return JSI_ERROR;
    }
    if (interp->maxDepth>0 && interp->level > interp->maxDepth)
        rc = Jsi_LogError("Exceeded call depth: %d", interp->level);
    
    while(ip < end && rc == JSI_OK) {
        int plop = ip->op;

        if (ip->logflag) { // Mask out LogDebug, etc if not enabled.
            interp->curIp = ip;
            switch (ip->logflag) {
                case jsi_Oplf_assert:
                    if (!interp->asserts) {
                        ip++;
                        if (ip->logflag != jsi_Oplf_assert && (ip->op == OP_POP || ip->op == OP_RET))
                            ip++;
                        continue;
                    }
                    break;
                case jsi_Oplf_debug:
                    if (!interp->logOpts.Debug && !(interp->framePtr->logflag &(1<<jsi_Oplf_debug))) {
                        ip++;
                        if (ip->logflag != jsi_Oplf_debug && (ip->op == OP_POP || ip->op == OP_RET))
                            ip++;
                        continue;
                    }
                    break;
                case jsi_Oplf_test:
                    if (!interp->logOpts.Test && !(interp->framePtr->logflag &(1<<jsi_Oplf_test))) {
                        ip++;
                        if (ip->logflag != jsi_Oplf_test && (ip->op == OP_POP || ip->op == OP_RET))
                            ip++;
                        continue;
                    }
                    break;
                case jsi_Oplf_trace:
                    if (!interp->logOpts.Trace && !(interp->framePtr->logflag &(1<<jsi_Oplf_trace))) {
                        ip++;
                        if (ip->logflag != jsi_Oplf_trace && (ip->op == OP_POP || ip->op == OP_RET))
                            ip++;
                        continue;
                    }
                    break;
                default:
                    break;
            }
        }
        if (interp->interrupted) {
            if (!interp->framePtr->tryDepth) {
                Jsi_LogError("program interrupted: function=%s", interp->framePtr->funcName);
                interp->interrupted = 0;
            } else {
                interp->interrupted++;
            }
            rc = JSI_ERROR;
            break;
        }
        if (interp->exited) {
            rc = JSI_ERROR;
            break;
        }
        interp->opCnt++;
        if (interp->maxOpCnt && interp->opCnt > interp->maxOpCnt) {
            puts("EXEC CAP EXCEED");
            interp->maxOpCnt += 1000;
            rc = Jsi_LogError("Exceeded execution cap: %d", interp->opCnt);
            interp->exited = 1;
            interp->exitCode = 99;
            break;
        }
        if (interp->traceOp) {
            jsiDumpInstr(interp, ps, _this, trylist, ip, opcodes);
        }
        if (interp->parent && interp->busyCallback && (interp->opCnt%(interp->busyInterval<=0?100000:interp->busyInterval))==0) {
            // Handle parent interp events.
            if (jsi_FuncIsNoop(interp, interp->busyCallback))
                Jsi_EventProcess(interp->parent, -1);
            else {
                Jsi_DString nStr;
                Jsi_DSInit(&nStr);
                Jsi_DSPrintf(&nStr, "[\"#Interp_%d\", %d]", interp->objId, interp->opCnt);//TODO: use actual time interval rather than opCnt.
                if (Jsi_FunctionInvokeJSON(interp->parent, interp->busyCallback, Jsi_DSValue(&nStr), NULL) != JSI_OK)
                    rc = JSI_ERROR;
                Jsi_DSFree(&nStr);
            }
        }
        ip->hit=1;
#ifndef USE_STATIC_STACK
        if ((interp->maxStack-interp->framePtr->Sp)<STACK_MIN_PAD)
            jsiSetupStack(interp);
#endif
        jsiPush(interp,0);
        interp->curIp = ip;
        // Carry forward line/file info from previous OPs.
        if (!ip->Line)
            ip->Line = curLine;
        else
            curLine = ip->Line;
        if (!ip->fname)
            ip->fname = curFile;
        else
            curFile = ip->fname;
        if (interp->debugOpts.hook) {
            interp->framePtr->fileName = curFile;
            interp->framePtr->line = curLine;
            if ((rc = (*interp->debugOpts.hook)(interp, curFile, curLine, interp->framePtr->level, interp->curFunction, jsi_opcode_string(ip->op), ip, NULL)) != JSI_OK)
                break;
        }

        switch(ip->op) {
            case OP_NOP:
            case OP_LASTOP:
                break;
            case OP_PUSHUND:
                Jsi_ValueMakeUndef(interp, &_jsi_STACKIDX(interp->framePtr->Sp));
                jsiPush(interp,1);
                break;
            case OP_PUSHNULL:
                Jsi_ValueMakeNull(interp, &_jsi_STACKIDX(interp->framePtr->Sp));
                jsiPush(interp,1);
                break;
            case OP_PUSHBOO:
                Jsi_ValueMakeBool(interp, &_jsi_STACKIDX(interp->framePtr->Sp), (uintptr_t)ip->data);
                jsiPush(interp,1);
                break;
            case OP_PUSHNUM:
                Jsi_ValueMakeNumber(interp, &_jsi_STACKIDX(interp->framePtr->Sp), (*((Jsi_Number *)ip->data)));
                jsiPush(interp,1);
                break;
            case OP_PUSHSTR: {
                Jsi_Value **v = &_jsi_STACKIDX(interp->framePtr->Sp);
                Jsi_ValueMakeStringKey(interp, v, (char*)ip->data);
                interp->lastPushStr = Jsi_ValueString(interp, *v, NULL);
                jsiPush(interp,1);
                break;
            }
            case OP_PUSHVSTR: {
                Jsi_String *s = (Jsi_String *)ip->data;
                Jsi_Value **v = &_jsi_STACKIDX(interp->framePtr->Sp);
                if (s->flags&1)
                    jsi_ValueMakeBlobDup(interp,v, (uchar*)s->str, s->len);
                else {
                    Jsi_ValueMakeStringKey(interp, v, s->str);
                    interp->lastPushStr = s->str;
                }
                jsiPush(interp,1);
                break;
            }
            case OP_PUSHVAR: {
                rc = jsi_PushVar(ps, ip, scope, currentScope, context_id);      
                break;
            }
            case OP_PUSHFUN: {
                jsi_PushFunc(ps, ip, scope, currentScope);
                break;
            }
            case OP_NEWFCALL:
                if (interp->maxUserObjs && interp->userObjCnt > interp->maxUserObjs) {
                    rc = Jsi_LogError("Max 'new' count exceeded");
                    break;
                }
            case OP_FCALL: {
                /* TODO: need reliable way to capture func string name to handle unknown functions.*/
                int discard = ((ip+1)<end && ip[1].op == OP_POP);
                switch (jsiEvalFunction(ps, ip, discard)) {        /* throw an execption */
                    case JSI_OK: break;
                    case JSI_BREAK:
                        if (interp->framePtr->tryDepth<=0)
                            interp->isHelp = 1;
                        JSI_DO_THROW("help");
                        break;
                    default:  
                        JSI_DO_THROW("fcall");
                }
                strict = interp->strict;
                /* TODO: new Function return a function without scopechain, add here */
                break;
            }
            case OP_SUBSCRIPT: {
                rc = jsi_evalSubscript(interp, _jsi_TOQ, _jsi_TOP, ip, end, currentScope);
                break;
            }
            case OP_ASSIGN: {
                Jsi_Value *sval = _jsi_TOP, *dval = _jsi_TOQ;
                bool globThis = (sval->vt == JSI_VT_OBJECT && sval->d.obj == interp->csc->d.obj);
                if ((uintptr_t)ip->data == 1) {
                    jsiVarDeref(interp,1);
                    rc = jsi_ValueAssign(interp, dval, sval, lop);                    
                    if (strict && sval->vt == JSI_VT_UNDEF)
                        rc = jsi_ValueAssignCheck(interp, sval, lop);
                    jsiPop(interp,1);
                } else {
                    jsiVarDeref(interp, 3);
                    Jsi_Value *v3 = _jsi_STACKIDX(interp->framePtr->Sp-3);
                    if (v3->vt == JSI_VT_OBJECT) {
                        if (strict && sval->vt == JSI_VT_UNDEF)
                            rc = jsi_ValueAssignCheck(interp, sval, lop);
                        jsi_ValueObjKeyAssign(interp, v3, dval, sval, 0);
                        jsi_ValueDebugLabel(sval, "assign", NULL);
                    } else if (strict)
                        rc = Jsi_LogError("assign to a non-exist object");
                    jsiClearStack(interp,3);
                    Jsi_ValueCopy(interp,v3, sval);
                    dval = v3;
                    jsiPop(interp, 2);
                }
                if (interp->framePtr->level<=1 && globThis && rc == JSI_OK && dval && dval->vt == JSI_VT_VARIABLE) {
                    dval = dval->d.lval;
                    //printf("GLOBAL THIS: %p\n", dval);
                    Jsi_HashSet(interp->genValueTbl, dval, dval);
                }
                break;
            }
            case OP_PUSHREG: {
                Jsi_Obj *obj = Jsi_ObjNewType(interp, JSI_OT_REGEXP);
                obj->d.robj = (Jsi_Regex *)ip->data;
                Jsi_ValueMakeObject(interp, &_jsi_STACKIDX(interp->framePtr->Sp), obj);
                jsiPush(interp,1);
                break;
            }
            case OP_PUSHARG:
                //Jsi_ValueCopy(interp,_jsi_STACKIDX(interp->framePtr->Sp), currentScope);
                
                if (!interp->framePtr->arguments) {
                    interp->framePtr->arguments = Jsi_ValueNewObj(interp,
                        Jsi_ObjNewArray(interp, currentScope->d.obj->arr, currentScope->d.obj->arrCnt, 0));
                    Jsi_IncrRefCount(interp, interp->framePtr->arguments);
                    if (interp->hasCallee) {
                        Jsi_Value *callee = Jsi_ValueObjLookup(interp, currentScope, "\1callee\1", 0);
                        if (callee)
                            Jsi_ValueInsert(interp, interp->framePtr->arguments, "\1callee\1", callee, JSI_OM_DONTENUM);
                    }
                    // interp->framePtr->arguments->d.obj->__proto__ = interp->Object_prototype; // ecma
                }
                Jsi_ValueCopy(interp,_jsi_STACKIDX(interp->framePtr->Sp), interp->framePtr->arguments);
                jsiPush(interp,1);
                break;
            case OP_PUSHTHS: { //TODO: Value copy can cause memory leak!
                Jsi_Value *tval = _jsi_STACKIDX(interp->framePtr->Sp);
                Jsi_ValueCopy(interp, tval, _this);
                /*if (interp->csc == _this)
                    Jsi_ValueDup2(interp, &tval, _this);
                else
                    Jsi_ValueCopy(interp, tval, _this);*/
                jsiPush(interp,1);
                break;
            }
            case OP_PUSHTOP:
                Jsi_ValueCopy(interp,_jsi_STACKIDX(interp->framePtr->Sp), _jsi_TOP);
                jsiPush(interp,1);
                break;
            case OP_UNREF:
                jsiVarDeref(interp,1);
                break;
            case OP_PUSHTOP2: {
                Jsi_Value *vp1 = _jsi_STACKIDX(interp->framePtr->Sp);
                Jsi_Value *vp2 = _jsi_STACKIDX(interp->framePtr->Sp+1);
                if (!vp1 || !vp2)
                    rc = Jsi_LogError("Invalid lookup/push");
                else {
                    Jsi_ValueCopy(interp, vp1, _jsi_TOQ);
                    Jsi_ValueCopy(interp, vp2, _jsi_TOP);
                    jsiPush(interp, 2);
                }
                break;
            }
            case OP_CHTHIS: {
                if (ip->data) {
                    int t = interp->framePtr->Sp - 2;
                    Assert(t>=0);
                    Jsi_Value *v = _jsi_THISIDX(t);
                    jsiClearThis(interp, t);
                    Jsi_ValueCopy(interp, v, _jsi_TOQ);
                    if (v->vt == JSI_VT_VARIABLE) {
                        Jsi_ValueCopy(interp, v, v->d.lval);
                    }
                    Jsi_ValueToObject(interp, v);
                }
                break;
            }
            case OP_LOCAL: {
                Jsi_Value key = VALINIT, *kPtr = &key; // Note we use a string key so no reset needed.
                Jsi_ValueMakeStringKey(interp, &kPtr, (char*)ip->data);
                jsi_ValueObjKeyAssign(interp, currentScope, kPtr, NULL, JSI_OM_DONTENUM);
                context_id = ps->_context_id++;
                break;
            }
            case OP_POP: {
                Jsi_Value *tval = _jsi_TOP;
                if ((interp->evalFlags&JSI_EVAL_RETURN) && (ip+1) >= end && 
                (Jsi_ValueIsObjType(interp, tval, JSI_OT_ITER)==0 &&
                Jsi_ValueIsObjType(interp, tval, JSI_OT_FUNCTION)==0)) {
                    /* Interactive and last instruction is a pop: save result. */
                    Jsi_ValueMove(interp, vret, tval); /*TODO***: correct ***/
                    tval->vt = JSI_VT_UNDEF;
                }
                jsiPop(interp, (uintptr_t)ip->data);
                break;
            }
            case OP_NEG:
                jsiVarDeref(interp,1);
                Jsi_ValueToNumber(interp, _jsi_TOP);
                rc = _jsi_StrictChk(_jsi_TOP);
                _jsi_TOP->d.num = -(_jsi_TOP->d.num);
                break;
            case OP_POS:
                jsiVarDeref(interp,1);
                Jsi_ValueToNumber(interp, _jsi_TOP);
                rc = _jsi_StrictChk(_jsi_TOP);
                break;
            case OP_NOT: {
                int val = 0;
                jsiVarDeref(interp,1);
                
                val = Jsi_ValueIsTrue(interp, _jsi_TOP);
                
                jsiClearStack(interp,1);
                Jsi_ValueMakeBool(interp, &_jsi_TOP, !val);
                break;
            }
            case OP_BNOT: {
                jsiVarDeref(interp,1);
                jsi_ValueToOInt32(interp, _jsi_TOP);
                rc = _jsi_StrictChk(_jsi_TOP);
                _jsi_TOP->d.num = (Jsi_Number)(~((int)_jsi_TOP->d.num));
                break;
            }
            case OP_ADD: {
                jsiVarDeref(interp,2);
                Jsi_Value *v, *v1 = _jsi_TOP, *v2 = _jsi_TOQ;
                int l1, l2;
                if (strict)
                    if (Jsi_ValueIsUndef(interp, v1) || Jsi_ValueIsUndef(interp, v2)) {
                        rc = Jsi_LogError("operand value to + is undefined");
                        break;
                    }
                char *s1 = Jsi_ValueString(interp, v1, &l1);
                char *s2 = Jsi_ValueString(interp, v2, &l2);
                if (s1 || s2) {
                    char *str;
                    if (!(s1 && s2)) {
                        v = (s1 ? v2 : v1);
                        jsi_ValueToPrimitive(interp, &v);
                        Jsi_ValueToString(interp, v, NULL);
                        str = Jsi_ValueString(interp, v, (s1?&l2:&l1));
                        if (s1) s2 = str; else s1 = str;
                    }
                    Assert(l1>=0 && l1<=JSI_MAX_ALLOC_BUF);
                    Assert(l2>=0 && l2<=JSI_MAX_ALLOC_BUF);
                    str = (char*)Jsi_Malloc(l1+l2+1);
                    memcpy(str, s2, l2);
                    memcpy(str+l2, s1, l1);
                    str[l1+l2] = 0;
                    jsiClearStack(interp,2);
                    Jsi_ValueMakeBlob(interp, &v2, (uchar*)str, l1+l2);
                } else {
                    Jsi_ValueToNumber(interp, v1);
                    Jsi_ValueToNumber(interp, v2);
                    rc = _jsi_StrictChk2(v1, v2);
                    Jsi_Number n = v1->d.num + v2->d.num;
                    jsiClearStack(interp,2);
                    Jsi_ValueMakeNumber(interp, &v2, n);
                }
                jsiPop(interp,1);
                break;
            }
            case OP_IN: {
                Jsi_Value *v, *vl;
                const char *cp = NULL;
                Jsi_Number nval;
                jsiVarDeref(interp,2);
                vl = _jsi_TOQ;
                v = _jsi_TOP;
                if (Jsi_ValueIsString(interp,vl))
                    cp = Jsi_ValueGetStringLen(interp, vl, NULL);
                else if (Jsi_ValueIsNumber(interp,vl))
                    Jsi_ValueGetNumber(interp, vl, &nval);
                else {
                    if (strict)
                        Jsi_LogWarn("expected string or number before IN");
                    Jsi_ValueMakeBool(interp, &_jsi_TOQ, 0);
                    jsiPop(interp,1);
                    break;
                }
                
                if (v->vt == JSI_VT_VARIABLE) {
                    v = v->d.lval;
                    SIGASSERT(v, VALUE);
                }
                if (v->vt != JSI_VT_OBJECT || v->d.obj->ot != JSI_OT_OBJECT) {
                    if (strict)
                        Jsi_LogWarn("expected object after IN");
                    Jsi_ValueMakeBool(interp, &_jsi_TOQ, 0);
                    jsiPop(interp,1);
                    break;
                }
                int bval = 0;
                char nbuf[JSI_MAX_NUMBER_STRING];
                Jsi_Value *vv;
                Jsi_Obj *obj = v->d.obj;
                if (!cp) {
                    snprintf(nbuf, sizeof(nbuf), "%d", (int)nval);
                    cp = nbuf;
                }
                if (obj->arr) {
                    vv = jsi_ObjArrayLookup(interp, obj, (char*)cp);
                } else {
                    vv = Jsi_TreeObjGetValue(obj, (char*)cp, 1);
                }
                bval = (vv != 0);
                Jsi_ValueMakeBool(interp, &_jsi_TOQ, bval);
                jsiPop(interp,1);
                break;
            }
            case OP_SUB: 
                common_math_opr(-); break;
            case OP_MUL:
                common_math_opr(*); break;
            case OP_DIV:
                common_math_opr(/); break;
            case OP_MOD: {
                jsiVarDeref(interp,2);
                if (!Jsi_ValueIsType(interp,_jsi_TOP, JSI_VT_NUMBER))
                    Jsi_ValueToNumber(interp, _jsi_TOP);
                if (!Jsi_ValueIsType(interp,_jsi_TOQ, JSI_VT_NUMBER))
                    Jsi_ValueToNumber(interp, _jsi_TOQ);
                rc = _jsi_StrictChk2(_jsi_TOP,_jsi_TOQ);
                if (rc == JSI_OK)
                    _jsi_TOQ->d.num = fmod(_jsi_TOQ->d.num, _jsi_TOP->d.num);
                jsiPop(interp,1);
                break;
            }
            case OP_LESS:
                jsiVarDeref(interp,2);
                rc = jsi_logic_less(interp,2,1);
                jsiPop(interp,1);
                break;
            case OP_GREATER:
                jsiVarDeref(interp,2);
                rc = jsi_logic_less(interp,1,2);
                jsiPop(interp,1);
                break;
            case OP_LESSEQU:
                jsiVarDeref(interp,2);
                rc = jsi_logic_less(interp,1,2);
                if (rc == JSI_OK)
                    _jsi_TOQ->d.val = !_jsi_TOQ->d.val;
                jsiPop(interp,1);
                break;
            case OP_GREATEREQU:
                jsiVarDeref(interp,2);
                rc = jsi_logic_less(interp,2,1);
                if (rc == JSI_OK)
                    _jsi_TOQ->d.val = !_jsi_TOQ->d.val;
                jsiPop(interp,1);
                break;
            case OP_EQUAL:
            case OP_NOTEQUAL: {
                jsiVarDeref(interp,2);
                int r = Jsi_ValueCmp(interp, _jsi_TOP, _jsi_TOQ, 0);
                r = (ip->op == OP_EQUAL ? !r : r);
                jsiClearStack(interp,2);
                Jsi_ValueMakeBool(interp, &_jsi_TOQ, r);
                jsiPop(interp,1);
                break;
            }
            case OP_STRICTEQU:
            case OP_STRICTNEQ: {
                int r = 0;
                jsiVarDeref(interp,2);
                rc = _jsi_StrictUChk3(_jsi_TOQ, _jsi_TOP);
                r = !Jsi_ValueIsEqual(interp, _jsi_TOP, _jsi_TOQ);
                r = (ip->op == OP_STRICTEQU ? !r : r);
                jsiClearStack(interp,2);
                Jsi_ValueMakeBool(interp, &_jsi_TOQ, r);
                jsiPop(interp,1);
                break;
            }
            case OP_BAND: 
                common_bitwise_opr(&); break;
            case OP_BOR:
                common_bitwise_opr(|); break;
            case OP_BXOR:
                common_bitwise_opr(^); break;
            case OP_SHF: {
                jsiVarDeref(interp,2);
                jsi_ValueToOInt32(interp, _jsi_TOQ);
                jsi_ValueToOInt32(interp, _jsi_TOP);
                int t1 = (int)_jsi_TOQ->d.num;
                int t2 = ((unsigned int)_jsi_TOP->d.num) & 0x1f;
                if (ip->data) {                 /* shift right */
                    if ((uintptr_t)ip->data == 2) {   /* unsigned shift */
                        unsigned int t3 = (unsigned int)t1;
                        t3 >>= t2;
                        Jsi_ValueMakeNumber(interp, &_jsi_TOQ, t3);
                    } else {
                        t1 >>= t2;
                        Jsi_ValueMakeNumber(interp, &_jsi_TOQ, t1);
                    }
                } else {
                    t1 <<= t2;
                    Jsi_ValueMakeNumber(interp, &_jsi_TOQ, t1);
                }
                jsiPop(interp,1);
                break;
            }
            case OP_KEY: {
                jsiVarDeref(interp,1);
                if (ip->isof && !Jsi_ValueIsArray(interp, _jsi_TOP)) {
                    rc = Jsi_LogError("operand not an array");
                    break;
                }
                if (_jsi_TOP->vt != JSI_VT_UNDEF && _jsi_TOP->vt != JSI_VT_NULL)
                    Jsi_ValueToObject(interp, _jsi_TOP);
                Jsi_Value *spret = Jsi_ValueNew1(interp);
                jsi_ValueObjGetKeys(interp, _jsi_TOP, spret, ip->isof);
                Jsi_ValueReplace(interp, _jsi_STACK+interp->framePtr->Sp, spret);  
                Jsi_DecrRefCount(interp, spret);  
                jsiPush(interp,1);
                break;
            }
            case OP_NEXT: {
                Jsi_Value *toq = _jsi_TOQ, *top = _jsi_TOP;
                if (toq->vt != JSI_VT_OBJECT || toq->d.obj->ot != JSI_OT_ITER) Jsi_LogBug("next: toq not a iter\n");
                if (top->vt != JSI_VT_VARIABLE) {
                    rc = Jsi_LogError ("invalid for/in left hand-side");
                    break;
                }
                if (strict && top->f.bits.local==0) {
                    const char *varname = "";
                    Jsi_Value *v = top->d.lval;
                    if (v->f.bits.lookupfailed)
                        varname = v->d.lookupFail;

                    rc = Jsi_LogError("function created global: \"%s\"", varname);
                    break;
                }
                
                Jsi_IterObj *io = toq->d.obj->d.iobj;
                if (io->iterCmd) {
                    io->iterCmd(io, top, _jsi_STACKIDX(interp->framePtr->Sp-3), io->iter++);
                } else {
                    while (io->iter < io->count) {
                        if (!io->isArrayList) {
                            if (Jsi_ValueKeyPresent(interp, _jsi_STACKIDX(interp->framePtr->Sp-3), io->keys[io->iter],1)) 
                                break;
                        } else {
                            while (io->cur < io->obj->arrCnt) {
                                if (io->obj->arr[io->cur]) break;
                                io->cur++;
                            }
                            if (io->cur >= io->obj->arrCnt) {
                                /* TODO: Is this really a bug??? */
                                /* Jsi_LogBug("NOT FOUND LIST ARRAY");*/
                                io->iter = io->count;
                                break;
                            } else if (io->obj->arr[io->cur]) {
                                io->cur++;
                                break;
                            }
                        }
                        io->iter++;
                    }
                    if (io->iter >= io->count) {
                        jsiClearStack(interp,1);
                        Jsi_ValueMakeNumber(interp, &_jsi_TOP, 0);
                    } else {
                        Jsi_Value **vPtr = &_jsi_TOP->d.lval, *v = *vPtr;
                        SIGASSERT(v, VALUE);
                        Jsi_ValueReset(interp, vPtr);
                        if (io->isArrayList) {
                            if (!io->isof)
                                Jsi_ValueMakeNumber(interp, &v, io->cur-1);
                            else if (!io->obj->arr[io->cur-1])
                                Jsi_ValueMakeNull(interp, &v);
                            else
                                Jsi_ValueCopy(interp, v, io->obj->arr[io->cur-1]);
                        } else
                            Jsi_ValueMakeStringKey(interp, &v, io->keys[io->iter]);
                        io->iter++;
                        
                        jsiClearStack(interp,1);
                        Jsi_ValueMakeNumber(interp, &_jsi_TOP, 1);
                    }
                    break;
                }
            }
            case OP_INC:
            case OP_DEC: {
                int inc = ip->op == OP_INC ? 1 : -1;
                
                if (_jsi_TOP->vt != JSI_VT_VARIABLE) {
                    rc = Jsi_LogError("operand not left value");
                    break;
                }
                Jsi_Value *v = _jsi_TOP->d.lval;
                SIGASSERT(v, VALUE);
                Jsi_ValueToNumber(interp, v);
                rc = _jsi_StrictChk(v);
                v->d.num += inc;
                    
                jsiVarDeref(interp,1);
                if (ip->data) {
                    _jsi_TOP->d.num -= inc;
                }
                break;
            }
            case OP_TYPEOF: {
                const char *typ;
                Jsi_Value *v = _jsi_TOP;
                if (v->vt == JSI_VT_VARIABLE) {
                    v = v->d.lval;
                    SIGASSERT(v, VALUE);
                }
                typ = Jsi_ValueTypeStr(interp, v);
                jsiVarDeref(interp,1);
                Jsi_ValueMakeStringKey(interp, &_jsi_TOP, (char*)typ);
                break;
            }
            case OP_INSTANCEOF: {

                jsiVarDeref(interp,2);
                int bval = Jsi_ValueInstanceOf(interp, _jsi_TOQ, _jsi_TOP);
                jsiPop(interp,1);
                Jsi_ValueMakeBool(interp, &_jsi_TOP, bval);
                break;
            }
            case OP_JTRUE:
            case OP_JFALSE: 
            case OP_JTRUE_NP:
            case OP_JFALSE_NP: {
                jsiVarDeref(interp,1);
                int off = (uintptr_t)ip->data - 1; 
                int r = Jsi_ValueIsTrue(interp, _jsi_TOP);
                
                if (ip->op == OP_JTRUE || ip->op == OP_JFALSE) jsiPop(interp,1);
                ip += ((ip->op == OP_JTRUE || ip->op == OP_JTRUE_NP) ^ r) ? 0 : off;
                break;
            }
            case OP_JMPPOP: 
                jsiPop(interp, ((jsi_JmpPopInfo *)ip->data)->topop);
            case OP_JMP: {
                int off = (ip->op == OP_JMP ? (uintptr_t)ip->data - 1
                            : (uintptr_t)((jsi_JmpPopInfo *)ip->data)->off - 1);

                while (1) {
                    if (trylist == NULL) break;
                    jsi_OpCode *tojmp = ip + off;

                    /* jmp out of a try block, should execute the finally block */
                    /* while jmp out a 'with' block, restore the scope */

                    if (trylist->type == jsi_TL_TRY) { 
                        if (tojmp >= trylist->d.td.tstart && tojmp < trylist->d.td.fend) break;
                        
                        if (ip >= trylist->d.td.tstart && ip < trylist->d.td.cend) {
                            trylist->d.td.last_op = jsi_LOP_JMP;
                            trylist->d.td.ld.tojmp = tojmp;
                            
                            ip = trylist->d.td.fstart - 1;
                            off = 0;
                            break;
                        } else if (ip >= trylist->d.td.fstart && ip < trylist->d.td.fend) {
                            pop_try(trylist);
                        } else Jsi_LogBug("jmp within a try, but not in its scope?");
                    } else {
                        /* with block */
                        
                        if (tojmp >= trylist->d.wd.wstart && tojmp < trylist->d.wd.wend) break;
                        
                        JSI_RESTORE_SCOPE();
                        pop_try(trylist);
                    }
                }
                
                ip += off;
                break;
            }
            case OP_EVAL: {
                int stackargc = (uintptr_t)ip->data;
                jsiVarDeref(interp, stackargc);

                int r = 0;
                Jsi_Value *spPtr = Jsi_ValueNew1(interp);
                if (stackargc > 0) {
                    if (_jsi_STACKIDX(interp->framePtr->Sp - stackargc)->vt == JSI_VT_UNDEF) {
                        Jsi_LogError("undefined value to eval()");
                        goto undef_eval;
                    }
                    int plen;
                    char *pro = Jsi_ValueString(interp, _jsi_STACKIDX(interp->framePtr->Sp - stackargc), &plen);
                    if (pro) {
                        pro = Jsi_StrdupLen(pro, plen);
                        r = jsiEvalOp(interp, ps, pro, scope, currentScope, _this, &spPtr);
                        Jsi_Free(pro);
                    } else {
                        Jsi_ValueCopy(interp, spPtr, _jsi_STACKIDX(interp->framePtr->Sp - stackargc));
                    }
                }
undef_eval:
                jsiPop(interp, stackargc);
                Jsi_ValueCopy(interp, _jsi_STACK[interp->framePtr->Sp], spPtr); /*TODO: is this correct?*/
                Jsi_DecrRefCount(interp, spPtr);
                jsiPush(interp,1);

                if (r) {
                    JSI_DO_THROW("eval");
                }
                break;
            }
            case OP_RET: {
                if (interp->framePtr->Sp>=1 && ip->data) {
                    jsiVarDeref(interp,1);
                    Jsi_ValueMove(interp, vret, _jsi_TOP);
                }
                jsiPop(interp, (uintptr_t)ip->data);
                interp->didReturn = 1;
                if (trylist) {
                    while (trylist) {
                        if (trylist->type == jsi_TL_TRY && trylist->inCatch)
                            JSI_RESTORE_SCOPE();
                        pop_try(trylist);
                    }
                    goto done;
                }
                ip = end;
                break;
            }
            case OP_DELETE: {
                int count = (uintptr_t)ip->data;
                if (count == 1) { // Non-standard.
                    if (_jsi_TOP->vt != JSI_VT_VARIABLE)
                        rc = Jsi_LogError("delete a right value");
                    else {
                        Jsi_Value **vPtr = &_jsi_TOP->d.lval, *v = *vPtr;
                        SIGASSERT(v, VALUE);
                        if (v->f.bits.dontdel) {
                            if (strict) rc = Jsi_LogWarn("delete not allowed");
                        } else if (v != currentScope) {
                            Jsi_ValueReset(interp,vPtr);     /* not allow to delete arguments */
                        }
                        else if (strict)
                            Jsi_LogWarn("Delete arguments");
                    }
                    jsiPop(interp,1);
                } else if (count == 2) {
                    jsiVarDeref(interp,2);
                    assert(interp->framePtr->Sp>=2);
                    if (strict) {
                        if (_jsi_TOQ->vt != JSI_VT_OBJECT) Jsi_LogWarn("delete non-object key, ignore");
                        if (_jsi_TOQ->d.obj == currentScope->d.obj) Jsi_LogWarn("Delete arguments");
                    }
                    jsi_ValueObjDelete(interp, _jsi_TOQ, _jsi_TOP, 0);
                    
                    jsiPop(interp,2);
                } else Jsi_LogBug("delete");
                break;
            }
            case OP_OBJECT: {
                int itemcount = (uintptr_t)ip->data;
                Assert(itemcount>=0);
                jsiVarDeref(interp, itemcount * 2);
                Jsi_Obj *obj = Jsi_ObjNewObj(interp, _jsi_STACK+(interp->framePtr->Sp-itemcount*2), itemcount*2);
                jsiPop(interp, itemcount * 2 - 1);       /* one left */
                jsiClearStack(interp,1);
                Jsi_ValueMakeObject(interp, &_jsi_TOP, obj);
                break;
            }
            case OP_ARRAY: {
                int itemcount = (uintptr_t)ip->data;
                Assert(itemcount>=0);
                jsiVarDeref(interp, itemcount);
                Jsi_Obj *obj = Jsi_ObjNewArray(interp, _jsi_STACK+(interp->framePtr->Sp-itemcount), itemcount, 1);
                jsiPop(interp, itemcount - 1);
                jsiClearStack(interp,1);
                Jsi_ValueMakeObject(interp, &_jsi_TOP, obj);
                break;
            }
            case OP_STRY: {
                jsi_TryInfo *ti = (jsi_TryInfo *)ip->data;
                jsi_TryList *n = jsi_trylist_new(jsi_TL_TRY, scope, currentScope);
                
                n->d.td.tstart = ip;                            /* make every thing pointed to right pos */
                n->d.td.tend = n->d.td.tstart + ti->trylen;
                n->d.td.cstart = n->d.td.tend + 1;
                n->d.td.cend = n->d.td.tend + ti->catchlen;
                n->d.td.fstart = n->d.td.cend + 1;
                n->d.td.fend = n->d.td.cend + ti->finallen;
                n->d.td.tsp = interp->framePtr->Sp;
                n->inCatch=0;
                n->inFinal=0;

                jsi_push_try(interp, &trylist, n);
                break;
            }
            case OP_ETRY: {             /* means nothing happen go to final */
                if (trylist == NULL || trylist->type != jsi_TL_TRY)
                    Jsi_LogBug("Unexpected ETRY opcode??");

                ip = trylist->d.td.fstart - 1;
                break;
            }
            case OP_SCATCH: {
                if (trylist == NULL || trylist->type != jsi_TL_TRY) 
                    Jsi_LogBug("Unexpected SCATCH opcode??");

                if (!ip->data) {
                    JSI_DO_THROW("catch");
                } else {
                    trylist->inCatch=1;
                    /* new scope and make var */
                    scope = jsi_ScopeChainDupNext(interp, scope, currentScope);
                    currentScope = jsi_ObjValueNew(interp);
                    interp->framePtr->ingsc = scope;  //TODO: changing frame
                    interp->framePtr->incsc = currentScope;
                    Jsi_IncrRefCount(interp, currentScope);
                    Jsi_Value *excpt = Jsi_ValueNew1(interp);
                    if (ps->last_exception && ps->last_exception->vt != JSI_VT_UNDEF) {
                        //TODO: fix test262 crash in freeValueTbl@jsiInterp.c:565 for last_exception which is
                        // freed in jsi_PstateFree@jsiPstate.c:251. Is this code the problem?
                        Jsi_Value *ple = ps->last_exception;
                        Jsi_ValueCopy(interp, excpt, ple);
                        Jsi_ValueReset(interp, &ps->last_exception);
                    } else if (interp->errMsgBuf[0]) {
                        Jsi_ValueMakeStringDup(interp, &excpt, interp->errMsgBuf);
                        interp->errMsgBuf[0] = 0;
                    }
                    Jsi_ValueInsert(interp, currentScope, (char*)ip->data, excpt, JSI_OM_DONTENUM);
                    Jsi_DecrRefCount(interp, excpt);
                    context_id = ps->_context_id++;
                }
                break;
            }
            case OP_ECATCH: {
                if (trylist == NULL || trylist->type != jsi_TL_TRY)
                    Jsi_LogBug("Unexpected ECATCH opcode??");

                trylist->inCatch=0;
                ip = trylist->d.td.fstart - 1;
                break;
            }
            case OP_SFINAL: {
                if (trylist == NULL || trylist->type != jsi_TL_TRY)
                    Jsi_LogBug("Unexpected SFINAL opcode??");

                /* restore scatch scope chain */
                trylist->inFinal = 1;
                JSI_RESTORE_SCOPE();
                break;
            }
            case OP_EFINAL: {
                if (trylist == NULL || trylist->type != jsi_TL_TRY)
                    Jsi_LogBug("Unexpected EFINAL opcode??");

                trylist->inFinal = 0;
                int last_op = trylist->d.td.last_op;
                jsi_OpCode *tojmp = (last_op == jsi_LOP_JMP ? trylist->d.td.ld.tojmp : 0);
                
                pop_try(trylist);

                if (last_op == jsi_LOP_THROW) {
                    JSI_DO_THROW("finally");
                } else if (last_op == jsi_LOP_JMP) {
                    while (1) {
                        if (trylist == NULL) {
                            ip = tojmp;
                            break;
                        }
                        /* same as jmp opcode, see above */
                        if (trylist->type == jsi_TL_TRY) {
                            if (tojmp >= trylist->d.td.tstart && tojmp < trylist->d.td.fend) {
                                ip = tojmp;
                                break;
                            }
                            
                            if (ip >= trylist->d.td.tstart && ip < trylist->d.td.cend) {
                                trylist->d.td.last_op = jsi_LOP_JMP;
                                trylist->d.td.ld.tojmp = tojmp;
                                
                                ip = trylist->d.td.fstart - 1;
                                break;
                            } else if (ip >= trylist->d.td.fstart && ip < trylist->d.td.fend) {
                                pop_try(trylist);
                            } else Jsi_LogBug("jmp within a try, but not in its scope?");
                        } else {        /* 'with' block */
                            if (tojmp >= trylist->d.wd.wstart && tojmp < trylist->d.wd.wend) {
                                ip = tojmp;
                                break;
                            }
                            JSI_RESTORE_SCOPE();
                            pop_try(trylist);
                        }
                    }
                }
                break;
            }
            case OP_THROW: {
                jsiVarDeref(interp,1);
                Jsi_ValueDup2(interp,&ps->last_exception, _jsi_TOP);
                interp->didReturn = 1; /* TODO: could possibly hide _jsi_STACK problem */
                JSI_DO_THROW("throw");
                break;
            }
            case OP_WITH: {
                static int warnwith = 1;
                if (strict && warnwith && interp->typeCheck.nowith) {
                    warnwith = 0;
                    rc = Jsi_LogError("use of with is illegal due to \"use nowith\"");
                    break;
                }
                jsiVarDeref(interp,1);
                Jsi_ValueToObject(interp, _jsi_TOP);
                
                jsi_TryList *n = jsi_trylist_new(jsi_TL_WITH, scope, currentScope);
                
                n->d.wd.wstart = ip;
                n->d.wd.wend = n->d.wd.wstart + (uintptr_t)ip->data;

                jsi_push_try(interp, &trylist, n);
                interp->framePtr->withDepth++;
                
                /* make expr to top of scope chain */
                scope = jsi_ScopeChainDupNext(interp, scope, currentScope);
                currentScope = Jsi_ValueNew1(interp);
                interp->framePtr->ingsc = scope;
                interp->framePtr->incsc = currentScope;
                Jsi_ValueCopy(interp, currentScope, _jsi_TOP);
                jsiPop(interp,1);
                
                context_id = ps->_context_id++;
                break;
            }
            case OP_EWITH: {
                if (trylist == NULL || trylist->type != jsi_TL_WITH)
                    Jsi_LogBug("Unexpected EWITH opcode??");

                JSI_RESTORE_SCOPE();
                
                pop_try(trylist);
                interp->framePtr->withDepth--;
                break;
            }
            case OP_DEBUG: {
                jsi_DebuggerStmt();
                jsiPush(interp,1);
                break;
            }
            case OP_RESERVED: {
                jsi_ReservedInfo *ri = (jsi_ReservedInfo *)ip->data;
                const char *cmd = ri->type == RES_CONTINUE ? "continue" : "break";
                /* TODO: continue/break out of labeled scope: see tests/prob/break.jsi. */
                if (ri->label) {
                    Jsi_LogError("%s: label(%s) not found", cmd, ri->label);
                } else {
                    Jsi_LogError("%s must be inside loop(or switch)", cmd);
                }
                rc = JSI_ERROR;
                break;
            }
#ifndef __cplusplus
            default:
                Jsi_LogBug("invalid op ceod: %d", ip->op);
#endif
        }
        lop = plop;
        ip++;
    }
done:
    while (trylist) {
        JSI_RESTORE_SCOPE();
        pop_try(trylist);
    }
    return rc;
}

Project: jsish

static void SwapNodes(Jsi_TreeEntry* n, Jsi_TreeEntry* m)
{
    Jsi_Tree* t = n->treePtr;
    Jsi_TreeEntry *np, *nl, *nr, *mp, *ml, *mr;
    int mpc = 0, npc = 0, col = n->f.bits.color;
    n->f.bits.color = m->f.bits.color;  m->f.bits.color = col;
    np = n->parent; nl = n->left; nr = n->right;
    mp = m->parent; ml = m->left; mr = m->right;
    if (mp) mpc = (mp->left == m ?1 : 2);
    if (np) npc = (np->left == n ?1 : 2);

    n->parent = mp; n->left = ml; n->right = mr;
    m->parent = np; m->left = nl; m->right = nr;
    
    if (np == m) {
        m->parent = n;
        if (mr == n) n->right = m; else n->left = m;
    } else if (mp == n) {
        n->parent = m;
        if (nr == m) m->right = n; else m->left = n;
    }
    /* Fixup back pointers. */
    if (m->left)  m->left->parent  = m;
    if (m->right) m->right->parent = m;
    if (n->left)  n->left->parent  = n;
    if (n->right) n->right->parent = n;
    if (mpc) { if (mpc==1) n->parent->left = n; else  n->parent->right = n;}
    if (npc) { if (npc==1) m->parent->left = m; else  m->parent->right = m; }
    if (n->parent == NULL) {
        t->root = n;
    } else if (m->parent == NULL) {
        t->root = m;
    }
}

Project: jsish

Jsi_HashEntryFind( Jsi_Hash *tablePtr, const void *key)
{
    if (tablePtr->opts.lockHashProc && (*tablePtr->opts.lockHashProc)(tablePtr, 1) != JSI_OK)
        return NULL;
    Jsi_HashEntry *hPtr = (*((tablePtr)->findProc))(tablePtr, key);
    if (tablePtr->opts.lockHashProc)
        (*tablePtr->opts.lockHashProc)(tablePtr, 0);
    return hPtr;
}

Project: jsish

static Jsi_OpCodes *code_push_num(Jsi_Number *v) { JSI_NEW_CODES(1,OP_PUSHNUM, v); }

Project: jsish

Jsi_Value* Jsi_ValueNewBlob(Jsi_Interp *interp, unsigned char *s, uint len) {
    Jsi_Value *v = Jsi_ValueNew(interp);
    Jsi_Obj *o = Jsi_ObjNewType(interp, JSI_OT_STRING);
    Jsi_ValueMakeObject(interp, &v, o);
    o->d.s.str = (char*)Jsi_Malloc(len+1);
    memcpy(o->d.s.str, (char*)s, len);
    o->d.s.str[len] = 0;
    o->d.s.len = len;
    o->isBlob = 1;
    return v;
}

Project: jsish

bool Jsi_NumberIsNaN(Jsi_Number n)
{
    return isnan(n);
}

Project: jsish

static int OneWordCompare(Jsi_Tree *treePtr, const void *key1, const void *key2)
{
    return ((uintptr_t )key1 - (uintptr_t)key2);
}

Project: jsish

Jsi_RC Jsi_ValueGetNumber(Jsi_Interp *interp, Jsi_Value *pv, Jsi_Number *val)
{
    if (!pv) return JSI_ERROR;
    if (pv->vt == JSI_VT_NUMBER)
        *val = pv->d.num;
    else if (pv->vt == JSI_VT_OBJECT && pv->d.obj->ot == JSI_OT_NUMBER)
        *val = pv->d.obj->d.num;
    else 
        return JSI_ERROR;
    return JSI_OK;
}

Project: jsish

bool Jsi_GlobMatch(const char *pattern, const char *string, int nocase)
{
    int c;
    int pchar;
    while (*pattern) {
        switch (pattern[0]) {
            case '*':
                while (pattern[1] == '*') {
                    pattern++;
                }
                pattern++;
                if (!pattern[0]) {
                    return 1;   /* match */
                }
                while (*string) {
                    if (Jsi_GlobMatch(pattern, string, nocase))
                        return 1;       /* match */
                    string += Jsi_UtfToUniChar(string, &c);
                }
                return 0;       /* no match */

            case '?':
                string += Jsi_UtfToUniChar(string, &c);
                break;

            case '[': {
                    string += Jsi_UtfToUniChar(string, &c);
                    pattern = JsiCharsetMatch(pattern + 1, c, nocase ? JSI_CMP_NOCASE : 0);
                    if (!pattern) {
                        return 0;
                    }
                    if (!*pattern) {
                        /* Ran out of pattern (no ']') */
                        continue;
                    }
                    break;
                }
            case '\\':
                if (pattern[1]) {
                    pattern++;
                }
                /* fall through */
            default:
                string += Jsi_UtfToUniCharCase(string, &c, nocase);
                Jsi_UtfToUniCharCase(pattern, &pchar, nocase);
                if (pchar != c) {
                    return 0;
                }
                break;
        }
        pattern += Jsi_UtfToUniCharCase(pattern, &pchar, nocase);
        if (!*string) {
            while (*pattern == '*') {
                pattern++;
            }
            break;
        }
    }
    if (!*pattern && !*string) {
        return 1;
    }
    return 0;
}

Project: jsish

static const char **jsi_OptGetEnumList(Jsi_OptionSpec* spec) {
    const char **list = (const char**)spec->data;
    Jsi_OptionSpec* es=NULL;
    int fflags = (spec->flags);
    if (spec->id != JSI_OPTION_CUSTOM || spec->custom != Jsi_Opt_SwitchEnum)
        return list;
    if (list && (fflags & JSI_OPT_ENUM_SPEC)) {
        es = (typeof(es))list;
        while (es->id != JSI_OPTION_END)
            es++;
        list = es->init.STRKEY;
    }
    return list;
}

Project: jsish

void jsi_ValueObjGetKeys(Jsi_Interp *interp, Jsi_Value *target, Jsi_Value *ret, bool isof)
{
    Jsi_IterObj *io = Jsi_IterObjNew(interp, NULL);
    Jsi_Obj *to = target->d.obj;
    
    if (target->vt != JSI_VT_UNDEF && target->vt != JSI_VT_NULL) {

        if (target->vt == JSI_VT_OBJECT && to->arr) {
            io->isArrayList = 1;
            io->count = to->arrCnt;
        } else {
            if (isof &&interp->strict)
                Jsi_LogWarn("non-array in 'for...of'");
            Jsi_IterGetKeys(interp, target, io, 0);
        }
    }
    io->obj = to;
    io->isof = isof;
    Jsi_Obj *r = Jsi_ObjNew(interp);
    r->ot = JSI_OT_ITER;
    r->d.iobj = io;
    Jsi_ValueMakeObject(interp, &ret, r);
}

Project: jsish

Jsi_OptionsDump(Jsi_Interp *interp, Jsi_OptionSpec *specs, void *rec, Jsi_Value **ret, Jsi_Wide flags)
{
    char *record = (char*)rec;
    Jsi_OptionSpec *specPtr = specs;
    int len = 0, i = 0, count = 0;
    if (!Jsi_OptionsValid(interp, specs))
        return Jsi_LogError("invalid options");
    
    while (specPtr->id>=JSI_OPTION_BOOL && specPtr->id < JSI_OPTION_END && specPtr->name) {
        specPtr++;
        len+=2;
    }
    if (!len)
        return JSI_OK;
    Jsi_Value *rv = Jsi_ValueMakeObject(interp, NULL, NULL);
    Jsi_IncrRefCount(interp, rv);
    Jsi_Obj *obj = rv->d.obj;
    specPtr = specs;
    while (specPtr->id>=JSI_OPTION_BOOL && specPtr->id < JSI_OPTION_END && specPtr->name) {
        Jsi_Value  *vv = Jsi_ValueNew1(interp);
        if (jsi_GetOption(interp, specPtr, record, NULL, &vv, flags) != JSI_OK) {
            Jsi_DecrRefCount(interp, vv);
            Jsi_DecrRefCount(interp, rv);
            return JSI_ERROR;
        }
        if (flags&JSI_OPTS_VERBOSE) {
            // dump: id,value,help,info,init
            Jsi_Value *vrv = Jsi_ValueMakeObject(interp, NULL, NULL);
            Jsi_IncrRefCount(interp, vrv);
            Jsi_Obj *vobj = vrv->d.obj;
            Jsi_ObjInsert(interp, vobj, "value", vv, 0);
            Jsi_DecrRefCount(interp, vv);
            vv = vrv;
            jsi_DumpOptionSpec(interp, vobj, specPtr, 0);
        }
        Jsi_ObjInsert(interp, obj, specPtr->name, vv, 0);
        Jsi_DecrRefCount(interp, vv);
        count++;
        i++;
        specPtr++;
    }
    assert(specPtr->id == JSI_OPTION_END);
    Jsi_ValueReplace(interp, ret, rv);
    Jsi_DecrRefCount(interp, rv);
    return JSI_OK;
}

Project: jsish

static void interpObjErase(InterpObj *fo)
{
    SIGASSERTV(fo,INTERPOBJ);
    if (fo->subinterp) {
        Jsi_Interp *interp = fo->subinterp;
        fo->subinterp = NULL;
        Jsi_InterpDelete(interp);
        /*fclose(fo->fp);
        Jsi_Free(fo->interpname);
        Jsi_Free(fo->mode);*/
    }
    fo->subinterp = NULL;
}

Project: jsish

Jsi_RC jsi_HashFree(Jsi_Interp *interp, Jsi_HashEntry *hPtr, void *ptr) {
    Jsi_Free(ptr);
    return JSI_OK;
}

Project: jsish

Jsi_RC Jsi_CleanValue(Jsi_Interp *interp, Jsi_Interp *tointerp, Jsi_Value *val, Jsi_Value **ret)
{
    Jsi_RC rc = JSI_OK;
    const char *cp;
    int len, iskey;
    Jsi_Obj *obj;
    switch (val->vt) {
        case JSI_VT_UNDEF: Jsi_ValueMakeUndef(interp, ret); return rc;
        case JSI_VT_NULL: Jsi_ValueMakeNull(tointerp, ret); return rc;
        case JSI_VT_BOOL: Jsi_ValueMakeBool(tointerp, ret, val->d.val); return rc;
        case JSI_VT_NUMBER: Jsi_ValueMakeNumber(tointerp, ret, val->d.num); return rc;
        case JSI_VT_STRING:
            iskey = val->f.bits.isstrkey;
            cp = val->d.s.str;
            len = val->d.s.len;
makestr:
            if (iskey) {
                Jsi_ValueMakeStringKey(interp, ret, cp);
                return rc;
            }
            jsi_ValueMakeBlobDup(tointerp, ret, (uchar*)cp, len);
            return rc;
        case JSI_VT_OBJECT:
            obj = val->d.obj;
            switch (obj->ot) {
                case JSI_OT_BOOL: Jsi_ValueMakeBool(tointerp, ret, obj->d.val); return rc;
                case JSI_OT_NUMBER: Jsi_ValueMakeNumber(tointerp, ret, obj->d.num); return rc;
                case JSI_OT_STRING:
                    cp = obj->d.s.str;
                    len = obj->d.s.len;
                    iskey = obj->isstrkey;
                    goto makestr;
                default: break;
            }
            break;
        default:
            break;
    }
    Jsi_DString dStr;
    Jsi_DSInit(&dStr);
    cp = Jsi_ValueGetDString(interp, val, &dStr, JSI_OUTPUT_JSON);
    if (Jsi_JSONParse(tointerp, cp, ret, 0) != JSI_OK) {
        Jsi_DSFree(&dStr);
        return Jsi_LogWarn("bad JSON parse in subinterp");
    }
    Jsi_DSFree(&dStr);
    return rc;
}

Project: jsish

bool Jsi_NumberIsWide(Jsi_Number n) { return (isnormal(n) && (Jsi_Number)((Jsi_Wide)(n)) == (n)); }

Project: jsish

int Jsi_TreeWalk(Jsi_Tree* treePtr, Jsi_TreeWalkProc* callback, void *data, int flags) {
    Jsi_Interp *interp = treePtr->opts.interp;
    JSI_NOTUSED(interp);
    int n = 0, m = -1, lastm, order;
    order = flags & JSI_TREE_ORDER_MASK;
    switch (order) {
    case JSI_TREE_ORDER_PRE:
        return tree_preorder(treePtr, treePtr->root, callback, data);
    case JSI_TREE_ORDER_POST:
        return tree_postorder(treePtr, treePtr->root, callback, data);
    case JSI_TREE_ORDER_IN:
        return tree_inorder(treePtr, treePtr->root, callback, data);
    case JSI_TREE_ORDER_LEVEL:
        while (1) {
            lastm = m;
            if (tree_levelorder(treePtr, treePtr->root, callback, data, 0, n, &m) != JSI_OK)
                return JSI_ERROR;
            if (lastm == m)
                return JSI_OK;
            n++;
        }
            
    default:
        Jsi_LogError("Invalid order: %d", order);
    }
    return JSI_ERROR;
}

Project: jsish

static Jsi_RC mdbStmtFreeProc(Jsi_Interp *interp, Jsi_HashEntry *hPtr, void *value) {
    MySqlObj *jdb = (MySqlObj*)interp;
    Jsi_ListEntry *l = (Jsi_ListEntry*)hPtr;
    mdbRelease1Stmt(jdb, (MysqlPrep*)Jsi_ListValueGet(l));
    return JSI_OK;
}

Project: jsish

static void jsiObjGetNames(Jsi_Interp *interp, Jsi_Obj *obj, Jsi_DString* dStr, int flags) {
    Jsi_TreeEntry *hPtr;
    Jsi_TreeSearch srch;
    Jsi_Value *v;
    int m = 0;
    Jsi_DSInit(dStr);
    if (obj->isarrlist)
        obj = interp->Array_prototype->d.obj;
    else if (!obj->tree->numEntries && obj->__proto__)
        obj = obj->__proto__->d.obj;
    for (hPtr=Jsi_TreeSearchFirst(obj->tree, &srch,  JSI_TREE_ORDER_IN, NULL); hPtr; hPtr=Jsi_TreeSearchNext(&srch)) {
        v = (Jsi_Value*)Jsi_TreeValueGet(hPtr);
        if (!v) continue;
        if ((flags&JSI_NAME_FUNCTIONS) && !Jsi_ValueIsFunction(interp,v)) {
            continue;
        }
        if ((flags&JSI_NAME_DATA) && Jsi_ValueIsFunction(interp,v)) {
            continue;
        }

        Jsi_DSAppend(dStr, (m++?" ":""), Jsi_TreeKeyGet(hPtr), NULL);
    }
    Jsi_TreeSearchDone(&srch);
}

Project: jsish

static Jsi_RC NumberToFixedCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    char buf[JSI_MAX_NUMBER_STRING+1];
    int prec = 0, skip = 0;
    Jsi_Number num;
    Jsi_Value *v;
    ChkStringN(_this, funcPtr, v);
    Jsi_Value *pa = Jsi_ValueArrayIndex(interp, args, skip);
    if (pa && Jsi_GetIntFromValue(interp, pa, &prec) != JSI_OK)
        return JSI_ERROR;
    if (prec<0) prec = 0;
    Jsi_GetDoubleFromValue(interp, v, &num);
    snprintf(buf, sizeof(buf), "%.*" JSI_NUMFFMT, prec, num);
    Jsi_ValueMakeStringDup(interp, ret, buf);
    return JSI_OK;
}

Project: jsish

static int codes_insertln(Jsi_OpCodes *c, jsi_Eopcode code, void *extra, jsi_Pstate *pstate, jsi_Pline *line, int doalloc)
{
    if (c->code_size - c->code_len <= 0) {
        c->code_size += 100;
        c->codes = (jsi_OpCode *)Jsi_Realloc(c->codes, c->code_size * sizeof(jsi_OpCode));
    }
    c->codes[c->code_len].op = code;
    c->codes[c->code_len].data = extra;
    c->codes[c->code_len].Line = line->first_line;
    c->codes[c->code_len].Lofs = line->first_column;
    c->codes[c->code_len].fname = jsi_PstateGetFilename(pstate);
    c->codes[c->code_len].alloc = doalloc;
    c->code_len ++;
    return 0;
}

Project: jsish

Jsi_TreeEntry * Jsi_ObjInsert(Jsi_Interp *interp, Jsi_Obj *obj, const char *key, Jsi_Value *val, int flags)
{
    Jsi_TreeEntry *hPtr;
    SIGASSERT(val, VALUE);
    /*if (val)
        Jsi_IncrRefCount(interp, val);*/
    hPtr = Jsi_TreeObjSetValue(obj, key, val, (flags&JSI_OM_ISSTRKEY));
    if ((flags&JSI_OM_DONTDEL))
        val->f.bits.dontdel = hPtr->f.bits.dontdel = 1;
    if ((flags&JSI_OM_READONLY))
        val->f.bits.readonly =hPtr->f.bits.readonly = 1;
    if ((flags&JSI_OM_DONTENUM))
        val->f.bits.dontenum =hPtr->f.bits.dontenum = 1;
    return hPtr;
}

Project: jsish

static Jsi_RC packageHashFree(Jsi_Interp *interp, Jsi_HashEntry *hPtr, void *ptr) {
    jsi_PkgInfo *p = (jsi_PkgInfo*)ptr;
    if (p->popts.info) Jsi_DecrRefCount(interp, p->popts.info);
    Jsi_Free(p);
    return JSI_OK;
}

Project: jsish

static int dbAuthCallback(
    void *pArg,
    int code,
    const char *zArg1,
    const char *zArg2,
    const char *zArg3,
    const char *zArg4
) {
    const char *zCode;
    int rc;
    const char *zReply;
    Jsi_Db *jdb = (Jsi_Db*)pArg;
    Jsi_Interp *interp = jdb->interp;
    if( jdb->disableAuth ) return SQLITE_OK;

    switch( code ) {
    case SQLITE_COPY              :
        zCode="SQLITE_COPY";
        break;
    case SQLITE_CREATE_INDEX      :
        zCode="SQLITE_CREATE_INDEX";
        break;
    case SQLITE_CREATE_TABLE      :
        zCode="SQLITE_CREATE_TABLE";
        break;
    case SQLITE_CREATE_TEMP_INDEX :
        zCode="SQLITE_CREATE_TEMP_INDEX";
        break;
    case SQLITE_CREATE_TEMP_TABLE :
        zCode="SQLITE_CREATE_TEMP_TABLE";
        break;
    case SQLITE_CREATE_TEMP_TRIGGER:
        zCode="SQLITE_CREATE_TEMP_TRIGGER";
        break;
    case SQLITE_CREATE_TEMP_VIEW  :
        zCode="SQLITE_CREATE_TEMP_VIEW";
        break;
    case SQLITE_CREATE_TRIGGER    :
        zCode="SQLITE_CREATE_TRIGGER";
        break;
    case SQLITE_CREATE_VIEW       :
        zCode="SQLITE_CREATE_VIEW";
        break;
    case SQLITE_DELETE            :
        zCode="SQLITE_DELETE";
        break;
    case SQLITE_DROP_INDEX        :
        zCode="SQLITE_DROP_INDEX";
        break;
    case SQLITE_DROP_TABLE        :
        zCode="SQLITE_DROP_TABLE";
        break;
    case SQLITE_DROP_TEMP_INDEX   :
        zCode="SQLITE_DROP_TEMP_INDEX";
        break;
    case SQLITE_DROP_TEMP_TABLE   :
        zCode="SQLITE_DROP_TEMP_TABLE";
        break;
    case SQLITE_DROP_TEMP_TRIGGER :
        zCode="SQLITE_DROP_TEMP_TRIGGER";
        break;
    case SQLITE_DROP_TEMP_VIEW    :
        zCode="SQLITE_DROP_TEMP_VIEW";
        break;
    case SQLITE_DROP_TRIGGER      :
        zCode="SQLITE_DROP_TRIGGER";
        break;
    case SQLITE_DROP_VIEW         :
        zCode="SQLITE_DROP_VIEW";
        break;
    case SQLITE_INSERT            :
        zCode="SQLITE_INSERT";
        break;
    case SQLITE_PRAGMA            :
        zCode="SQLITE_PRAGMA";
        break;
    case SQLITE_READ              :
        zCode="SQLITE_READ";
        break;
    case SQLITE_SELECT            :
        zCode="SQLITE_SELECT";
        break;
    case SQLITE_TRANSACTION       :
        zCode="SQLITE_TRANSACTION";
        break;
    case SQLITE_UPDATE            :
        zCode="SQLITE_UPDATE";
        break;
    case SQLITE_ATTACH            :
        zCode="SQLITE_ATTACH";
        break;
    case SQLITE_DETACH            :
        zCode="SQLITE_DETACH";
        break;
    case SQLITE_ALTER_TABLE       :
        zCode="SQLITE_ALTER_TABLE";
        break;
    case SQLITE_REINDEX           :
        zCode="SQLITE_REINDEX";
        break;
    case SQLITE_ANALYZE           :
        zCode="SQLITE_ANALYZE";
        break;
    case SQLITE_CREATE_VTABLE     :
        zCode="SQLITE_CREATE_VTABLE";
        break;
    case SQLITE_DROP_VTABLE       :
        zCode="SQLITE_DROP_VTABLE";
        break;
    case SQLITE_FUNCTION          :
        zCode="SQLITE_FUNCTION";
        break;
    case SQLITE_SAVEPOINT         :
        zCode="SQLITE_SAVEPOINT";
        break;
    default                       :
        zCode="????";
        break;
    }
    int i = 0;
    Jsi_Value *vpargs, *items[10] = {}, *ret;
    items[i++] = Jsi_ValueNewObj(interp, jdb->fobj);
    items[i++] = Jsi_ValueMakeStringDup(interp, NULL, zCode);
    items[i++] = Jsi_ValueMakeStringDup(interp, NULL, zArg1 ? zArg1 : "");
    items[i++] = Jsi_ValueMakeStringDup(interp, NULL, zArg2 ? zArg2 : "");
    items[i++] = Jsi_ValueMakeStringDup(interp, NULL, zArg3 ? zArg3 : "");
    items[i++] = Jsi_ValueMakeStringDup(interp, NULL, zArg4 ? zArg4 : "");
    vpargs = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, items, i, 0));
    Jsi_IncrRefCount(interp, vpargs);
    ret = Jsi_ValueNew(interp);
    rc = Jsi_FunctionInvoke(interp, jdb->onAuth, vpargs, &ret, NULL);
    Jsi_DecrRefCount(interp, vpargs);

    if (rc == JSI_OK && (zReply = Jsi_ValueGetStringLen(interp, ret, NULL)))
    {
        if( Jsi_Strcmp(zReply,"SQLITE_OK")==0 ) {
            rc = SQLITE_OK;
        } else if( Jsi_Strcmp(zReply,"SQLITE_DENY")==0 ) {
            rc = SQLITE_DENY;
        } else if( Jsi_Strcmp(zReply,"SQLITE_IGNORE")==0 ) {
            rc = SQLITE_IGNORE;
        } else {
            rc = 999;
        }
    }
    Jsi_DecrRefCount(interp, ret);
    return rc;
}

Project: jsish

bool Jsi_FunctionReturnIgnored(Jsi_Interp *interp, Jsi_Func *funcPtr) {
    return funcPtr->callflags.bits.isdiscard;
}

Project: jsish

bool Jsi_InterpGone( Jsi_Interp* interp)
{
    return (interp == NULL || interp->deleting || interp->destroying || interp->exited);
}

Project: jsish

static MYSQL* mdbConnect(Jsi_Interp *interp, MySqlObj* jdb)
{
    return mysql_real_connect(jdb->db,
        jdb->host?Jsi_ValueString(interp, jdb->host, NULL):NULL,
        jdb->user, jdb->password, jdb->database,
        jdb->port, 0, jdb->dbflags);
}

Project: jsish

static Jsi_RC KeyLockerTree(Jsi_Tree* tree, int lock) { return KeyLocker((Jsi_Hash*)tree, lock); }

Project: jsish

Jsi_RC jsi_evalcode(jsi_Pstate *ps, Jsi_Func *func, Jsi_OpCodes *opcodes, 
         jsi_ScopeChain *scope, Jsi_Value *fargs,
         Jsi_Value *_this,
         Jsi_Value **vret)
{
    Jsi_Interp *interp = ps->interp;
    if (interp->exited)
        return JSI_ERROR;
    Jsi_RC rc;
    jsi_Frame frame = *interp->framePtr;
    frame.parent = interp->framePtr;
    interp->framePtr = &frame;
    frame.parent->child = interp->framePtr = &frame;
    frame.ps = ps;
    frame.ingsc = scope;
    frame.incsc = fargs;
    frame.inthis = _this;
    frame.opcodes = opcodes;
    frame.fileName = ((func && func->script)?func->script:interp->curFile);
    frame.funcName = interp->curFunction;
    frame.dirName = interp->curDir;
    if (frame.fileName && frame.fileName == frame.parent->fileName)
        frame.logflag = frame.parent->logflag;
    else
        frame.logflag = 0;
    frame.level = frame.parent->level+1;
    frame.evalFuncPtr = func;
    frame.arguments = NULL;
   // if (func && func->strict)
    //    frame.strict = 1;
    if (interp->curIp)
        frame.parent->line = interp->curIp->Line;
    frame.ip = interp->curIp;
    interp->refCount++;
    interp->level++;
    Jsi_IncrRefCount(interp, fargs);
    rc = jsi_evalcode_sub(ps, opcodes, scope, fargs, _this, *vret);
    Jsi_DecrRefCount(interp, fargs);
    if (interp->didReturn == 0 && !interp->exited && rc == JSI_OK) {
        if ((interp->evalFlags&JSI_EVAL_RETURN)==0)
            Jsi_ValueMakeUndef(interp, vret);
        /*if (interp->framePtr->Sp != oldSp) //TODO: at some point after memory refs???
            Jsi_LogBug("Stack not balance after execute script");*/
    }
    if (frame.arguments)
        Jsi_DecrRefCount(interp, frame.arguments);
    interp->didReturn = 0;
    interp->refCount--;
    interp->level--;
    interp->framePtr = frame.parent;
    interp->framePtr->child = NULL;
    interp->curIp = frame.ip;
    if (interp->exited)
        rc = JSI_ERROR;
    return rc;
}

Project: jsish

static Jsi_OpCodes *code_push_func_sub(jsi_Pstate *p, jsi_Pline *line, Jsi_Func *fun) { p->funcDefs++; JSI_NEW_CODESLN(0,OP_PUSHFUN, fun); }

Project: jsish

char *get_current_dir_name() {
    static char buf[MAX_PATH] = ".";
    getcwd(buf, sizeof(buf));
    return buf;
}

Project: jsish

void* Jsi_CurrentThread(void) {
#ifdef __WIN32
    return (void*)(uintptr_t)GetCurrentThreadId();
#else
    return (void*)pthread_self();
#endif
}

Project: jsish

static const char *mdbFindEndStr(const char *cp) {
    const char endc = *cp;
    cp++;
    while (*cp && *cp != endc) {
        if (*cp == '\\' && cp[1]) cp++;
        cp++;
    }
    if (*cp != endc)
        return NULL;
    return ++cp;
}

Project: jsish

static void mdbClose(MYSQL  *db) {
        mysql_close(db);
}

Project: jsish

void Jsi_InterpSetData(Jsi_Interp *interp, const char *key, void *data, Jsi_DeleteProc *proc)
{
    bool isNew;
    Jsi_HashEntry *hPtr;
    AssocData *ptr;
    hPtr = Jsi_HashEntryNew(interp->assocTbl, key, &isNew);
    if (!hPtr)
        return;
    if (isNew) {
        ptr = (AssocData *)Jsi_Calloc(1,sizeof(*ptr));
        Jsi_HashValueSet(hPtr, ptr);
    } else
        ptr = (AssocData *)Jsi_HashValueGet(hPtr);
    ptr->data = data;
    ptr->delProc = proc;
}

Project: jsish

static Jsi_OpCodes *code_pos() { JSI_NEW_CODES(0,OP_POS, 0); }

Project: jsish

static Jsi_RC mysqlObjFree(Jsi_Interp *interp, void *data)
{
    MySqlObj *jdb = (MySqlObj*)data;
    SQLSIGASSERT(jdb,DB);
    jdb->_->activeCnt--;
    mysqlObjErase(jdb);
    _JSI_MEMCLEAR(jdb);
    Jsi_Free(jdb);
    return JSI_OK;
}

Project: jsish

static Jsi_RC jsi_VerifyToValue(Jsi_Interp *interp, Jsi_OptionSpec* spec, Jsi_Value **outValue, Jsi_DString *dStr, void *record, Jsi_Wide flags)
{
    if (dStr)
        return JSI_ERROR;
    Jsi_Value **s = (Jsi_Value**)(((char*)record) + spec->offset);
    if (*s)
        Jsi_ValueReplace(interp, outValue, *s);
    return JSI_OK;
}

Project: jsish

static Jsi_RC RegexpExecCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    int skip = 0;
    Jsi_Value *v;
    ChkRegexp(_this, funcPtr, v);
    /* TODO: add lastIndex support. */
    int slen;
    char *str = Jsi_ValueString(interp,Jsi_ValueArrayIndex(interp, args, skip), &slen);
    if (!str) 
        return Jsi_LogError("expected string");
    if (v == NULL || v->vt != JSI_VT_OBJECT || v->d.obj->ot != JSI_OT_REGEXP) 
        return Jsi_LogError("expected pattern");
    Jsi_Regex *re = v->d.obj->d.robj;
    int isglob = (re->eflags&JSI_REG_GLOB);
    Jsi_Value *l = NULL;
    Jsi_Number lv = 0;
    if (isglob) {
        l = Jsi_ValueObjLookup(interp, v, "lastIndex", 0);
        if (l && Jsi_ValueGetNumber(interp, l, &lv) != JSI_OK) 
            return Jsi_LogError("lastIndex not a number");
        if (l)
            re->lastIndex = (int)lv;
    }
    int ofs = 0;
    Jsi_RC rc = jsi_RegExpMatches(interp, v, re->lastIndex<slen?str+re->lastIndex:"", -1, *ret, isglob?&ofs:NULL, 0);
    if (isglob) {
        if (rc != JSI_OK)
            return rc;
        re->lastIndex += ofs;
        if (Jsi_ValueIsNull(interp, *ret))
            re->lastIndex = 0;
        lv = (Jsi_Number)re->lastIndex;
        if (!l)
            Jsi_ValueInsert(interp, v, "lastIndex", Jsi_ValueNewNumber(interp, lv), JSI_OM_DONTDEL);
        else if (l->vt == JSI_VT_NUMBER)
            l->d.num = lv;
        else if (l->vt == JSI_VT_OBJECT && l->d.obj->ot == JSI_OT_NUMBER)
            l->d.obj->d.num = lv;
    }
    return rc;
}

Project: jsish

int jsi_FSScandirProc(Jsi_Interp *interp, Jsi_Value *path, Jsi_Dirent ***namelist,
   int (*filter)(const Jsi_Dirent *), int (*compar)(const Jsi_Dirent **, const Jsi_Dirent**))
{
    const char *dirname = Jsi_ValueToString(interp, path, NULL);
    Jsi_DString dStr = {};
    if (*dirname == '~')
        dirname = jsi_TildePath(interp, dirname, &dStr);
    int rc = scandir(dirname, namelist, filter, compar);
    Jsi_DSFree(&dStr);
    return rc;
}

Project: jsish

static Jsi_RC StringConstructor(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    if (Jsi_FunctionIsConstructor(funcPtr)) {
        const char *nv = "";
        int len = -1;
        if (Jsi_ValueGetLength(interp, args) > 0) {
            Jsi_Value *v = Jsi_ValueArrayIndex(interp, args, 0);
            if (v) {
                
                nv = Jsi_ValueToString(interp, v, &len);
            }
        }
        if (_this->vt == JSI_VT_OBJECT && _this->d.obj->ot == JSI_OT_STRING) {
            if (_this->d.obj->d.s.str)
                if (!_this->d.obj->isstrkey)
                    Jsi_Free(_this->d.obj->d.s.str);
            _this->d.obj->isstrkey = 0;
            _this->d.obj->d.s.str = Jsi_StrdupLen(nv, len);
            _this->d.obj->d.s.len = len;
        } else
            jsi_ValueMakeBlobDup(interp, &_this, (uchar*)nv, len);
        Jsi_ValueDup2(interp, ret, _this);
        return JSI_OK;
    }
    if (Jsi_ValueGetLength(interp, args) > 0) {
        Jsi_Value *v = Jsi_ValueArrayIndex(interp, args, 0);
        if (v) {
            Jsi_ValueDup2(interp, ret, v);
            Jsi_ValueToString(interp, *ret, NULL);
            return JSI_OK;
        }
    }
    Jsi_ValueMakeStringDup(interp, ret, "");
    return JSI_OK;
}

Project: jsish

Jsi_RC Jsi_ValueGetBoolean(Jsi_Interp *interp, Jsi_Value *pv, bool *val)
{
    if (!pv) return JSI_ERROR;
    if (pv->vt == JSI_VT_BOOL)
        *val = pv->d.val;
    else if (pv->vt == JSI_VT_OBJECT && pv->d.obj->ot == JSI_OT_BOOL)
        *val = pv->d.obj->d.val;
    else 
        return JSI_ERROR;
    return JSI_OK;
}

Project: jsish

static Jsi_RC jsi_DeleteSpecCacheTable(Jsi_Interp *interp, void *clientData)
{
  Jsi_Hash *tablePtr = (Jsi_Hash *) clientData;
  Jsi_HashEntry *entryPtr;
  Jsi_HashSearch search;

  for (entryPtr = Jsi_HashSearchFirst(tablePtr,&search); entryPtr != NULL;
      entryPtr = Jsi_HashSearchNext(&search)) {

    Jsi_Free(Jsi_HashValueGet(entryPtr));
  }
  Jsi_HashDelete(tablePtr);
  return JSI_OK;
}

Project: jsish

static void dbOutputHexBlob(Jsi_DString *dStr, const void *pBlob, int nBlob){
  int i;
  char out[JSI_MAX_NUMBER_STRING], *zBlob = (char *)pBlob;
  Jsi_DSAppend(dStr, "X'", NULL);
  for(i=0; i<nBlob; i++){ snprintf(out, sizeof(out),"%02x",zBlob[i]&0xff);Jsi_DSAppend(dStr, out, NULL); }
  Jsi_DSAppend(dStr, "'", NULL);
}

Project: jsish

static void jsi_wsDumpQuery(jsi_wsCmdObj *cmdPtr, jsi_wsPss *pss, const char *name, Jsi_Value **ret)
{
    int n = 0;
    Jsi_Interp *interp = cmdPtr->interp;
    Jsi_Obj *nobj;
    Jsi_Value *nv;
    if (!name) {
        nobj = Jsi_ObjNewType(interp, JSI_OT_OBJECT);
        Jsi_ValueMakeObject(interp, ret, nobj);
    }
    for (n = 0; n<pss->paramc; n++) {
        const char *cp = lws_spa_get_string(pss->spa, n);
        if (!cp) cp = "";
        if (name) {
            if (Jsi_Strcmp(name, pss->paramv[n])) { n++; continue; }
            Jsi_ValueMakeStringDup(interp, ret, cp);
            return;
        }
        nv = Jsi_ValueNewStringDup(interp, cp);
        Jsi_ObjInsert(interp, nobj, pss->paramv[n], nv, 0);
    }
}

Project: jsish

int Jsi_Puts(Jsi_Interp *interp, Jsi_Channel chan, const char *str, int size)
{
    int code = 0, len = Jsi_Strlen(str);
    if (chan->fsPtr==0 || !chan->fsPtr->putsProc) {
        FILE *fp = (chan && chan->fp ? chan->fp : stdout);
        code = fputs(str, fp);
    } else {
        if (interp && interp->debugOpts.putsCallback && interp->parent) {
            int code = 0;
            Jsi_DString jStr={}, kStr={};
            Jsi_DSPrintf(&kStr, "[%s, %d]",
                Jsi_JSONQuote(interp, str, size, &jStr), (chan->fp == stderr?1:0));
            if (Jsi_FunctionInvokeJSON(interp->parent, interp->debugOpts.putsCallback, Jsi_DSValue(&kStr), NULL) != JSI_OK)
                code = 1;
            Jsi_DSFree(&jStr);
            Jsi_DSFree(&kStr);
            return code;
        } else if (interp && interp->stdoutStr) {
            Jsi_DString dStr = {};
            Jsi_DSAppend(&dStr, Jsi_ValueString(interp, interp->stdoutStr, NULL), NULL);
            Jsi_DSAppend(&dStr, str, NULL);
            Jsi_ValueFromDS(interp, &dStr, &interp->stdoutStr);
        } else

            code = chan->fsPtr->putsProc(chan, str);
    }
    if (size>=0 && len < size) {
        Jsi_Puts(interp, chan, "\\0", -1);
        Jsi_Puts(interp, chan, str+len+1, size-len-1);
    }
    return code;
}

Project: jsish

Jsi_FuncObj *jsi_FuncObjNew(Jsi_Interp *interp, Jsi_Func *func)
{
    Jsi_FuncObj *f = (Jsi_FuncObj*)Jsi_Calloc(1,sizeof(Jsi_FuncObj));
    f->interp = interp;
    SIGINIT(f,FUNCOBJ);
    f->func = func;
    func->refCnt++;
    return f;
}

Project: jsish

HashOneWord(
    Jsi_Hash *tablePtr,
    const void *key)
{
    uint64_t a0, a1;
    uint64_t y0, y1;
    uint64_t y2, y3;
    uint64_t p1, p2;
    uint64_t result;
    /* Compute key * GOLDEN_RATIO in 128-bit arithmetic */
    a0 = (uint64_t)key & 0x00000000FFFFFFFF;
    a1 = (uint64_t)key >> 32;

    y0 = a0 * 0x000000007f4a7c13;
    y1 = a0 * 0x000000009e3779b9;
    y2 = a1 * 0x000000007f4a7c13;
    y3 = a1 * 0x000000009e3779b9;
    y1 += y0 >> 32;     /* Can't carry */
    y1 += y2;           /* Might carry */
    if (y1 < y2) {
        y3 += (1LL << 32);  /* Propagate */
    }

    /* 128-bit product: p1 = loword, p2 = hiword */
    p1 = ((y1 & 0x00000000FFFFFFFF) << 32) + (y0 & 0x00000000FFFFFFFF);
    p2 = y3 + (y1 >> 32);

    /* Left shift the value downward by the size of the table */
    if (tablePtr->downShift > 0) {
        if (tablePtr->downShift < 64) {
            result = ((p2 << (64 - tablePtr->downShift)) |
                      (p1 >> (tablePtr->downShift & 63)));
        } else {
            result = p2 >> (tablePtr->downShift & 63);
        }
    } else {
        result = p1;
    }
    /* Finally mask off the high bits */
    return (jsi_Hash)(result & tablePtr->mask);
}

Project: jsish

static void mdbStmtLimit( MySqlObj *jdb)
{
    while(jdb->numStmts>jdb->maxStmts ) {
        Jsi_ListEntry *l = Jsi_ListPopBack(jdb->stmtCache);
        mdbRelease1Stmt(jdb, (MysqlPrep*)Jsi_ListValueGet(l));
        jdb->numStmts = Jsi_ListSize(jdb->stmtCache);
    }
}

Project: jsish

static Jsi_RC jsi_NullToValue(Jsi_Interp *interp, Jsi_OptionSpec* spec, Jsi_Value **outValue, Jsi_DString *outStr, void *record, Jsi_Wide flags)
{
    Jsi_ValueMakeNull(interp, outValue);
    return JSI_OK;
}

Project: jsish

Jsi_Tree *Jsi_TreeFromValue(Jsi_Interp *interp, Jsi_Value *v)
{
    if (!Jsi_ValueIsObjType(interp, v, JSI_OT_OBJECT))
        return NULL;
    return v->d.obj->tree;
}

Project: jsish

Jsi_OptionsGet(Jsi_Interp *interp, Jsi_OptionSpec *specs, void *rec, const char *option, Jsi_Value** valuePtr, Jsi_Wide flags)
{
    char *record = (char*)rec;
    Jsi_OptionSpec *specPtr;
    const char *cp = NULL, *cb = NULL;
    if (option) {
        cp = Jsi_Strchr(option, '.');
        cb = Jsi_Strchr(option, '[');
    }
    if (cp && (!cb || cp<cb) ) {
        Jsi_DString dStr;
        int len = (cp-option);
        Jsi_DSInit(&dStr);
        cp = Jsi_DSAppendLen(&dStr, option, len);
        specPtr = Jsi_OptionsFind(interp, specs, cp, flags);
        Jsi_DSFree(&dStr);
        if (!specPtr || !specPtr->data || specPtr->id != JSI_OPTION_CUSTOM || specPtr->custom != Jsi_Opt_SwitchSuboption) 
            return Jsi_LogError("unknown or bad sub-config option: %s", option);
        cp = option+len+1;
        return Jsi_OptionsGet(interp, (Jsi_OptionSpec *)(specPtr->data), (void*)(((char*)rec)+specPtr->offset), cp, valuePtr, flags);
    }
    if (cb && cb != option) {
        char *ce = Jsi_Strchr(option, ']');
        Jsi_Wide ul;
        Jsi_DString dStr;
        Jsi_DSInit(&dStr);
        int len = 0;
        if (ce && ce>cb) {
            len = (ce-cb-1);
            cp = Jsi_DSAppendLen(&dStr, cb+1, len);
        }
        if (len <= 0 || Jsi_GetWide(interp, cp, &ul, 0) != JSI_OK || ul<0) 
            return Jsi_LogError("bad sub-array option: %s", option);
        len = (cb-option);
        Jsi_DSSetLength(&dStr, 0);
        cp = Jsi_DSAppendLen(&dStr, option, len);
        specPtr = Jsi_OptionsFind(interp, specs, cp, flags);
        Jsi_DSFree(&dStr);
        if (!specPtr || !specPtr->init.OPT_CARRAY|| specPtr->id != JSI_OPTION_CUSTOM || specPtr->custom != Jsi_Opt_SwitchCArray) {
bail:
            return Jsi_LogError("unknown or bad array option: %s", option);
        }
        cp = cb+1;
        Jsi_OptionSpec *subSpec = specPtr->init.OPT_CARRAY;
        int isize, size = specPtr->arrSize;
        if (!subSpec || size<=0 || (isize=subSpec->size)<=0)
            goto bail;
        isize = isize/size;
        uchar *s = (((uchar*)rec)+specPtr->offset + isize*ul);
        if (ce[1] != '.' || !subSpec->data) {
            if (Jsi_OptionsGet(interp, subSpec, (void*)s, subSpec->name, valuePtr, flags) != JSI_OK)
                return JSI_ERROR;
        } else {
            if (Jsi_OptionsGet(interp, (Jsi_OptionSpec *)subSpec->data, (void*)s, ce+2, valuePtr, flags) != JSI_OK)
                return JSI_ERROR;
        }
        return JSI_OK;
    }

    specPtr = Jsi_OptionsFind(interp, specs, option, flags);
    if (specPtr == NULL || jsi_GetOption(interp, specPtr, record, option, valuePtr, flags) != JSI_OK) {
        return JSI_ERROR;
    }

    return JSI_OK;

}

Project: jsish

static void jsi_DumpOptionSpec(Jsi_Interp *interp, Jsi_Obj *nobj, Jsi_OptionSpec* spec, int addName)
{
    if (addName)
        Jsi_ObjInsert(interp, nobj, "name", Jsi_ValueNewStringKey(interp, spec->name),0);
    if (spec->help) {
        if (Jsi_Strchr(spec->help, '\n'))
            Jsi_LogError("%s .help contains newline: %s", spec->name, spec->help);
        Jsi_ObjInsert(interp, nobj, "help", Jsi_ValueNewStringKey(interp, spec->help),0);
    }
    if (spec->info)
        Jsi_ObjInsert(interp, nobj, "info", Jsi_ValueNewStringKey(interp, spec->info),0);
    Jsi_ObjInsert(interp, nobj, "type", Jsi_ValueNewStringKey(interp, jsi_OptionTypeStr(spec->id,0)),0);
    Jsi_ObjInsert(interp, nobj, "cName", Jsi_ValueNewStringKey(interp, jsi_OptionTypeStr(spec->id,1)),0);
    Jsi_ObjInsert(interp, nobj, "initOnly", Jsi_ValueNewBoolean(interp, (spec->flags & JSI_OPT_INIT_ONLY)!=0), 0);
    Jsi_ObjInsert(interp, nobj, "readOnly", Jsi_ValueNewBoolean(interp, (spec->flags & JSI_OPT_READ_ONLY)!=0), 0);
    Jsi_ObjInsert(interp, nobj, "required", Jsi_ValueNewBoolean(interp, (spec->flags & JSI_OPT_REQUIRED)!=0), 0);
    Jsi_ObjInsert(interp, nobj, "noCase", Jsi_ValueNewBoolean(interp, (spec->flags & JSI_OPT_CUST_NOCASE)!=0), 0);
    Jsi_ObjInsert(interp, nobj, "size", Jsi_ValueNewNumber(interp, (Jsi_Number)spec->size), 0);
    if (spec->flags)
        Jsi_ObjInsert(interp, nobj, "flags", Jsi_ValueNewNumber(interp, (Jsi_Number)spec->flags), 0);
    if (spec->data) {
        if (spec->id == JSI_OPTION_FUNC && spec->data) 
            Jsi_ObjInsert(interp, nobj, "args", Jsi_ValueNewStringDup(interp, (char*)spec->data), 0);
        else if (spec->id == JSI_OPTION_CUSTOM && (spec->custom == Jsi_Opt_SwitchBitset ||
            spec->custom == Jsi_Opt_SwitchEnum)) {
            const char **list = jsi_OptGetEnumList(spec);
            if (list)
                Jsi_ObjInsert(interp, nobj, "data", Jsi_ValueNewArray(interp, list, -1), 0);
        }
    }
    if (spec->id == JSI_OPTION_CUSTOM && spec->custom) {
        Jsi_Obj *sobj = Jsi_ObjNewType(interp, JSI_OT_OBJECT);
        Jsi_Value *cvalue = Jsi_ValueMakeObject(interp, NULL, sobj);
        jsi_DumpCustomSpec(interp, sobj,  spec);
        Jsi_ObjInsert(interp, nobj, "customArg", cvalue,0);
        Jsi_OptionSpec *os = spec;
        while (os->id != JSI_OPTION_END) os++;
        Jsi_ObjInsert(interp, nobj, "customArgHelp", Jsi_ValueNewStringKey(interp, (os->help?os->help:"")), 0);
        
    }
}

Project: jsish

bool Jsi_ValueIsNumber(Jsi_Interp *interp, Jsi_Value *pv)
{
    return (pv->vt == JSI_VT_NUMBER || (pv->vt == JSI_VT_OBJECT && pv->d.obj->ot == JSI_OT_NUMBER));
}

Project: jsish

static void insert_case4(Jsi_TreeEntry* n) {
    Jsi_TreeEntry* g = grandparent(n);
    if (n == n->parent->right && n->parent == g->left) {
        rotate_left(n->parent);
        n = n->left;
    } else if (n == n->parent->left && n->parent == g->right) {
        rotate_right(n->parent);
        n = n->right;
    }
    insert_case5(n);
}

Project: jsish

HashOneWordFind( Jsi_Hash *tablePtr,  const void *key)
{
     Jsi_HashEntry *hPtr;
    size_t hindex;

    hindex = RANDOM_INDEX(tablePtr, key);

    /*
     * Search all of the entries in the appropriate bucket.
     */
    for (hPtr = tablePtr->buckets[hindex]; hPtr != NULL;
        hPtr = hPtr->nextPtr) {
    if (hPtr->key.oneWordValue == key) {
        return hPtr;
    }
    }
    return NULL;
}

Project: jsish

static void jsiPop(Jsi_Interp* interp, int n) {
    int t = n;
    while (t > 0) {
        Assert((interp->framePtr->Sp-t)>=0);
/*        Jsi_Value *v = _jsi_STACKIDX(interp->framePtr->Sp-t);
         if (v->refCnt>1) puts("OO");*/
        jsiClearStack(interp,t);
        --t;
    }
    interp->framePtr->Sp -= n;
}

Project: jsish

static bool mysqlObjEqual(void *data1, void *data2)
{
    //SQLSIGASSERT(data1,DB);
    //SQLSIGASSERT(data2,DB);
    return (data1 == data2);
}

Project: jsish

static Jsi_RC _StringTrimCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this, // TODO: UTF
    Jsi_Value **ret, Jsi_Func *funcPtr, int ends)
{
    const char *tstr = " \t\n\r", *vstr;
    int vend,  n, tlen = Jsi_Strlen(tstr), clen, bLen;
    ChkString(_this, funcPtr, vstr, &clen, &bLen);
    
    Jsi_Value *tchars = Jsi_ValueArrayIndex(interp, args, skip);
    
    if (tchars) {
        tstr = Jsi_ValueToString(interp, tchars, NULL);
        tlen = Jsi_Strlen(tstr);
    }
    
    if (ends&1) {
        while (*vstr) {
            for (n=0; n<tlen; n++)
                if (tstr[n] == *vstr) break;
            if (n>=tlen) break;
            vstr++;
            clen--;
        }
    }
    vend = clen-1;
    if (ends&2) {
        for (; vend>=0; vend--) {
            for (n=0; n<tlen; n++)
                if (tstr[n] == vstr[vend]) break;
            if (n>=tlen) break;
        }
    }
    Jsi_DString dStr;
    Jsi_DSInit(&dStr);
    Jsi_UtfSubstr(vstr, 0, vend+1, &dStr);
    Jsi_ValueFromDS(interp, &dStr, ret);
    return JSI_OK;
}

Project: jsish

static bool interpObjIsTrue(void *data)
{
    InterpObj *fo = (InterpObj *)data;
    SIGASSERT(fo,INTERPOBJ);
    if (!fo->subinterp) return 0;
    else return 1;
}

Project: jsish

Jsi_RC Jsi_HashConf(Jsi_Hash *hashPtr, Jsi_MapOpts *opts, bool set)
{
    if (set)
        hashPtr->opts = *opts;
    else
        *opts = hashPtr->opts;
    return JSI_OK;
}

Project: jsish

Jsi_MapEntry* Jsi_MapEntryFind (Jsi_Map *mapPtr, const void *key){
    SIGASSERT(mapPtr, MAP);
    switch (mapPtr->typ) {
        case JSI_MAP_HASH: return (Jsi_MapEntry*)Jsi_HashEntryFind(mapPtr->v.hash, key);
        case JSI_MAP_TREE: return (Jsi_MapEntry*)Jsi_TreeEntryFind(mapPtr->v.tree, key);
        case JSI_MAP_LIST:
            return (Jsi_MapEntry*) (key == NULL? Jsi_ListGetFront(mapPtr->v.list) : Jsi_ListGetBack(mapPtr->v.list));
        case JSI_MAP_NONE: break;
    }
    return NULL;
}

Project: jsish

Jsi_Number Jsi_NumberNaN(void)
{
    return NAN;
}

Project: jsish

int Jsi_ValueInstanceOf( Jsi_Interp *interp, Jsi_Value* v1, Jsi_Value* v2)
{
    Jsi_Value *proto, *sproto;
    if (v1->vt != JSI_VT_OBJECT || v2->vt != JSI_VT_OBJECT  || v2->d.obj->ot != JSI_OT_FUNCTION)
        return 0;
    proto = Jsi_ValueObjLookup(interp, v2, "prototype", 0);
    if (!proto)
        return 0;
    sproto = v1->d.obj->__proto__ ;
    while (sproto) {
        if (sproto == proto)
            return 1;
        if (sproto->vt != JSI_VT_OBJECT)
            return 0;
        sproto = sproto->d.obj->__proto__;
    }
    return 0;
}

Project: jsish

static Jsi_OpCodes *code_with(jsi_Pstate *p, jsi_Pline *line, int withlen) { JSI_NEW_CODESLN(0,OP_WITH, withlen); }

Project: jsish

static Jsi_RC MySqlReconnectCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                         Jsi_Value **ret, Jsi_Func *funcPtr)
{
    MySqlObj *jdb  = _mysql_getDbHandle(interp, _this, funcPtr);
    if (!jdb) return JSI_ERROR;
    int oldMax = jdb->maxStmts;
    jdb->maxStmts = 0;
    mdbStmtLimit(jdb);
    jdb->maxStmts = oldMax;
    mysql_close(jdb->db);
    jdb->db = mysql_init(NULL);
    if (!mdbConnect(interp, jdb)) 
        return Jsi_LogError("reconnect failed: %s", mysql_error(jdb->db));
    return JSI_OK;
}

Project: jsish

Jsi_MapEntry* Jsi_MapSearchNext (Jsi_MapSearch *searchPtr){
    switch (searchPtr->typ) {
        case JSI_MAP_HASH: return (Jsi_MapEntry*)Jsi_HashSearchNext(&searchPtr->v.hash);
        case JSI_MAP_TREE: return (Jsi_MapEntry*)Jsi_TreeSearchNext(&searchPtr->v.tree);
        case JSI_MAP_LIST: return (Jsi_MapEntry*)Jsi_ListSearchNext(&searchPtr->v.list);
        case JSI_MAP_NONE: break;
    }
    return NULL;
}

Project: jsish

static Jsi_RC Jsi_DoneWebSocket(Jsi_Interp *interp)
{
    Jsi_UserObjUnregister(interp, &websockobject);
    Jsi_PkgProvide(interp, "WebSocket", -1, NULL);
    return JSI_OK;
}

Project: jsish

static Jsi_RC MySqlOnecolumnCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                          Jsi_Value **ret, Jsi_Func *funcPtr)
{
    Jsi_RC rc;
    MySqlObj *jdb;
    if (!(jdb = _mysql_getDbHandle(interp, _this, funcPtr))) return JSI_ERROR;
    Jsi_Value *vSql = Jsi_ValueArrayIndex(interp, args, 0);
    Jsi_DString dStr;
    Jsi_DSInit(&dStr);
    Jsi_DString sStr;
    Jsi_DSInit(&sStr);
    MyDbEvalContext sEval = {};
    const char *zSql = Jsi_ValueGetDString(interp, vSql, &dStr, 0);

    sEval.nocache = jdb->queryOpts.nocache;
    if (mdbEvalInit(interp, &sEval, jdb, zSql, &sStr, 0, 0) != JSI_OK)
        return JSI_ERROR;
    sEval.ret = *ret;
    sEval.tocall = NULL;
    int cnt = 0;


    if( JSI_OK==(rc = mdbEvalStep(&sEval)) ) {
        int nCol = sEval.prep->numCol;
        if (nCol>0)
            mdbEvalSetColumnValue(&sEval, 0, ret);
        cnt++;
    }
    mdbEvalFinalize(&sEval);
    if( rc==JSI_BREAK ) {
        rc = JSI_OK;
    }
    Jsi_DSFree(&dStr);
    return rc;
}

Project: jsish

Jsi_RC jsi_FuncArgsToString(Jsi_Interp *interp, Jsi_Func *f, Jsi_DString *dStr, int withTypes)
{
    if (f->type == FC_NORMAL) {
        int i;
        for (i = 0; i < f->argnames->count; ++i) {
            jsi_ArgValue *av = f->argnames->args+i;
            if (i) Jsi_DSAppend(dStr, ", ", NULL);
            Jsi_DSAppend(dStr,  jsi_ScopeStrsGet(f->argnames, i), NULL);
            if (withTypes && av) {
                Jsi_DString tStr = {};
                int atyp = av->type;
                if (av->defValue)
                    atyp &= ~(av->defValue->vt==JSI_VT_NULL?JSI_TT_NULL:(1<<av->defValue->vt));
                if (atyp) {
                    Jsi_DSAppend(dStr, ":", jsi_typeName(interp, atyp, &tStr), NULL);
                }
                Jsi_DSSetLength(&tStr, 0);
                if (av->defValue)
                    Jsi_DSAppend(dStr, "=", Jsi_ValueGetDString(interp, av->defValue, &tStr, 1), NULL);
                Jsi_DSFree(&tStr);
            }
        }
    } else if (f->cmdSpec && f->cmdSpec->argStr)
        Jsi_DSAppend(dStr, f->cmdSpec->argStr, NULL);
    return JSI_OK;
}

Project: jsish

Jsi_HashEntry* Jsi_HashSet(Jsi_Hash *tbl, const void *key, void *value) {
    Jsi_HashEntry *hPtr;
    bool isNew;
    hPtr = Jsi_HashEntryNew(tbl, key, &isNew);
    if (!hPtr) return hPtr;
    Jsi_HashValueSet(hPtr, value);
    return hPtr;
}

Project: jsish

static Jsi_RC FilesysCloseCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    UdfGet(udf, _this, funcPtr);
    fileObjErase(udf);
    Jsi_ValueMakeBool(interp, ret, 1);
    return JSI_OK;
}

Project: jsish

static Jsi_RC jsi_EnumToValue(Jsi_Interp *interp, Jsi_OptionSpec* spec, Jsi_Value **outValue, Jsi_DString *outStr, void *record, Jsi_Wide flags)
{
    uint i = 0, j, esiz = 0;
    int64_t n;
    char *s = (char*)(((char*)record) + spec->offset);
    const char **list = (const char**)spec->data;
    Jsi_OptionSpec* es=NULL, *esp = NULL;
    int fflags = (flags|spec->flags);
    int uflag = (fflags&JSI_OPT_ENUM_UNSIGNED);
    if (list && (fflags & JSI_OPT_ENUM_SPEC)) {
        esp = es = (typeof(es))list;
        while (es->id != JSI_OPTION_END) es++;
        list = es->init.STRKEY;
        esiz = es->size;
        es = (typeof(es))spec->data;
    }
    if (!list) 
        return Jsi_LogError("custom enum spec did not set data: %s", spec->name);
    if (outStr) {
        if (uflag) {
            switch (spec->size) {
                case 1: n = *(uint8_t*)s; break;
                case 2: n = *(uint16_t*)s; break;
                case 4: n = *(uint32_t*)s; break;
                case 8: n = *(uint64_t*)s; break;
                default: 
                    return Jsi_LogError("enum size %d must be 1, 2, 4, or 8: %s", spec->size, spec->name);
            }
        } else {
            switch (spec->size) {
                case 1: n = *(int8_t*)s; break;
                case 2: n = *(int16_t*)s; break;
                case 4: n = *(int32_t*)s; break;
                case 8: n = *(int64_t*)s; break;
                default: 
                    return Jsi_LogError("enum size %d must be 1, 2, 4, or 8: %s", spec->size, spec->name);
            }
        }
        if (spec->flags&JSI_OPT_FMT_NUMBER) {
            Jsi_DSPrintf(outStr, "%" PRIu64, (uint64_t)n);
            return JSI_OK;
        }
        
        if (es) {
            for (j=0; j<esiz && list[j]; j++) {
                if (n == esp[j].value) {
                    i = j;
                    break;
                }
                if (j>=esiz)
                    i = esiz;
            }
        } else
            for (i=0; i<n && list[i]; i++) ; /* Look forward til n */
        if (list[i])
            Jsi_DSAppendLen(outStr, list[i], -1);
        else if ((spec->flags&JSI_OPT_COERCE)) {
            Jsi_DSPrintf(outStr, "%" PRIu64, (uint64_t)n);
            return JSI_OK;
        } else
            return Jsi_LogError("enum has unknown value: %d", *s);
        return JSI_OK;
    }
#ifndef JSI_LITE_ONLY
    Jsi_DString dStr;
    Jsi_DSInit(&dStr);
    Jsi_RC rc = jsi_EnumToValue(interp, spec, NULL, &dStr, record, flags);
    if (rc == JSI_OK)
        Jsi_ValueMakeStringKey(interp, outValue, Jsi_DSValue(&dStr));
    Jsi_DSFree(&dStr);
    return rc;
#endif
    return JSI_ERROR;
}

Project: jsish

void* Jsi_Malloc(unsigned int size) {
    void *v = malloc(size);
    Assert(v);
    return v;
}

Project: jsish

Jsi_vtype Jsi_ValueTypeGet(Jsi_Value *pv) { return pv->vt; }

Project: jsish

void jsi_PstatePop(jsi_Pstate *ps)
{
    Jsi_Interp *interp = ps->interp;
    if (interp->cur_scope <= 0) {
        Jsi_LogBug("No more scope to pop");
        return;
    }
    jsi_ScopeStrsFree(interp, interp->scopes[interp->cur_scope]);
    interp->scopes[interp->cur_scope] = NULL;
    interp->cur_scope--;
}

Project: jsish

static Jsi_RC jsi_wsGetCmd(Jsi_Interp *interp, jsi_wsCmdObj *cmdPtr, jsi_wsPss* pss, struct lws *wsi,
    const char *inPtr, Jsi_Value *cmd, Jsi_DString *tStr)
{
    Jsi_RC jrc;
    Jsi_Value *retStr = Jsi_ValueNew1(interp);
    // 4 args: ws, id, url, query
    Jsi_Value *vpargs, *vargs[10];
    int n = 0;
    if (cmdPtr->deleted) return JSI_ERROR;
    vargs[n++] = Jsi_ValueNewObj(interp, cmdPtr->fobj);
    vargs[n++] = Jsi_ValueNewNumber(interp, (Jsi_Number)(pss?pss->wid:0));
    vargs[n++]  = Jsi_ValueNewStringDup(interp, inPtr);
    vargs[n++]  = (pss->query?pss->query:Jsi_ValueNewArray(interp, NULL, 0));
    vpargs = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, vargs, n, 0));
    Jsi_IncrRefCount(interp, vpargs);
    jrc = Jsi_FunctionInvoke(interp, cmd, vpargs, &retStr, NULL);
    Jsi_DecrRefCount(interp, vpargs);
    if (Jsi_InterpGone(interp))
        return JSI_ERROR;
    Jsi_DString dStr = {};
    const char *rstr = "";
    if (jrc != JSI_OK)
        rstr = "Error";
    else if (Jsi_ValueIsFalse(interp, retStr)) {
        rstr = "Access denied";
        jrc = JSI_ERROR;
    } else
        rstr = Jsi_ValueString(interp, retStr, NULL);
    if (rstr && tStr && Jsi_Strncmp(rstr, "!!!", 3)==0) {
        Jsi_DSAppend(tStr, rstr+3, NULL);
        jrc = JSI_CONTINUE;
    } else if (rstr && tStr && Jsi_Strncmp(rstr, ">>>", 3)==0) {
        Jsi_DSAppend(tStr, rstr+3, NULL);
        jrc = JSI_SIGNAL;
    } else if (rstr && rstr[0] != 0)
        jsi_wsServeString(pss, wsi, rstr, jrc==JSI_OK?0:404, NULL, NULL);
    else
        jrc = JSI_BREAK;
    Jsi_DecrRefCount(interp, retStr);
    Jsi_DSFree(&dStr);
    return jrc;
}

Project: jsish

int jsi_FuncSigsMatch(jsi_Pstate *pstate, Jsi_Func *f1, Jsi_Func *f2)
{
    // Skip where both functions have no types.
    if (f1->retType==0 && f1->argnames->typeCnt==0 && f1->argnames->varargs==0 &&
        f2->retType==0 && f2->argnames->typeCnt==0 && f2->argnames->varargs==0 &&
        pstate->interp->typeCheck.all==0)
        return 1;
    if (f1->retType != f2->retType)
        return 0;
    if (f1->argnames->count != f2->argnames->count)
        return 0;
    if (f1->argnames->typeCnt != f2->argnames->typeCnt)
        return 0;
    if (f1->argnames->varargs != f2->argnames->varargs)
        return 0;
    int i;
    for (i=0; i<f1->argnames->count; i++) {
        Jsi_ScopeStrs *a1 = f1->argnames, *a2 = f2->argnames;
        if (a1->args[i].type != a2->args[i].type)
            return 0;
        Jsi_Value *v1, *v2;
        v1 = a1->args[i].defValue;
        v2 = a2->args[i].defValue;
        if (v1==NULL && v2 == NULL)
            continue;
        if (v1==NULL || v2 == NULL)
            return 0;
        if (v1->vt != v2->vt)
            return 0;
        if (Jsi_ValueCmp(pstate->interp, v1, v2, 0))
            return 0;
    }
    return 1;
}

Project: jsish

static Jsi_RC jsi_wswebsocketObjFree(Jsi_Interp *interp, void *data)
{
    jsi_wsCmdObj *cmdPtr = (jsi_wsCmdObj*)data;
    WSSIGASSERT(cmdPtr,OBJ);
    cmdPtr->deleted = 1;
    struct lws_context *ctx = cmdPtr->context;
    if (ctx)
        lws_context_destroy(ctx);
    cmdPtr->context = NULL;
    cmdPtr->_->activeCnt--;
    jsi_wswebsocketObjErase(cmdPtr);
    _JSI_MEMCLEAR(cmdPtr);
    Jsi_Free(cmdPtr);
    return JSI_OK;
}

Project: jsish

static Jsi_OpCodes *code_debug(jsi_Pstate *p, jsi_Pline *line) { JSI_NEW_CODESLN(0,OP_DEBUG, 0); }

Project: jsish

Jsi_RC jsi_AliasInvoke(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    AliasCmd *ac = (AliasCmd *)funcPtr->cmdSpec->reserved[2];
    Jsi_Interp *dinterp = ac->dinterp;
    Jsi_Value *nargs = NULL;
    int inc=0, argc = Jsi_ValueGetLength(interp, args);
    if (!ac) {
        Jsi_LogBug("BAD ALIAS INVOKE OF DELETED");
        return JSI_ERROR;
    }
    SIGASSERT(ac,ALIASCMD);
    bool isthrd = (interp != dinterp && interp->threadId != dinterp->threadId);
    Jsi_Value *nrPtr = Jsi_ValueNew1(dinterp);

     if (argc == 0 && ac->args)
        nargs = ac->args;
     else if (argc) {
        if (dinterp == interp)
            Jsi_ValueCopy(interp, nrPtr, args);
        else if (Jsi_CleanValue(interp, dinterp, args, &nrPtr) != JSI_OK)
            return JSI_ERROR;
        if (ac->args && Jsi_ValueGetLength(dinterp, ac->args)) {
            nargs = Jsi_ValueArrayConcat(dinterp, ac->args, nrPtr);
            Jsi_IncrRefCount(dinterp, nargs);
            inc=1;
        } else {
            nargs = nrPtr;
        }
    }

    if (isthrd) {
        /* Post to thread event in sub-interps queue. */
        if (Jsi_MutexLock(interp, dinterp->QMutex) != JSI_OK)
            return JSI_ERROR;

       /* Is an async call. */
        InterpStrEvent *se, *s = (InterpStrEvent *)Jsi_Calloc(1, sizeof(*s));
        // TODO: is s->data inited?
        Jsi_DSInit(&s->data);
        Jsi_ValueGetDString(interp, nargs, &s->data, JSI_OUTPUT_JSON);
        if (inc)
            Jsi_DecrRefCount(dinterp, nargs);
        Jsi_DecrRefCount(dinterp, nrPtr);
        s->acfunc = ac->func;
        Jsi_IncrRefCount(dinterp, ac->func);
        se = dinterp->interpStrEvents;
        if (!se)
            dinterp->interpStrEvents = s;
        else {
            while (se->next)
                se = se->next;
            se->next = s;
        }

        Jsi_MutexUnlock(interp, dinterp->QMutex);
        return JSI_OK;
    }

    if (dinterp != interp) {
        if (interp->subOpts.mutexUnlock) Jsi_MutexUnlock(interp, interp->Mutex);
        if (Jsi_MutexLock(interp, dinterp->Mutex) != JSI_OK) {
            if (interp->subOpts.mutexUnlock) Jsi_MutexLock(interp, interp->Mutex);
            return JSI_ERROR;
        }
    }
    ac->refCount++;
    Jsi_Value *srPtr, **srpPtr = ret;
    if (dinterp != interp) {
        srPtr = Jsi_ValueNew1(dinterp);
        srpPtr = &srPtr;
    }
    Jsi_RC rc = Jsi_FunctionInvoke(dinterp, ac->func, nargs, srpPtr, NULL);
    ac->refCount--;
    if (inc)
        Jsi_DecrRefCount(dinterp, nargs);
    Jsi_DecrRefCount(dinterp, nrPtr);
    if (dinterp != interp) {
        Jsi_MutexUnlock(interp, dinterp->Mutex);
        if (interp->subOpts.mutexUnlock && Jsi_MutexLock(interp, interp->Mutex) != JSI_OK) {
            return JSI_ERROR;
        }
    }
    if (dinterp != interp) {
        Jsi_CleanValue(dinterp, interp, *srpPtr, ret);
        Jsi_DecrRefCount(dinterp, srPtr);
        if (rc != JSI_OK && dinterp->errMsgBuf[0] && interp != dinterp) {
            Jsi_Strcpy(interp->errMsgBuf, dinterp->errMsgBuf);
            interp->errLine = dinterp->errLine;
            interp->errFile = dinterp->errFile;
            dinterp->errMsgBuf[0] = 0;
        }
    }
    return rc;
}

Project: jsish

static Jsi_OpCodes *code_bxor() { JSI_NEW_CODES(0,OP_BXOR, 0); }

Project: jsish

static Jsi_OpCodes *code_array(jsi_Pstate *p, jsi_Pline *line, int c) { JSI_NEW_CODESLN(0,OP_ARRAY, c); }

Project: jsish

static JSITHREADRET NewInterpThread(void* iPtr)
{
    int rc = JSI_OK;
    InterpObj *udf = (InterpObj *)iPtr;
    Jsi_Interp *interp = udf->subinterp;
    interp->threadId = Jsi_CurrentThread();
    if (interp->scriptStr)
        rc = Jsi_EvalString(interp, interp->scriptStr, 0);
    else if (interp->scriptFile) {
        if (!interp->debugOpts.debugCallback) // Debug will use Interp.source() instead.
            Jsi_EvalFile(interp, interp->scriptFile, 0);
    } else {
        Jsi_LogBug("no eval");
    }
    if (rc != JSI_OK) {
        Jsi_LogError("eval failure");
        interp->threadErrCnt++;
        if (Jsi_MutexLock(interp, interp->Mutex) != JSI_OK)
            return NULL;
        Jsi_MutexUnlock(interp, interp->Mutex);
    }
    interpObjErase(udf);
#ifndef __WIN32
    /* TODO: should we wait/notify parent??? */
    pthread_detach(pthread_self());
    return NULL;
#endif
}

Project: jsish

static Jsi_OpCodes *code_push_null() { JSI_NEW_CODES(0,OP_PUSHNULL, 0); }

Project: jsish

int Jsi_HexStr(const uchar *data, int len, Jsi_DString *dStr, bool decode) {
    int olen = (decode?(len/2+1):(len*2+1));
    Jsi_DSSetLength(dStr, olen);
    if (!decode)
        return jsi_FromHexStr((const char*)data, (uchar*)Jsi_DSValue(dStr));
    jsi_ToHexStr((const uchar*)data, len, Jsi_DSValue(dStr));
    return olen-1;
}

Project: jsish

Jsi_ListEntry* Jsi_ListSearchFirst (Jsi_List *list, Jsi_ListSearch *searchPtr, int flags)
{
    SIGASSERT(list, LIST);
    searchPtr->flags = flags;
    Jsi_ListEntry *lptr;
    if (flags & JSI_LIST_REVERSE) {
        lptr = Jsi_ListGetBack(list);
        searchPtr->nextEntryPtr = (lptr?Jsi_ListEntryPrev(lptr):NULL);
    } else {
        lptr = Jsi_ListGetFront(list);
        searchPtr->nextEntryPtr = (lptr?Jsi_ListEntryNext(lptr):NULL);
    }
    return lptr;
}

Project: jsish

Jsi_TreeEntry *Jsi_TreeSet(Jsi_Tree *treePtr, const void *key, void *value)
{
    Jsi_TreeEntry *hPtr;
    bool isNew;
    hPtr = Jsi_TreeEntryNew(treePtr, key, &isNew);
    if (!hPtr) return hPtr;
    Jsi_TreeValueSet(hPtr, value);
    return hPtr;
}

Project: jsish

void* Jsi_MapGet(Jsi_Map *mapPtr, const void *key, int flags){
    SIGASSERT(mapPtr, MAP);
    switch (mapPtr->typ) {
        case JSI_MAP_HASH: return Jsi_HashGet(mapPtr->v.hash, (void*)key, flags);
        case JSI_MAP_TREE: return Jsi_TreeGet(mapPtr->v.tree, (void*)key, flags);
        case JSI_MAP_LIST: {
            Jsi_ListEntry* lptr = (key == NULL? Jsi_ListGetFront(mapPtr->v.list) : Jsi_ListGetBack(mapPtr->v.list));
            if (lptr)
                return Jsi_ListValueGet(lptr);
            break;
        }
        case JSI_MAP_NONE: break;
    }
    return NULL;
}

Project: jsish

void *Jsi_StackHead(Jsi_Stack *stack)
{
    if (stack->len == 0)
        return NULL;
    return stack->vector[0];
}

Project: jsish

void *Jsi_CommandNewObj(Jsi_Interp *interp, const char *name, const char *arg1, const char *opts, const char *var) {
    char buf[JSI_BUFSIZ];
    if (arg1)
        snprintf(buf, sizeof(buf), "%s%snew %s('%s', %s);", var?var:"", var?"=":"return ", name, arg1, opts?opts:"null");
    else
        snprintf(buf, sizeof(buf), "%s%snew %s(%s);", var?var:"", var?"=":"return ", name, opts?opts:"null");
    int rc = Jsi_EvalString(interp, buf, 0);
    if (rc != JSI_OK)
        return NULL;
    Jsi_Value *vObj = interp->retValue;
    if (var)
        vObj = Jsi_NameLookup(interp, var);
    if (!vObj)
        return NULL;
    return Jsi_UserObjGetData(interp, vObj, NULL);
}

Project: jsish

Jsi_GetIndex( Jsi_Interp *interp, const char *str,
    const char **tablePtr, const char *msg, int flags,
    int *indexPtr)
{
  const char *msg2 = "unknown ";
  char **cp, *c;
  int cond, index = -1, slen, i, dup = 0;
  int exact = (flags & JSI_CMP_EXACT);
  int nocase = (flags & JSI_CMP_NOCASE);
  slen = Jsi_Strlen(str);
 /* if (slen==0) 
        return Jsi_LogError("empty option %s %s", msg, str);*/
  cp = (char**)tablePtr;
  i = -1;
  while (*cp != 0) {
    i++;
    c = *cp;
    if (c[0] != str[0]) { cp++; continue; }
    if (!nocase)
        cond = (exact ? Jsi_Strcmp(c,str) : Jsi_Strncmp(c,str,slen));
    else {
        cond = (exact ? Jsi_Strncasecmp(c,str, -1) : Jsi_Strncasecmp(c,str,slen));
    }
    if (cond == 0) {
      if (index<0) {
        index = i;
      } else {
        dup = 1;
        break;
      }
    }
    cp++;
  }
  if (index >= 0 && dup == 0) {
    *indexPtr = index;
    return JSI_OK;
  }
  if (exact && (dup || index<=0)) {
    if (interp != NULL) {
      msg2 = (index>=0? "unknown ":"duplicate ");
    }
    goto err;
  }
  cp = (char**)tablePtr;
  i = -1;
  dup = 0;
  index = -1;
  while (*cp != 0) {
    i++;
    c = *cp;
    if (c[0] == str[0] && Jsi_Strncmp(c,str, slen) == 0) {
      if (index<0) {
        index = i;
        if (slen == (int)Jsi_Strlen(c))
            break;
      } else {
        if (interp != NULL) {
          msg2 = "ambiguous ";
        }
        goto err;
      }
    }
    cp++;
  }
  if (index >= 0 && dup == 0) {
    *indexPtr = index;
    return JSI_OK;
  }
err:
  if (interp != NULL) {
    Jsi_DString dStr = {};
    Jsi_DSAppend(&dStr, msg2, msg, " \"", str, "\" not one of: ", NULL);
    cp = (char**)tablePtr;
    while (*cp != 0) {
      c = *cp;
      Jsi_DSAppend(&dStr, c, NULL);
      Jsi_DSAppend(&dStr, " ", NULL);
      cp++;
    }
    Jsi_LogError("%s", Jsi_DSValue(&dStr));
    Jsi_DSFree(&dStr);
  }
  return JSI_ERROR;
}

Project: jsish

static bool jsi_wswebsocketObjEqual(void *data1, void *data2)
{
    return (data1 == data2);
}

Project: jsish

static Jsi_OptionSpec * jsi_GetCachedOptionSpecs(Jsi_Interp *interp, const Jsi_OptionSpec *staticSpecs)
{
    Jsi_OptionSpec *cachedSpecs;
    Jsi_Hash *specCacheTablePtr;
    Jsi_HashEntry *entryPtr;
    bool isNew;

    specCacheTablePtr = (Jsi_Hash*)Jsi_InterpGetData(interp, "jsi:OptionSpec", NULL);
    if (specCacheTablePtr == NULL) {
        specCacheTablePtr = Jsi_HashNew(interp, JSI_KEYS_ONEWORD, 0);
        Jsi_InterpSetData(interp, "jsi:OptionSpec", specCacheTablePtr, jsi_DeleteSpecCacheTable);
    }
    
    entryPtr = Jsi_HashEntryNew(specCacheTablePtr, (char *) staticSpecs, &isNew);
    if (isNew) {
        unsigned int entrySpace = sizeof(Jsi_OptionSpec);
        const Jsi_OptionSpec *staticSpecPtr;

        for (staticSpecPtr=staticSpecs; staticSpecPtr->id>=JSI_OPTION_BOOL && staticSpecPtr->id!=JSI_OPTION_END; staticSpecPtr++)
            entrySpace += sizeof(Jsi_OptionSpec);
        assert(staticSpecPtr->id==JSI_OPTION_END);

        cachedSpecs = (Jsi_OptionSpec *) Jsi_Malloc(entrySpace);
        memcpy((void *) cachedSpecs, (void *) staticSpecs, entrySpace);
        Jsi_HashValueSet(entryPtr, cachedSpecs);

    } else {
        cachedSpecs = (Jsi_OptionSpec *) Jsi_HashValueGet(entryPtr);
    }

    return cachedSpecs;
}

Project: jsish

Jsi_ScopeStrs *jsi_argInsert(jsi_Pstate *pstate, Jsi_ScopeStrs *a, const char *name, Jsi_Value *defValue, jsi_Pline *lPtr, bool prepend)
{
    Jsi_Interp *interp = pstate->interp;
    if (!a)
        a = jsi_ScopeStrsNew();
    pstate->args = a;
    int atyp = pstate->argType;
    if (defValue) {
        int vt = defValue->vt;
        if (vt == JSI_VT_NULL)
            vt = JSI_TT_NULL;
        else if (vt == JSI_VT_UNDEF && defValue->d.num==1)
            vt = JSI_TT_VOID;
        else if (vt == JSI_VT_OBJECT && defValue->d.obj->ot==JSI_OT_STRING)
            vt = JSI_TT_STRING;
        else
            vt = (1<<defValue->vt);
        atyp |= vt;
    }
    jsi_ScopeStrsPush(interp, a, name, atyp);
    pstate->argType = 0;
    a->args[a->count-1].defValue = defValue;
    a->argCnt++;
    if (prepend) {
        jsi_ArgValue t;
        int i, end = a->argCnt-1;
        t = a->args[end];
        for (i=end-1; i>=0; i--)
            a->args[i+1]=a->args[i];
        a->args[0] = t;
    }
    jsi_Pline *opl = interp->parseLine;
    interp->parseLine = lPtr;
    if (defValue) {
        Jsi_IncrRefCount(interp, defValue);
        if (a->firstDef==0)
            a->firstDef = a->argCnt;
            if (atyp)
                jsi_ArgTypeCheck(interp, atyp, defValue, "default value", name, a->argCnt, NULL, 1);
    } else {
        if (a->firstDef && (interp->typeCheck.run || interp->typeCheck.all) )
            Jsi_LogWarn("expected default value in argument list: \"%s\"", name);
    }
    interp->parseLine = opl;
    return a;
}

Project: jsish

Jsi_RC Jsi_JSONParseFmt(Jsi_Interp *interp, Jsi_Value **ret, const char *fmt, ...) 
{
    va_list argList;
    uint n;
    char buf[JSI_BUFSIZ], *cp = buf;
    Jsi_DString dStr = {};
    va_start(argList, fmt);
    n = vsnprintf(buf, sizeof(buf), fmt, argList);
    if (n>JSI_MAX_ALLOC_BUF) {
        Jsi_LogError("Jsi_JSONParseFmt error: rc = %d", n);
        va_end(argList);
        return JSI_ERROR;
    }
    if (n >= sizeof(buf)) {
        uint m;
        Jsi_DSSetLength(&dStr, n+1);
        m = vsnprintf(Jsi_DSValue(&dStr), n+1, fmt, argList);
        assert(m == n);
        JSI_NOWARN(m);
        cp = Jsi_DSValue(&dStr);
    }
    va_end(argList);
    return Jsi_JSONParse(interp, cp, ret, 0);
}

Project: jsish

void Jsi_HashValueSet(Jsi_HashEntry *h, void *value)
{
    h->clientData = value;
}

Project: jsish

void jsi_FuncObjFree(Jsi_FuncObj *fobj)
{
    if (fobj->scope)
        jsi_ScopeChainFree(fobj->interp, fobj->scope);
    if (fobj->bindArgs)
        Jsi_DecrRefCount(fobj->interp, fobj->bindArgs);
    if (fobj->bindFunc)
        Jsi_DecrRefCount(fobj->interp, fobj->bindFunc);
    if (fobj->func)
        jsi_FuncFree(fobj->interp, fobj->func);
    _JSI_MEMCLEAR(fobj);
    Jsi_Free(fobj);
}

Project: jsish

static bool mysqlObjIsTrue(void *data)
{
    MySqlObj *jdb = (MySqlObj*)data;
    SQLSIGASSERT(jdb,DB);
    if (!jdb->db) return 0;
    else return 1;
}

Project: jsish

static Jsi_RC jsi_ValueToBitfield(Jsi_Interp *interp, Jsi_OptionSpec* spec, Jsi_Value *inValue,
    const char *inStr, void *record, Jsi_Wide flags)
{
    Jsi_csgset *bsget = spec->init.OPT_BITS;
    Jsi_Interp *d = interp;
    int idx = spec->idx;
    uchar *data = (uchar*)record;
    Jsi_Number num;
    Jsi_OptionSpec* enumSpec = (typeof(enumSpec))spec->data;

    if (!d || !bsget || idx<0) 
        return Jsi_LogBug("invalid bitfield");
    if (enumSpec) {
        struct numStruct { int64_t numVal; } nval = {};
        Jsi_OptionSpec eSpec[] = {
            JSI_OPT(CUSTOM, typeof(nval), numVal, .help=spec->help, .flags=JSI_OPT_ENUM_SPEC,
                .custom=Jsi_Opt_SwitchEnum, .data=(void*)enumSpec, .info=0, .tname=spec->tname, .value=0, .bits=0, .boffset=8*sizeof(int64_t) ),
            JSI_OPT_END(typeof(nval))
        };
        if (JSI_OK != jsi_ValueToEnum(interp, eSpec, inValue, inStr, (void*)&nval, flags))
            return JSI_ERROR;
        num = (Jsi_Number)nval.numVal;
    } else if (inStr) {
        if (Jsi_GetDouble(interp, inStr, &num) != JSI_OK)
            return JSI_ERROR;
    } else {
        if (inValue && !Jsi_ValueIsNumber(interp, inValue)) 
            return JSI_ERROR;
        Jsi_ValueGetNumber(interp, inValue, &num);
    }
    int64_t inum = (int64_t)num;
    return (*bsget)(interp, data, &inum, spec, idx, 1);
}

Project: jsish

static void dbCollateNeeded(
    void *cd,
    sqlite3 *db,
    int enc,
    const char *zName
) {
    int rc;
    Jsi_Db *jdb = (Jsi_Db*)cd;
    Jsi_Interp *interp = jdb->interp;
    Jsi_Value *vpargs, *items[2], *ret;
    items[0] = Jsi_ValueNewObj(interp, jdb->fobj);
    items[1] = Jsi_ValueMakeStringDup(interp, NULL, zName);
    vpargs = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, items, 2, 0));
    Jsi_IncrRefCount(interp, vpargs);
    ret = Jsi_ValueNew1(interp);
    rc = Jsi_FunctionInvoke(interp, jdb->onNeedCollate, vpargs,& ret, NULL);
    Jsi_DecrRefCount(interp, vpargs);
    Jsi_DecrRefCount(interp, ret);
    if (rc != JSI_OK)
        jdb->errCnt++;

}

Project: jsish

static int StringCompare(Jsi_Tree *treePtr, const void *key1, const void *key2)
{
    return Jsi_DictionaryCompare((char*)key1, (char*)key2);
    //return Jsi_Strcmp((char*)key1, (char*)key2);
}

Project: jsish

static Jsi_TreeEntry *TreeArrayCreate(Jsi_Tree *treePtr, const void *key, bool *newPtr)
{
    Jsi_TreeEntry *hPtr;
    size_t size;

    if ((hPtr = Jsi_TreeEntryFind(treePtr, key))) {
        if (newPtr)
            *newPtr = 0;
        return hPtr;
    }
    if (newPtr)
        *newPtr = 1;
    size = sizeof(Jsi_TreeEntry) + treePtr->keyType; /*- sizeof(jsi_TreeKey);*/
    hPtr = (Jsi_TreeEntry*)Jsi_Calloc(1,size);
    SIGINIT(hPtr,TREEENTRY);
    hPtr->typ = JSI_MAP_TREE;
    hPtr->treePtr = treePtr;
    hPtr->value = 0;
    memcpy(hPtr->key.string, key, treePtr->keyType);
    treePtr->numEntries++;
    return hPtr;
}

Project: jsish

static void jsi_InteractiveSignal(int sig){
    if (jsi_interactiveInterp)
        jsi_interactiveInterp->interrupted = 1;
}

Project: jsish

int Jsi_Write(Jsi_Interp *interp, Jsi_Channel chan, const char *bufPtr, int slen) {
    if (chan->fsPtr==0 || !chan->fsPtr->writeProc) return -1;
    return chan->fsPtr->writeProc(chan, bufPtr, slen);
}

Project: jsish

Jsi_Interp *jsi_DoExit(Jsi_Interp *interp, int rc)
{
    if (rc<0 || rc>127) rc = 127;
    if (!interp || !interp->opts.no_exit) {
        if (rc) {
            Jsi_Flush(interp, jsi_Stdout);
            Jsi_Flush(interp, jsi_Stderr);
        }
        exit(rc);
    }
    fprintf(stderr, "ignoring attempted exit: may cause a crash\n");
    if (interp) interp->deleting = 1;
    return NULL;
}