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

void jsiFlagDebugValues(Jsi_Interp *interp, Jsi_Obj *obj)
{
    Jsi_Value *v;
    int oflags;
    if (obj->ot != JSI_OT_OBJECT && obj->ot != JSI_OT_ARRAY)
        return;
    if (obj->tree) {
        Jsi_TreeEntry *hPtr;
        Jsi_TreeSearch srch;
        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 == NULL || v->sig != JSI_SIG_VALUE) continue;
            oflags = v->VD.flags;
            v->VD.flags |= (MDB_VISITED|MDB_INOBJ);
            if (oflags&MDB_VISITED || v->vt != JSI_VT_OBJECT)
                continue;
            jsiFlagDebugValues(interp, v->d.obj);
        }
    }
    if (obj->arr) {
        uint i;
        for (i=0; i<obj->arrCnt; i++) {
            v = obj->arr[i];
            if (v == NULL || v->sig != JSI_SIG_VALUE) continue;
            oflags = v->VD.flags;
            v->VD.flags |= (MDB_VISITED|MDB_INOBJ);
            if (oflags&MDB_VISITED || v->vt != JSI_VT_OBJECT)
                continue;
            jsiFlagDebugValues(interp, v->d.obj);
        }
    }
}

Project: jsish

static Jsi_RC SqliteTransactionCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                                Jsi_Value **ret, Jsi_Func *funcPtr)
{
    int rc;
    Jsi_Db *jdb;

    int argc = Jsi_ValueGetLength(interp, args);
    if (!(jdb = dbGetDbHandle(interp, _this, funcPtr))) return JSI_ERROR;

    Jsi_Value *pScript;
    const char *zBegin = "SAVEPOINT _jsi_transaction";

    if( jdb->nTransaction==0 && argc==2 ) {
        Jsi_Value *arg = Jsi_ValueArrayIndex(interp, args, 0);
        static const char *TTYPE_strs[] = {
            "deferred",   "exclusive",  "immediate", 0
        };
        enum TTYPE_enum {
            TTYPE_DEFERRED, TTYPE_EXCLUSIVE, TTYPE_IMMEDIATE
        };
        int ttype;
        if( Jsi_ValueGetIndex(interp, arg, TTYPE_strs, "transaction type",
                              0, &ttype) ) {
            return JSI_ERROR;
        }
        switch( (enum TTYPE_enum)ttype ) {
        case TTYPE_DEFERRED:    /* no-op */
            ;
            break;
        case TTYPE_EXCLUSIVE:
            zBegin = "BEGIN EXCLUSIVE";
            break;
        case TTYPE_IMMEDIATE:
            zBegin = "BEGIN IMMEDIATE";
            break;
        }
    }
    pScript = Jsi_ValueArrayIndex(interp, args, argc-1);
    if(!Jsi_ValueIsFunction(interp, pScript)) 
        return Jsi_LogError("expected function");

    /* Run the SQLite BEGIN command to open a transaction or savepoint. */
    jdb->disableAuth++;
    rc = sqlite3_exec(jdb->db, zBegin, 0, 0 ,0);
    jdb->disableAuth--;
    if( rc!=SQLITE_OK ) 
        return Jsi_LogError("%s", sqlite3_errmsg(jdb->db));
    jdb->nTransaction++;

    /* Evaluate the function , then
    ** call function dbTransPostCmd() to commit (or rollback) the transaction
    ** or savepoint.  */
    Jsi_RC rv = Jsi_FunctionInvoke(interp, pScript, NULL, NULL, NULL);
    rv = dbTransPostCmd(jdb, interp, rv);
    return rv;
}

Project: jsish

bool jsi_StrIsBalanced(char *str) {
    int cnt = 0, quote = 0;
    char *cp = str;
    while (*cp) {
        switch (*cp) {
        case '\\':
            cp++;
            break;
        case '{': case '(': case '[':
            cnt++;
            break;
        case '\'': case '\"':
            quote++;
            break;
        case '}': case ')': case ']':
            cnt--;
            break;
        }
        if (*cp == 0)
            break;
        cp++;
    }
    return ((quote%2) == 0 && cnt <= 0);
}

Project: jsish

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

Project: jsish

int Jsi_Stat(Jsi_Interp *interp, Jsi_Value* path, Jsi_StatBuf *buf) {
    void *data;
    Jsi_Filesystem *fsPtr = Jsi_FilesystemForPath(interp, path, &data);
    if (fsPtr == NULL || !fsPtr->statProc) return -1;
    return fsPtr->statProc(interp, path, buf);
}

Project: jsish

int Jsi_StackSize(Jsi_Stack *stack)
{
    return stack->len;
}

Project: jsish

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

Project: jsish

static Jsi_TreeEntry *OneWordCreate( 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);
    hPtr = (Jsi_TreeEntry*)Jsi_Calloc(1,size);
    SIGINIT(hPtr,TREEENTRY);
    hPtr->typ = JSI_MAP_TREE;
    hPtr->treePtr = treePtr;
    hPtr->value = 0;
    hPtr->key.oneWordValue = (void *)key;
    treePtr->numEntries++;
    return hPtr;
}

Project: jsish

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

Project: jsish

void Jsi_TreeDelete (Jsi_Tree *treePtr)
{
    SIGASSERTV(treePtr, TREE);
    if (treePtr->flags.destroyed)
        return;
    //Jsi_TreeClear(treePtr);
    treePtr->flags.destroyed = 1;
    destroy_node(treePtr->opts.interp, treePtr->root);
    _JSI_MEMCLEAR(treePtr);
    Jsi_Free(treePtr);
}

Project: jsish

static void jsiLNGetMatches(const char *str, linenoiseCompletions *lc) {
    char buf[JSI_BUFSIZ], pre[JSI_BUFSIZ], hpre[6] = {};
    const char *cp, *fnam = "Info.completions";
    int i = 0, len;
    int rc, isfile = 0, start = 0, end = Jsi_Strlen(str);
    Jsi_Interp* interp = jsi_interactiveInterp;
    if (!Jsi_Strncmp(str, "help ", 5)) {
        Jsi_Strcpy(hpre, "help ");
        str += 5;
        end -= 5;
    }
    if (end<=0) return;
    Jsi_Strncpy(buf, str, sizeof(buf)-1);
    buf[sizeof(buf)-1] = 0;
    pre[0] = 0;
    if (end<=3 && !Jsi_Strncmp(str, "help", end)) {
        linenoiseAddCompletion(lc, "help");
        return;
    }
    if (!completeValues)
        completeValues = Jsi_ValueNew1(interp);
    Jsi_Value *func = interp->onComplete;
    if (func == NULL || !Jsi_ValueIsFunction(interp, func)) {
        for (i=0; jsiFilePreCmds[i]; i++)
            if (!Jsi_Strncmp(buf, jsiFilePreCmds[i], Jsi_Strlen(jsiFilePreCmds[i]))) break;
        if (jsiFilePreCmds[i] && ((cp=Jsi_Strrchr(buf, '(')) && (cp[1]=='\"' || cp[1]=='\''))) {
            Jsi_Strcpy(pre, buf);
            pre[cp-buf+2] = 0;
            snprintf(buf, sizeof(buf), "%s*%s", cp+2, (buf[0]=='s'?".js*":""));
            isfile = 1;
            fnam = "File.glob";
        }
    }
    func = Jsi_NameLookup(interp, fnam);
    if (func && Jsi_ValueIsFunction(interp, func)) {
        //printf("PATTERN: %s\n", str);
        Jsi_Value *items[3] = {};;
        i = 0;
        items[i++] = Jsi_ValueNewStringDup(interp, buf);
        if (!isfile) {
            items[i++] = Jsi_ValueNewNumber(interp, (Jsi_Number)start);
            items[i++] = Jsi_ValueNewNumber(interp, (Jsi_Number)end);
        }
        Jsi_Value *args = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, items, i, 0));
        Jsi_IncrRefCount(interp, args);
        rc = Jsi_FunctionInvoke(interp, func, args, &completeValues, interp->csc);
        Jsi_DecrRefCount(interp, args);
        if (rc != JSI_OK) {
            fprintf(stderr, "bad completion: %s %d %d\n", str?str:"", start, end);
            return;
        }
        const char *name;
        Jsi_Interp* interp = jsi_interactiveInterp;
        if (completeValues == NULL || !Jsi_ValueIsArray(interp, completeValues))
            return;
        Jsi_Value **arr = completeValues->d.obj->arr;
        int aLen = completeValues->d.obj->arrCnt;
        i = 0;
        while (i<aLen)
        {
            name = Jsi_ValueString(interp, arr[i], &len);
            if (name) {
                if (!pre[0] && !hpre[0])
                    linenoiseAddCompletion(lc, name);
                else {
                    snprintf(buf, sizeof(buf), "%s%s%s", hpre, pre, name);
                    linenoiseAddCompletion(lc, buf);
                }
            }
            i++;
        }
    }
}

Project: jsish

bool Jsi_ValueIsFalse(Jsi_Interp *interp, Jsi_Value *v)
{
    if (v->vt == JSI_VT_BOOL)  return v->d.val ? 0:1;
    return 0;
}

Project: jsish

Jsi_OptionsSet(Jsi_Interp *interp, Jsi_OptionSpec *specs, void* rec, const char *option, Jsi_Value *valuePtr, Jsi_Wide flags)
{
    char *record = (char*)rec;
    Jsi_OptionSpec *specPtr;
    specs = jsi_GetCachedOptionSpecs(interp, specs);
    const char *cp = NULL, *cb = NULL;
    bool isSafe = interp->isSafe;
    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_OptionsSet(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_OptionsSet(interp, subSpec, (void*)s, subSpec->name, valuePtr, flags) != JSI_OK)
                return JSI_ERROR;
        } else {
            if (Jsi_OptionsSet(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)
        return JSI_ERROR;
    return jsi_SetOption(interp, specPtr, option, record, valuePtr, flags, isSafe);
}

Project: jsish

static Jsi_OpCodes *code_shf(int right) { JSI_NEW_CODES(0,OP_SHF, right); }

Project: jsish

Jsi_RC jsi_PkgDumpInfo(Jsi_Interp *interp, const char *name, Jsi_Value **ret) {
    jsi_PkgInfo *ptr;
    Jsi_HashEntry *hPtr = Jsi_HashEntryFind(interp->packageHash, name);
    if (hPtr && ((ptr = (jsi_PkgInfo*)Jsi_HashValueGet(hPtr)))) {
        Jsi_Obj *nobj = Jsi_ObjNew(interp);
        Jsi_ValueMakeObject(interp, ret, nobj);
        Jsi_ObjInsert(interp, nobj, "name", Jsi_ValueNewStringDup(interp, name), 0);
        Jsi_ObjInsert(interp, nobj, "version", Jsi_ValueNewNumber(interp, ptr->version), 0);
        Jsi_ObjInsert(interp, nobj, "lastReq", Jsi_ValueNewNumber(interp, ptr->lastReq), 0);
        char buf[JSI_MAX_NUMBER_STRING*2];
        jsi_VersionNormalize(ptr->version, buf, sizeof(buf));
        Jsi_ObjInsert(interp, nobj, "verStr", Jsi_ValueNewStringDup(interp, buf), 0);
        const char *cp = (ptr->loadFile?ptr->loadFile:"");
        Jsi_ObjInsert(interp, nobj, "loadFile", Jsi_ValueNewStringDup(interp, cp), 0);
        Jsi_Value *fval2, *fval = Jsi_NameLookup(interp, name);
        if (!fval || !Jsi_ValueIsFunction(interp, fval))
            fval = Jsi_ValueNewNull(interp);
        Jsi_ObjInsert(interp, nobj, "func", fval, 0);
        fval = ptr->popts.info;
        if (!fval) fval = interp->NullValue;
        if (!Jsi_ValueIsObjType(interp, fval, JSI_OT_FUNCTION))
            Jsi_ObjInsert(interp, nobj, "info", fval, 0);
        else {
            fval2 = Jsi_ValueNew1(interp);
            Jsi_RC rc = Jsi_FunctionInvoke(interp, fval, NULL, &fval2, NULL);
            if (rc != JSI_OK)
                Jsi_LogWarn("status call failed");
            Jsi_ObjInsert(interp, nobj, "info", fval2, 0);
            Jsi_DecrRefCount(interp, fval2);
        }
        fval = interp->NullValue;
        if (ptr->popts.spec && ptr->popts.data) {
            fval = Jsi_ValueNew1(interp);
            Jsi_OptionsConf(interp, ptr->popts.spec, ptr->popts.data, NULL, &fval, 0);
        }
        Jsi_ObjInsert(interp, nobj, "status", fval, 0);
        if (fval != interp->NullValue)
            Jsi_DecrRefCount(interp, fval);

        fval = Jsi_ValueNew1(interp);
        Jsi_OptionsConf(interp, jsiModuleOptions, &ptr->popts.modConf, NULL, &fval, 0);
        Jsi_ObjInsert(interp, nobj, "moduleOpts", fval, 0);
        Jsi_DecrRefCount(interp, fval);

        return JSI_OK;
    }
    return JSI_ERROR;
}

Project: jsish

void *Jsi_HashGet(Jsi_Hash *tbl, const void *key, int flags) {
    Jsi_HashEntry *hPtr;
    hPtr = Jsi_HashEntryFind(tbl, key);
    if (!hPtr)
        return NULL;
    return Jsi_HashValueGet(hPtr);
}

Project: jsish

static bool jsi_LogEnabled(Jsi_Interp *interp, uint code) {
    if (!interp->activeFunc) return 0;
    Jsi_CmdSpec *cs = interp->activeFunc->cmdSpec;
    if (!cs)
        return 0;
    if (interp->activeFunc->parentSpec)
        cs = interp->activeFunc->parentSpec;
    int cofs = (code - JSI_LOG_TEST);
    int ac = (cs->flags & (JSI_CMD_LOG_TEST<<cofs));
    return (ac)!=0;
}

Project: jsish

static Jsi_RC KeyLocker(Jsi_Hash* tbl, int lock)
{
    if (!lock)
        Jsi_MutexUnlock(jsiIntData.mainInterp, jsiIntData.mainInterp->Mutex);
    else
        return Jsi_MutexLock(jsiIntData.mainInterp, jsiIntData.mainInterp->Mutex);
    return JSI_OK;
}

Project: jsish

static Jsi_RC RegexpTestCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    int skip = 0, rc = 0;
    Jsi_Value *v;
    ChkRegexp(_this, funcPtr, v);
    char *str = Jsi_ValueString(interp,Jsi_ValueArrayIndex(interp, args, skip), NULL);
    if (!str) 
        return Jsi_LogError("expected string");
    if (Jsi_RegExpMatch(interp, v, str, &rc, NULL) != JSI_OK)
        return JSI_ERROR;    
    Jsi_ValueMakeBool(interp, ret, rc != 0);
    return JSI_OK;
}

Project: jsish

static void destroy_node(Jsi_Interp *interp, Jsi_TreeEntry* n)
{
    if (n == NULL) return;
    if (n->right != NULL) destroy_node(interp, n->right);
    if (n->left != NULL) destroy_node(interp, n->left);
    n->left = n->right = NULL;
    Jsi_TreeEntryDelete(n);
}

Project: jsish

static void jsi_CArrayFree(Jsi_Interp *interp, Jsi_OptionSpec* spec, void *ptr)
{
    /*Jsi_OptionSpec *subSpec = spec->init.ini.OPT_CARRAY; // TODO: ???
    if (!subSpec) {
        Jsi_Value **v = (Jsi_Value**)ptr;
        if (v)
            Jsi_DecrRefCount(interp, *v);
    }
    int i, isize, size = spec->asize;
    if ((isize=subSpec->size)<=0)
        return;
    isize = isize/size;
    uchar *s = (uchar*)ptr;
    for (i=0; i<size; i++) {
        Jsi_OptionsFree(interp, subSpec, s, 0);
        s += isize;
    }*/
}

Project: jsish

static void jsi_ValueObjDelete(Jsi_Interp *interp, Jsi_Value *target, Jsi_Value *key, int force)
{
    if (target->vt != JSI_VT_OBJECT) return;
    const char *kstr = Jsi_ValueToString(interp, key, NULL);
    Jsi_TreeEntry *hPtr;
    if (!Jsi_ValueIsStringKey(interp, key)) {
        Jsi_MapEntry *hePtr = Jsi_MapEntryFind(target->d.obj->tree->opts.interp->strKeyTbl, kstr);
        if (hePtr)
            kstr = (char*)Jsi_MapKeyGet(hePtr, 0);
    }
    hPtr = Jsi_TreeEntryFind(target->d.obj->tree, kstr);
    if (hPtr == NULL || (hPtr->f.bits.dontdel && !force))
        return;
    Jsi_TreeEntryDelete(hPtr);
}

Project: jsish

static void dbUpdateHandler(
    void *p,
    int op,
    const char *zDb,
    const char *zTbl,
    sqlite_int64 rowid
) {
    Jsi_Db *jdb = (Jsi_Db *)p;
    Jsi_Interp *interp = jdb->interp;
    int rc, i = 0;
    Jsi_Value *vpargs, *items[10] = {}, *ret;
    
    assert( op==SQLITE_INSERT || op==SQLITE_UPDATE || op==SQLITE_DELETE );
    items[i++] = Jsi_ValueNewObj(interp, jdb->fobj);
    items[i++] = Jsi_ValueMakeStringDup(interp, NULL, (op==SQLITE_INSERT)?"INSERT":(op==SQLITE_UPDATE)?"UPDATE":"DELETE");
    items[i++] = Jsi_ValueMakeStringDup(interp, NULL, zDb);
    items[i++] = Jsi_ValueMakeStringDup(interp, NULL, zTbl);
    items[i++] = Jsi_ValueMakeNumber(interp, NULL, (Jsi_Number)rowid);
    vpargs = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, items, i, 0));
    Jsi_IncrRefCount(interp, vpargs);
    ret = Jsi_ValueNew1(interp);
    rc = Jsi_FunctionInvoke(interp, jdb->onUpdate, vpargs, &ret, NULL);
    Jsi_DecrRefCount(interp, vpargs);
    Jsi_DecrRefCount(interp, ret);
    if (rc != JSI_OK)
        jdb->errCnt++;
}

Project: jsish

static int jsi_FSUngetcProc(Jsi_Channel chan, int ch) {
    return ungetc(ch, _JSI_GETFP(chan,1));
}

Project: jsish

static Jsi_RC dbStmtFreeProc(Jsi_Interp *interp, Jsi_HashEntry *hPtr, void *value) {
    Jsi_Db *jdb = (Jsi_Db*)interp;
    Jsi_ListEntry *l = (Jsi_ListEntry*)hPtr;
    SqlPreparedStmt *prep = (SqlPreparedStmt *)Jsi_ListValueGet(l);
    prep->elPtr = NULL;
    dbPrepStmtFree(jdb, prep);
    return JSI_OK;
}

Project: jsish

Jsi_RC jsi_RegExpMatches(Jsi_Interp *interp, Jsi_Value *pattern, const char *str, int n, Jsi_Value *ret, int *ofs, bool match)
{
    Jsi_Regex *re;
    int regexec_flags = 0;
    Jsi_Value *seq = pattern;

    if (seq == NULL || seq->vt != JSI_VT_OBJECT || seq->d.obj->ot != JSI_OT_REGEXP) {
        Jsi_ValueMakeNull(interp, &ret);
        return JSI_OK;
    }
    re = seq->d.obj->d.robj;
    regex_t *reg = &re->reg;
    
    regmatch_t pos[MAX_SUBREGEX] = {};
    int num_matches = 0, r;
    int isglob = (re->eflags&JSI_REG_GLOB);
    Jsi_Obj *obj;
    
    do {
        r = regexec(reg, str, MAX_SUBREGEX, pos, regexec_flags);

        if (r >= REG_BADPAT) {
            char buf[JSI_BUFSIZ];

            regerror(r, reg, buf, sizeof(buf));
            return Jsi_LogError("error while matching pattern: %s", buf);
        }
        if (r == REG_NOMATCH) {
            if (num_matches == 0) {
                Jsi_ValueMakeNull(interp, &ret);
                return JSI_OK;
            }
            break;
        }

        if (num_matches == 0) {
            obj = Jsi_ObjNewType(interp, JSI_OT_ARRAY);
            obj->__proto__ = interp->Array_prototype;
            Jsi_ValueMakeObject(interp, &ret, obj);
            Jsi_ObjSetLength(interp, ret->d.obj, 0);
        }
    
        int i;
        for (i = 0; i < MAX_SUBREGEX; ++i) {
            if (pos[i].rm_so <= 0 && pos[i].rm_eo <= 0)
                break;
            if (i && pos[i].rm_so == pos[i-1].rm_so && pos[i].rm_eo == pos[i-1].rm_eo)
                continue;
    
            int olen = -1;
            char *ostr = jsi_SubstrDup(str, -1, pos[i].rm_so, pos[i].rm_eo - pos[i].rm_so, &olen);
            Jsi_Value *val = Jsi_ValueMakeBlob(interp, NULL, (uchar*)ostr, olen);
            if (ofs)
                *ofs = pos[i].rm_eo;
            Jsi_ValueInsertArray(interp, ret, num_matches, val, 0);
            num_matches++;
            if ( match && isglob)
                break;
        }
        if (num_matches && match && !isglob)
            return JSI_OK;
        if (num_matches == 1 && (ofs || !isglob))
            break;
        
        str += pos[0].rm_eo;
        n -= pos[0].rm_eo;

        regexec_flags |= REG_NOTBOL;
    } while (n && pos[0].rm_eo>0);
    
    return JSI_OK;
}

Project: jsish

static Jsi_OpCodes *code_push_top2() { JSI_NEW_CODES(0,OP_PUSHTOP2, 0); }

Project: jsish

static void insert_case2(Jsi_TreeEntry* n) {
    if (node_color(n->parent) == _JSI_TREE_BLACK)
        return;
    insert_case3(n);
}

Project: jsish

Jsi_Stack* Jsi_StackNew(void)
{
    Jsi_Stack *stack = (Jsi_Stack*)Jsi_Calloc(1, sizeof(Jsi_Stack));
    return stack;
}

Project: jsish

static int jsi_DbQuery(Jsi_Db *jdb, Jsi_CDataDb *dbopts, const char *query)
{
    int k, cnt, erc = -1;
    Jsi_CDataDb statbinds[] = {{}, {}};
    if (!dbopts) dbopts = statbinds;
    OptionBind ob = {.binds = dbopts};
    Jsi_StructSpec *specPtr, *specs;
    Jsi_Interp *interp = jdb->interp;
    if (!query) query="";
    if (query[0]==';') {
        if (!dbExecCmd(jdb, query+1, &erc)) {
            Jsi_LogError("EXEC ERROR=\"%s\", SQL=\"%s\"", sqlite3_errmsg(jdb->db), query);
            return erc;
        }
        return 0;
    }
    const char *cPtr = Jsi_Strstr(query, " %s");
    if (!cPtr) cPtr = Jsi_Strstr(query, "\t%s");
    if (!dbopts) {
        Jsi_LogError("dbopts may not be null");
        return -1;
    }
    if (!dbopts[0].data) {
        Jsi_LogError("data may not be null");
        return -1;
    }
    if (!dbopts[0].sf) {
        Jsi_LogError("specs may not be null");
        return -1;
    }
    for (k=0; dbopts[k].sf; k++) {
        if (dbopts[k].arrSize>1 || k==0) {
            int scnt = 0;
            for (specPtr = dbopts[k].sf, scnt=0; specPtr->id>=JSI_OPTION_BOOL
                && specPtr->id < JSI_OPTION_END; specPtr++, scnt++) {
                if (specPtr->flags&JSI_OPT_DB_IGNORE)
                    continue;
                if (k==0) {
                    if (specPtr->flags&JSI_OPT_DB_ROWID) {
                        if (specPtr->id != JSI_OPTION_INT64) {
                            Jsi_LogError("rowid flag must be a wide field: %s", specPtr->name);
                            return -1;
                        }
                        ob.rowidPtr = specPtr;
                    }
                    if (specPtr->flags&JSI_OPT_DB_DIRTY) {
                        if (specPtr->id == JSI_OPTION_BOOL || specPtr->id == JSI_OPTION_INT) {
                            ob.dirtyPtr = specPtr;
                        } else {
                            Jsi_LogError("dirty flag must be a int/bool field: %s", specPtr->name);
                            return -1;
                        }
                    }
                            
                }
            }
            if (k==0)
                ob.optLen = scnt;
            assert(specPtr->id == JSI_OPTION_END);
        }
        if (!dbopts[k].prefix) break;
    }
    specs = dbopts[0].sf;
    int structSize = specs[ob.optLen].size;
    if (dbopts->memClear || dbopts->memFree) {
        cnt = dbopts[0].arrSize;
        void *rec = dbopts[0].data, *prec = rec;
        void **recPtrPtr = NULL;
        if (dbopts->isPtr2) {
            recPtrPtr = (void**)rec; /* This is really a void***, but this gets recast below. */
            rec = *recPtrPtr;
        }
        if (cnt<=0 && rec && dbopts->isPtr2) {
            for (cnt=0; ((void**)rec)[cnt]!=NULL; cnt++);
        }
        for (k=0; k<cnt; k++) {
            if (dbopts->isPtr2 || dbopts->isPtrs)
                prec = ((void**)rec)[k];
            else
                prec = (char*)rec + (k * structSize);
            if (!prec)
                continue;
            Jsi_OptionsFree(interp, (Jsi_OptionSpec*)specs, prec, 0);
            if (dbopts->isPtr2 || dbopts->isPtrs) {
                Jsi_Free(prec);
            }
        }
        if (recPtrPtr) {
            Jsi_Free(*recPtrPtr);
            *recPtrPtr = NULL;
        }
        if (query == NULL || query[0] == 0)
            return 0;
    }
    
    if (!Jsi_Strncasecmp(query, "SELECT", 6))
        return dbOptSelect(jdb, query, &ob, dbopts);
        
    DbEvalContext sEval = {};
    int insert = 0, replace = 0, update = 0;
    char nbuf[JSI_MAX_NUMBER_STRING], *bPtr;
#ifdef JSI_DB_DSTRING_SIZE
    JSI_DSTRING_VAR(dStr, JSI_DB_DSTRING_SIZE);
#else
    Jsi_DString sStr, *dStr = &sStr;
    Jsi_DSInit(dStr);
#endif
    if (dbopts->noCache)
        sEval.nocache = 1;
    if (dbEvalInit(interp, &sEval, jdb, NULL, dStr, 0, 0) != JSI_OK)
        return -1;
    int dataMax = dbopts[0].arrSize;
    cnt = 0;
    if (dataMax==0)
        dataMax = 1;
    char ch[2];

    ch[0] = dbopts[0].prefix;
    ch[1] = 0;
    if (!ch[0])
        ch[0] = ':';
    if ((update=(Jsi_Strncasecmp(query, "UPDATE", 6)==0))) {
        Jsi_DSAppendLen(dStr, query, cPtr?(cPtr-query):-1);
        if (cPtr) {
            Jsi_DSAppend(dStr, " ", NULL);
            int cidx = 0;
            int killf = (JSI_OPT_DB_IGNORE|JSI_OPT_READ_ONLY|JSI_OPT_INIT_ONLY);
            for (specPtr = specs; specPtr->id != JSI_OPTION_END; specPtr++, cidx++) {
                if (specPtr == ob.rowidPtr || specPtr == ob.dirtyPtr || (specPtr->flags&killf))
                    continue;
                const char *fname = specPtr->name;
                if (ch[0] == '?')
                    snprintf(bPtr=nbuf, sizeof(nbuf), "%d", cidx+1);
                else
                    bPtr = (char*)specPtr->name;
                Jsi_DSAppend(dStr, (cnt?",":""), "[", fname, "]=",
                    ch, bPtr, NULL);
                cnt++;
            }
            Jsi_DSAppend(dStr, cPtr+3, NULL);
        }
    } else if ((insert=(Jsi_Strncasecmp(query, "INSERT", 6)==0))
        || (replace=(Jsi_Strncasecmp(query, "REPLACE", 7)==0))) {
        Jsi_DSAppendLen(dStr, query, cPtr?(cPtr-query):-1);
        if (cPtr) {
            Jsi_DSAppend(dStr, " (", NULL);
            int killf = JSI_OPT_DB_IGNORE;
            if (replace)
                killf |= (JSI_OPT_READ_ONLY|JSI_OPT_INIT_ONLY);
            for (specPtr = specs; specPtr->id != JSI_OPTION_END; specPtr++) {
                if (specPtr == ob.rowidPtr || specPtr == ob.dirtyPtr || specPtr->flags&killf)
                    continue;
                const char *fname = specPtr->name;
                Jsi_DSAppend(dStr, (cnt?",":""), "[", fname, "]", NULL);
                cnt++;
            }
            Jsi_DSAppendLen(dStr,") VALUES(", -1);
            cnt = 0;
            int cidx = 0;
            for (specPtr = specs; specPtr->id != JSI_OPTION_END; specPtr++, cidx++) {
                if (specPtr == ob.rowidPtr || specPtr == ob.dirtyPtr
                    || specPtr->flags&killf)
                    continue;
                if (ch[0] == '?')
                    snprintf(bPtr=nbuf, sizeof(nbuf), "%d", cidx+1);
                else
                    bPtr = (char*)specPtr->name;
                Jsi_DSAppend(dStr, (cnt?",":""), ch, bPtr, NULL);
                cnt++;
            }
            Jsi_DSAppend(dStr,")", cPtr+3, NULL);
        }
    } else if (!Jsi_Strncasecmp(query, "DELETE", 6)) {
        Jsi_DSAppend(dStr, query, NULL);
    } else {
        Jsi_LogError("unrecognized query \"%s\": expected one of: SELECT, UPDATE, INSERT, REPLACE or DELETE", query);
        return -1;
    }
    sEval.zSql = Jsi_DSValue(dStr);
    if (jdb->echo && sEval.zSql)
        Jsi_LogInfo("SQL-ECHO: %s\n", sEval.zSql); 

    int rc, bindMax = -1, dataIdx = 0;
    cnt = 0;
    int ismodify = (replace||insert||update);
    int isnew = (replace||insert);
    int didBegin = 0;
    DbEvalContext *p = &sEval;
    rc = dbPrepareStmt(p->jdb, p->zSql, &p->zSql, &p->pPreStmt);
    if( rc!=JSI_OK ) return -1;
    if (dataMax>1 && !dbopts->noBegin) {
        didBegin = 1;
        if (!dbExecCmd(jdb, JSI_DBQUERY_BEGIN_STR, &erc))
            goto bail;
    }
    while (dataIdx<dataMax) {
        if (ismodify && ob.dirtyPtr && (dbopts->dirtyOnly)) { /* Check to limit updates to dirty values only. */
            void *rec = dbopts[0].data;
            if (dbopts->isPtrs || dbopts->isPtr2)
                rec = ((void**)rec)[dataIdx];
            else
                rec = (char*)rec + (dataIdx * structSize);
            char *ptr = (char*)rec + ob.dirtyPtr->offset;
            int isDirty = *(int*)ptr;
            int bit = 0;
            if (ob.dirtyPtr->id == JSI_OPTION_BOOL)
                bit = (uintptr_t)ob.dirtyPtr->data;
            if (!(isDirty&(1<<(bit)))) {
                dataIdx++;
                continue;
            }
            isDirty &= ~(1<<(bit));
            *(int*)ptr = isDirty; /* Note that the dirty bit is cleared, even upon error.*/
        }
        rc = dbBindOptionStmt(jdb, p->pPreStmt->pStmt, &ob, dataIdx, bindMax, dbopts);
        if( rc!=JSI_OK )
            goto bail;
        bindMax = 1;
        rc = dbEvalStepSub(p, (dataIdx>=dataMax), &erc);
        if (rc == JSI_ERROR)
            goto bail;
        cnt += sqlite3_changes(jdb->db);
        if (rc != JSI_OK && rc != JSI_BREAK)
            break;
        if (ob.rowidPtr && isnew) {
            void *rec = dbopts[0].data;
            if (dbopts->isPtrs || dbopts->isPtr2)
                rec = ((void**)rec)[dataIdx];
            else
                rec = (char*)rec + (dataIdx * structSize);
            char *ptr = (char*)rec + ob.rowidPtr->offset;
            *(Jsi_Wide*)ptr = sqlite3_last_insert_rowid(jdb->db);
        }
        dataIdx++;
    }
    if (didBegin && !dbExecCmd(jdb, JSI_DBQUERY_COMMIT_STR, &erc))
        rc = JSI_ERROR;
    dbEvalFinalize(&sEval);
    if( rc==JSI_BREAK ) {
        rc = JSI_OK;
    }
    return (rc==JSI_OK?cnt:erc);

bail:
    dbEvalFinalize(&sEval);
    if (didBegin)
        dbExecCmd(jdb, JSI_DBQUERY_ROLLBACK_STR, NULL);
    return erc;
}

Project: jsish

Jsi_Value* Jsi_ValueMakeDStringObject(Jsi_Interp *interp, Jsi_Value **vPtr, Jsi_DString *dsPtr)  {
    Jsi_Value *v = (vPtr?*vPtr:NULL);
    Jsi_Obj *obj;
    if (!v)
        v = Jsi_ValueNew(interp);
    else {
        assert(v->vt <= JSI_VT__MAX);
        if (v->vt == JSI_VT_OBJECT && v->d.obj->ot == JSI_OT_STRING
            && v->d.obj->refcnt == 1
        ) {
            Jsi_ObjFromDS(dsPtr, v->d.obj);
            return v;
        }
        Jsi_ValueReset(interp, &v);
    }
    obj = Jsi_ObjNewType(interp, JSI_OT_STRING);
    Jsi_ObjFromDS(dsPtr, obj);
    Jsi_ValueMakeObject(interp, &v, obj);
    return v;
}

Project: jsish

static Jsi_OpCodes *code_instanceof() { JSI_NEW_CODES(0,OP_INSTANCEOF, 0); }

Project: jsish

static Jsi_OpCodes *code_push_regex(jsi_Pstate *p, jsi_Pline *line, Jsi_Regex *reg) { JSI_NEW_CODESLN(0,OP_PUSHREG, reg); }

Project: jsish

static Jsi_OpCodes *code_push_undef() { JSI_NEW_CODES(0,OP_PUSHUND, 0); }

Project: jsish

Jsi_Func *jsi_FuncMake(jsi_Pstate *pstate, Jsi_ScopeStrs *args, Jsi_OpCodes *ops, jsi_Pline* line, const char *name, int isArrow)
{
    Jsi_Interp *interp = pstate->interp;
    Jsi_ScopeStrs *localvar = jsi_ScopeGetVarlist(pstate);
    Jsi_Func *f = jsi_FuncNew(interp);
    jsi_Lexer *l = pstate->lexer;
    if (isArrow)
        f->isArrow = isArrow;
    f->type = FC_NORMAL;
    f->opcodes = ops;
    f->argnames = args;
    f->localnames = localvar;
    f->script = interp->curFile;
    f->bodyline = *line;
    f->retType = (Jsi_otype)args->retType;
    if (!pstate->eval_flag) {
        f->scriptFile = f->script;
    }
    if (l->ltype == LT_STRING)
        f->bodyStr = l->d.str;
    f->endPos = l->cur;
    f->startPos = -1; // Have to get these from newline count.
    if (f->retType & JSI_TT_UNDEFINED)
        Jsi_LogWarn("illegal use of 'undefined' in a return type: %s", name?name:"");
    
    //f->strict = (jsi_GetDirective(interp, ops, "use strict") != NULL);
    pstate->argType = 0;
    if (localvar && args && (interp->strict)) {
        int i, j;
        for (i=0; i<args->count; i++) {
            for (j=0; j<args->count; j++) {
                if (i != j && !Jsi_Strcmp(args->args[i].name, args->args[j].name)) {
                        if (line)
                            interp->parseLine = line;
                        Jsi_LogWarn("function %s():  duplicate parameter name '%s'", name?name:"", args->args[i].name);
                        if (line)
                            interp->parseLine = NULL;
                        jsi_TypeMismatch(interp);
                        if (interp->typeCheck.error)
                            pstate->err_count++;
                }
            }
            for (j=0; j<localvar->count; j++) {
                if (!Jsi_Strcmp(localvar->args[j].name, args->args[i].name)) {
                        if (line)
                            interp->parseLine = line;
                        Jsi_LogWarn("function %s():  parameter name conflicts with 'var %s'", name?name:"", localvar->args[j].name);
                        if (line)
                            interp->parseLine = NULL;
                        jsi_TypeMismatch(interp);
                        if (interp->typeCheck.error)
                            pstate->err_count++;
                }
            }
        }
    }
    if (name) {
        f->name = Jsi_KeyAdd(interp, name);
        if ((interp->typeCheck.run|interp->typeCheck.parse|interp->typeCheck.all|interp->typeCheck.funcsig)) {
            
            if (f->retType && !(f->retType&JSI_TT_VOID) && ops && ops->code_len && ops->codes[ops->code_len-1].op != OP_RET) {
                if (line)
                    interp->parseLine = line;
                Jsi_LogWarn("missing return at end of function '%s'", name);
                if (line)
                    interp->parseLine = NULL;
                //if (interp->typeCheck.error)
                 //   pstate->err_count++;
            }
             
            if (interp->staticFuncsTbl) {
                Jsi_Func *fo = (Jsi_Func*)Jsi_HashGet(interp->staticFuncsTbl, (void*)name, 0);
                
                // Forward declaration signature compare (indicated by an empty body).
                if (interp->typeCheck.funcsig && fo && fo->opcodes && fo->opcodes->code_len == 1 && fo->opcodes->codes->op == OP_NOP) {
                    if (!jsi_FuncSigsMatch(pstate, f, fo)) {
                        if (line)
                            interp->parseLine = line;
                        Jsi_LogWarn("possible signature mismatch for function '%s' at %.120s:%d", name, fo->script, fo->bodyline.first_line);
                        if (line)
                            interp->parseLine = NULL;
                        jsi_TypeMismatch(interp);
                    }
                    //printf("OLD: %s\n", name);
                }
                Jsi_HashSet(interp->staticFuncsTbl, name, f);
            }
        }
    }
    return f;
}

Project: jsish

Jsi_RC Jsi_InitMySql(Jsi_Interp *interp, int release)
{
    if (release) {
        if (!--mydbObjCmd.init)
            mysql_library_end();
        return Jsi_DoneMySql(interp);
    }
    Jsi_Hash* dbSys;
#if JSI_USE_STUBS
  if (Jsi_StubsInit(interp, 0) != JSI_OK)
    return JSI_ERROR;
#endif
#ifndef JSI_OMIT_THREADS
    if (mydbObjCmd.init == 0 && mysql_library_init(0, NULL, NULL))
        return Jsi_LogError("failed to initialize MySQL library\n");
#else
    return Jsi_LogError("Threads required for mysql");
#endif

    Jsi_Value *info = Jsi_ValueNew1(interp);
    Jsi_JSONParseFmt(interp, &info, "{pkgVer:%d}", MYSQL_VERSION_ID);
    Jsi_PkgOpts dbPkgOpts = { mydb_ObjCmd_Specs, &mydbObjCmd, mysqlCmds, info};
    Jsi_RC rc = Jsi_PkgProvideEx(interp, "MySql", 1.1, Jsi_InitMySql, &dbPkgOpts);
    Jsi_DecrRefCount(interp, info);
    if (rc != JSI_OK)
        rc = JSI_ERROR;
    else if (!(dbSys = Jsi_UserObjRegister(interp, &mysqlobject))) 
        rc = Jsi_LogError("Failed to init mysql extension");
    else if (!Jsi_CommandCreateSpecs(interp, mysqlobject.name, mysqlCmds, dbSys, JSI_CMDSPEC_ISOBJ))
        rc = JSI_ERROR;
    if (rc == JSI_OK)
        mydbObjCmd.init++;
    else
        mysql_library_end();
    return rc;
}

Project: jsish

static Jsi_RC StringCharAtCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    int pos = 0, len, bLen;
    const char *vstr;
    ChkString(_this, funcPtr, vstr, &len, &bLen);
   
    Jsi_Value *vpos = Jsi_ValueArrayIndex(interp, args, 0);
    if (Jsi_GetIntFromValue(interp, vpos, &pos)) {
        return JSI_ERROR;        
    }
    if (pos<0 || pos >=len)
        Jsi_ValueMakeStringDup(interp, ret, "");
    else {
        Jsi_DString dStr;
        Jsi_DSInit(&dStr);
        Jsi_UtfSubstr(vstr, pos, 1, &dStr);
        Jsi_ValueFromDS(interp, &dStr, ret);
    }
    return JSI_OK;
}

Project: jsish

const char *jsi_GetCurFile(Jsi_Interp *interp)
{
    const char *curFile = NULL;
    if (!interp)
        return NULL;
    if (interp->inParse)
        curFile = interp->curFile;
    else
        curFile = (interp->curIp && interp->curIp->fname? interp->curIp->fname:interp->curFile);
    if (!curFile) curFile = interp->framePtr->fileName;
    if (!curFile) curFile = "";
    return curFile;
}

Project: jsish

static Jsi_RC NumberToExponentialCmd(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);
    if (Jsi_GetIntFromValue(interp, Jsi_ValueArrayIndex(interp, args, skip), &prec) != JSI_OK)
        return JSI_ERROR;
    if (prec<0) prec = 0;
    Jsi_GetDoubleFromValue(interp, v, &num);
    snprintf(buf, sizeof(buf), "%.*" JSI_NUMEFMT, prec, num);
#ifdef __WIN32
    char *e = strrchr(buf, 'e');
    if (e && (e[1]=='+' || e[1]=='-')) {
        e++;
        int eNum = atoi(e);
        if (e[0]=='-')
            eNum = -eNum;
        e++;
        snprintf(e, (e-buf), "%02d", eNum);
    }
#endif
    Jsi_ValueMakeStringDup(interp, ret, buf);
    return JSI_OK;
}

Project: jsish

static Jsi_RC SqliteQueryCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                             Jsi_Value **ret, Jsi_Func *funcPtr)
{
    Jsi_RC rc = JSI_OK;
    Jsi_Db *jdb;
    if (!(jdb = dbGetDbHandle(interp, _this, funcPtr))) return JSI_ERROR;
    Jsi_Value *vSql = Jsi_ValueArrayIndex(interp, args, 0);
    Jsi_DString eStr = {};
#ifdef JSI_DB_DSTRING_SIZE
    JSI_DSTRING_VAR(dStr, JSI_DB_DSTRING_SIZE);
#else
    Jsi_DString ddStr, *dStr = &ddStr;
    Jsi_DSInit(dStr);
#endif
    const char *zSql = Jsi_ValueGetDString(interp, vSql, &eStr, 0);
    Jsi_Value *arg = Jsi_ValueArrayIndex(interp, args, 1);
    int cnt = 0;
    char **apColName = NULL;
    int *apColType = NULL, isopts = 0;
    DbEvalContext sEval = {};
    QueryOpts opts, *oEopt = jdb->optPtr;
    opts = jdb->queryOpts;
    opts.callback = NULL;
    opts.width = NULL;
    Jsi_Value *callback = NULL, *width = NULL;
            
    if (arg) {
        if (Jsi_ValueIsFunction(interp,arg))
            callback = opts.callback = arg;
        else if (Jsi_ValueIsString(interp, arg))
            opts.objName = Jsi_ValueString(interp, arg, NULL);
        else if (Jsi_ValueIsObjType(interp, arg, JSI_OT_ARRAY))
            opts.values = arg;
        else if (Jsi_ValueIsObjType(interp, arg, JSI_OT_OBJECT))
            isopts = 1;
        else {
            rc = Jsi_LogError("arg 2: expected function, string, array or options");
            goto bail;
        }
    }

    if (isopts) {
        if (Jsi_OptionsProcess(interp, ExecFmtOptions, &opts, arg, 0) < 0) {
            rc = JSI_ERROR;
            goto bail;
        }
        callback = (opts.callback ? opts.callback : jdb->queryOpts.callback);
        width = (opts.width ? opts.width : jdb->queryOpts.width);
    }
    if (opts.retChanged) {
        if (opts.callback) {
            rc = Jsi_LogError("can not use retChanged with callback");
            goto bail;
        }
        opts.mode = _JSI_EF_NONE;
        opts.headers = 0;
    }
    if (opts.cdata) {
        Jsi_CDataDb* copts = Jsi_CDataLookup(interp, opts.cdata);
        if (!copts)
            rc = Jsi_LogError("unknown cdata name: %s", opts.cdata);
        else {
            int n = Jsi_DbQuery(jdb, copts, zSql);
            Jsi_ValueMakeNumber(interp, ret, (Jsi_Number)n);
        }
        goto bail;
    }
    if (opts.objName) {
        if (Jsi_SqlObjBinds(interp, &eStr, opts.objName, !(opts.objOpts&OBJMODE_NOTYPES), 
            !(opts.objOpts&OBJMODE_NODEFAULTS), (opts.objOpts&OBJMODE_NULLDEFAULTS)!=0) != JSI_OK)
            goto bail;
        zSql = Jsi_DSValue(&eStr);
    }
    if ((jdb->echo || opts.echo) && zSql)
        Jsi_LogInfo("SQL-ECHO: %s\n", zSql); 
    if ((opts.objOpts&OBJMODE_SQLONLY)) {
        if (opts.objName)
            Jsi_ValueMakeStringDup(interp, ret, zSql);
        else
            rc = Jsi_LogError("'objOpts.sqlOnly' can only be used with 'objName'");
        goto bail;
    }
    if (!opts.separator) {
        switch (opts.mode) {
            case _JSI_EF_LIST: opts.separator = "|"; break;
            case _JSI_EF_COLUMN: opts.separator = " "; break;
            case _JSI_EF_TABS: opts.separator = "\t"; break;
            default: opts.separator = ",";
        }
    }
    Jsi_DString sStr;
    Jsi_DSInit(&sStr);
    sEval.nocache = opts.nocache;
    if ((rc = dbEvalInit(interp, &sEval, jdb, zSql, &sStr, 0, 0)) != JSI_OK)
        goto bail;
    sEval.ret = *ret;
    jdb->optPtr = &opts;
    if (callback) {
        sEval.tocall = callback;
        if (opts.mode != _JSI_EF_ROWS) {
            Jsi_LogError("'mode' must be 'rows' with 'callback'");
            rc = JSI_ERROR;
            goto bail;
        }
        rc = dbEvalCallCmd(&sEval, interp, JSI_OK);
        goto bail;
    } else
    switch (opts.mode) {
    case _JSI_EF_NONE:
        while(JSI_OK==(rc = dbEvalStep(&sEval)) ) {
            cnt++;
            if (opts.limit && cnt>=opts.limit) break;
        }
        if (opts.retChanged)
            Jsi_ValueMakeNumber(interp, ret, (Jsi_Number)sqlite3_changes(jdb->db));
        if (rc == JSI_BREAK)
            rc = JSI_OK;
        goto bail;
        break;
    case _JSI_EF_JSON:
        if (opts.headers) {
            Jsi_DSAppend(dStr, "[ ", NULL);
            while( JSI_OK==(rc = dbEvalStep(&sEval)) ) {
                int i;
                int nCol;
                dbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
                if (cnt == 0) {
                    Jsi_DSAppend(dStr, "[", NULL);
                    for(i=0; i<nCol; i++) {
                        if (i)
                            Jsi_DSAppend(dStr, ", ", NULL);
                        Jsi_JSONQuote(interp, apColName[i], -1, dStr);
                    }
                    Jsi_DSAppend(dStr, "]", NULL);
                    cnt++;
                }
                if (cnt)
                    Jsi_DSAppend(dStr, ", ", NULL);
                Jsi_DSAppend(dStr, "[", NULL);
                for(i=0; i<nCol; i++) {
                    if (i)
                        Jsi_DSAppend(dStr, ", ", NULL);
                    dbEvalSetColumnJSON(&sEval, i, dStr);
                }
                Jsi_DSAppend(dStr, "]", NULL);
                cnt++;
                if (opts.limit && cnt>opts.limit) break;
            }
            Jsi_DSAppend(dStr, " ]", NULL);
            
        } else {
            Jsi_DSAppend(dStr, "[ ", NULL);
            while( JSI_OK==(rc = dbEvalStep(&sEval)) ) {
                int i;
                int nCol;
                dbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
                if (cnt)
                    Jsi_DSAppend(dStr, ", ", NULL);
                Jsi_DSAppend(dStr, "{", NULL);
                for(i=0; i<nCol; i++) {
                    if (i)
                        Jsi_DSAppend(dStr, ", ", NULL);
                    Jsi_JSONQuote(interp, apColName[i], -1, dStr);
                    Jsi_DSAppend(dStr, ":", NULL);
                    dbEvalSetColumnJSON(&sEval, i, dStr);
                }
                Jsi_DSAppend(dStr, "}", NULL);
                cnt++;
                if (opts.limit && cnt>=opts.limit) break;
            }
            Jsi_DSAppend(dStr, " ]", NULL);
        }
        break;
        
    case _JSI_EF_JSON2: {
            while( JSI_OK==(rc = dbEvalStep(&sEval)) ) {
                int i;
                int nCol;
                dbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
                if (cnt == 0 && 1) {
                    Jsi_DSAppend(dStr, "{ \"names\": [ ", NULL);
                    for(i=0; i<nCol; i++) {
                        if (i)
                            Jsi_DSAppend(dStr, ", ", NULL);
                        Jsi_JSONQuote(interp, apColName[i], -1, dStr);
                    }
                    Jsi_DSAppend(dStr, " ], \"values\": [ ", NULL);
                }
                if (cnt)
                    Jsi_DSAppend(dStr, ", ", NULL);
                Jsi_DSAppend(dStr, "[", NULL);
                for(i=0; i<nCol; i++) {
                    if (i)
                        Jsi_DSAppend(dStr, ", ", NULL);
                    dbEvalSetColumnJSON(&sEval, i, dStr);
                }
                Jsi_DSAppend(dStr, " ]", NULL);
                cnt++;
                if (opts.limit && cnt>=opts.limit) break;
            }
            if (cnt)
                Jsi_DSAppend(dStr, " ] } ", NULL);
        }
        break;
        
    case _JSI_EF_LIST:
        while( JSI_OK==(rc = dbEvalStep(&sEval)) ) {
            int i;
            int nCol;
            dbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
            if (cnt == 0 && opts.headers) {
                for(i=0; i<nCol; i++) {
                    if (i)
                        Jsi_DSAppend(dStr, opts.separator, NULL);
                    Jsi_DSAppend(dStr, apColName[i], NULL);
                }
            }

            if (cnt || opts.headers)
                Jsi_DSAppend(dStr, "\n", NULL);
            for(i=0; i<nCol; i++) {
                if (i)
                    Jsi_DSAppend(dStr, opts.separator, NULL);
                dbEvalSetColumn(&sEval, i, dStr);
            }
            cnt++;
            if (opts.limit && cnt>=opts.limit) break;
        }
        break;
        
    case _JSI_EF_COLUMN: {
        int *wids = NULL;
        Jsi_DString vStr = {};
        while( JSI_OK==(rc = dbEvalStep(&sEval)) ) {
            int i, w;
            int nCol;
            
            dbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
            if (cnt == 0 && nCol>0) {
                Jsi_DString sStr;
                wids = (int*)Jsi_Calloc(nCol, sizeof(int));
                Jsi_DSInit(&sStr);
                for(i=0; i<nCol; i++) {
                    int j = Jsi_Strlen(apColName[i]);
                    wids[i] = (j<10?10:j);
                    if (width) {
                        Jsi_Value *wv = Jsi_ValueArrayIndex(interp, width, i);
                        if (wv) {
                            Jsi_Number dv;
                            Jsi_ValueGetNumber(interp, wv, &dv);
                            if (dv>0)
                                wids[i] = (int)dv;
                        }
                    }
                    w = (j<wids[i] ? j : wids[i]);
                    Jsi_DSAppendLen(dStr, apColName[i], w);
                    w = (j<wids[i] ? wids[i]-j+1 : 0);
                    while (w-- > 0)
                        Jsi_DSAppend(dStr, " ", NULL);
                }
                for(i=0; i<nCol && opts.headers; i++) {
                    w = wids[i];
                    w -= Jsi_Strlen(apColName[i]);
                    if (i) {
                        Jsi_DSAppend(dStr, opts.separator, NULL);
                        Jsi_DSAppend(&sStr, opts.separator, NULL);
                    }
                    w = wids[i];
                    while (w-- > 0)
                        Jsi_DSAppend(&sStr, "-", NULL);
                }
                if (opts.headers)
                    Jsi_DSAppend(dStr, "\n", Jsi_DSValue(&sStr), "\n", NULL);
                Jsi_DSFree(&sStr);
            }

            if (cnt)
                Jsi_DSAppend(dStr, "\n", NULL);
            for(i=0; i<nCol; i++) {
                if (i)
                    Jsi_DSAppend(dStr, opts.separator, NULL);
                Jsi_DSSetLength(&vStr, 0);
                dbEvalSetColumn(&sEval, i, &vStr);
                int nl = Jsi_DSLength(&vStr);
                if (nl > wids[i]) {
                    Jsi_DSSetLength(&vStr, wids[i]);
                    w = 0;
                } else {
                    w = wids[i]-nl;
                }
                Jsi_DSAppend(dStr, Jsi_DSValue(&vStr), NULL);
                while (w-- > 0)
                    Jsi_DSAppend(dStr, " ", NULL);
            }
            cnt++;
            if (opts.limit && cnt>=opts.limit) break;
        }
        Jsi_DSFree(&vStr);
        if (wids)
            Jsi_Free(wids);
        break;
    }
    
    case _JSI_EF_INSERT: {
        Jsi_DString vStr = {};    
        while( JSI_OK==(rc = dbEvalStep(&sEval)) ) {
            int i;
            int nCol;
            const char *tbl = (opts.table ? opts.table : "table");
            Jsi_DSAppend(dStr, "INSERT INTO \"", tbl, "\" VALUES(", NULL);
            dbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
            for(i=0; i<nCol; i++) {
                Jsi_Number dv;
                const char *azArg;
                Jsi_DSSetLength(&vStr, 0);
                dbEvalSetColumn(&sEval, i, &vStr);
                sqlite3_stmt *pStmt = sEval.pPreStmt->pStmt;
                int ptype = sqlite3_column_type(pStmt, i);
                
                azArg = Jsi_DSValue(&vStr);
                const char *zSep = i>0 ? ",": "";
                if(apColType && apColType[i]==SQLITE_NULL) {
                  Jsi_DSAppend(dStr, zSep, "NULL", NULL);
                }else if( ptype ==SQLITE_TEXT ) {
                  if( zSep[0] ) Jsi_DSAppend(dStr,zSep, NULL);
                  dbOutputQuotedString(dStr, azArg);
                }else if (ptype==SQLITE_INTEGER || ptype ==SQLITE_FLOAT) {
                  Jsi_DSAppend(dStr, zSep, azArg, NULL);
                }else if (ptype ==SQLITE_BLOB) {
                  const void *pBlob = sqlite3_column_blob(pStmt, i );
                  int nBlob = sqlite3_column_bytes(pStmt, i);
                  if( zSep[0] ) Jsi_DSAppend(dStr,zSep, NULL);
                  dbOutputHexBlob(dStr, pBlob, nBlob);
                }else if( Jsi_GetDouble(interp, azArg, &dv) == JSI_OK ){
                  Jsi_DSAppend(dStr, zSep, azArg, NULL);
                }else{
                  if( zSep[0] ) Jsi_DSAppend(dStr,zSep, NULL);
                  dbOutputQuotedString(dStr, azArg);
                }
            }
            Jsi_DSAppend(dStr, ");\n", NULL);
            cnt++;
            if (opts.limit && cnt>=opts.limit) break;
        }
        Jsi_DSFree(&vStr);
    }

    case _JSI_EF_TABS:
    case _JSI_EF_CSV: {
        Jsi_DString vStr = {};  
        while( JSI_OK==(rc = dbEvalStep(&sEval)) ) {
            int i;
            int nCol;
            dbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
            if (cnt == 0 && opts.headers) {
                for(i=0; i<nCol; i++) {
                    if (i)
                        Jsi_DSAppend(dStr, opts.separator, NULL);
                    Jsi_DSAppend(dStr, apColName[i], NULL);
                }
            }

            if (cnt || opts.headers)
                Jsi_DSAppend(dStr, "\n", NULL);
            for(i=0; i<nCol; i++) {
                if (i)
                    Jsi_DSAppend(dStr, opts.separator, NULL);
                Jsi_DSSetLength(&vStr, 0);
                dbEvalSetColumn(&sEval, i, &vStr);
                if (opts.mode == _JSI_EF_CSV)
                    dbOutputCsv(&opts, Jsi_DSValue(&vStr), dStr, 0);
                else
                    Jsi_DSAppend(dStr, Jsi_DSValue(&vStr), NULL);
            }
            cnt++;
            if (opts.limit && cnt>=opts.limit) break;
        }
        Jsi_DSFree(&vStr);
        break;
    }
        
    case _JSI_EF_LINE: {
        int i, w = 5, ww;
        int nCol;
        Jsi_DString vStr = {};   
        while( JSI_OK==(rc = dbEvalStep(&sEval)) ) {
            dbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
            if (cnt == 0) {
                for(i=0; i<nCol; i++) {
                    ww = Jsi_Strlen(apColName[i]);
                    if (ww>w)
                        w = ww;
                }
            }

            for(i=0; i<nCol; i++) {
                Jsi_DString eStr;
                Jsi_DSInit(&eStr);
                Jsi_DSSetLength(&vStr, 0);
                dbEvalSetColumn(&sEval, i, &vStr);
                Jsi_DSPrintf(&eStr, "%*s = %s", w, apColName[i], Jsi_DSValue(&vStr));
                Jsi_DSAppend(dStr, (cnt?"\n":""), Jsi_DSValue(&eStr), NULL);
                Jsi_DSFree(&eStr);
            }
            cnt++;
            if (opts.limit && cnt>=opts.limit) break;
        }
        Jsi_DSFree(&vStr);
        break;
    }
        
    case _JSI_EF_HTML: {
        Jsi_DString vStr = {};   
        while( JSI_OK==(rc = dbEvalStep(&sEval)) ) {
            int i;
            int nCol;
            dbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
            if (cnt == 0 && opts.headers) {
                Jsi_DSAppend(dStr, "<TR>", NULL);
                for(i=0; i<nCol; i++) {
                    Jsi_DSAppend(dStr, "<TH>", NULL);
                    dbOutputHtmlString(&opts, apColName[i], dStr);
                    Jsi_DSAppend(dStr, "</TH>", NULL);
                }
                Jsi_DSAppend(dStr, "</TR>", NULL);
            }
            if (cnt || opts.headers)
                Jsi_DSAppend(dStr, "\n", NULL);
            Jsi_DSAppend(dStr, "<TR>", NULL);
            for(i=0; i<nCol; i++) {
                Jsi_DSAppend(dStr, "<TD>", NULL);
                Jsi_DSSetLength(&vStr, 0);
                dbEvalSetColumn(&sEval, i, &vStr);
                dbOutputHtmlString(&opts, Jsi_DSValue(&vStr), dStr);
                Jsi_DSAppend(dStr, "</TD>", NULL);
            }
            Jsi_DSAppend(dStr, "</TR>", NULL);
            cnt++;
            if (opts.limit && cnt>=opts.limit) break;
        }
        Jsi_DSFree(&vStr);
        break;
    }
        
    case _JSI_EF_ROWS:
    {
        Jsi_Value *vcur, *vrow;
        int cnt = 0;
        Jsi_Obj *oall, *ocur;
        Jsi_ValueMakeArrayObject(interp, ret, oall = Jsi_ObjNewType(interp, JSI_OT_ARRAY));

        while( JSI_OK==(rc = dbEvalStep(&sEval)) ) {
            int i;
            int nCol;
            dbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
            ocur = Jsi_ObjNewType(interp, JSI_OT_OBJECT);
            vrow = Jsi_ValueMakeObject(interp, NULL, ocur);
            for(i=0; i<nCol; i++) {
                vcur = dbEvalSetColumnValue(&sEval, i, NULL);
                Jsi_ObjInsert(interp, ocur, apColName[i], vcur, 0);
            }
            Jsi_ObjArrayAdd(interp, oall, vrow);
            cnt++;
            if (opts.limit && cnt>=opts.limit) break;
        }
        dbEvalFinalize(&sEval);
        if (rc != JSI_ERROR)
            rc = JSI_OK;
        goto bail;
        break;
    }
    case _JSI_EF_ARRAYS:
    {
        Jsi_Value *vcur, *vrow;
        int cnt = 0;
        Jsi_Obj *oall, *ocur;
        Jsi_ValueMakeArrayObject(interp, ret, oall = Jsi_ObjNewType(interp, JSI_OT_ARRAY));

        while( JSI_OK==(rc = dbEvalStep(&sEval)) ) {
            int i;
            int nCol;
            dbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
            if (cnt == 0 && opts.headers) {
                vrow = Jsi_ValueMakeArrayObject(interp, NULL, ocur = Jsi_ObjNewType(interp, JSI_OT_ARRAY));
                for(i=0; i<nCol; i++) {
                    vcur = Jsi_ValueNewStringDup(interp, apColName[i]);
                    Jsi_ObjArrayAdd(interp, ocur, vcur);
                }
                Jsi_ObjArrayAdd(interp, oall, vrow);
            }
            vrow = Jsi_ValueMakeArrayObject(interp, NULL, ocur = Jsi_ObjNewType(interp, JSI_OT_ARRAY));
            for(i=0; i<nCol; i++) {
                vcur = dbEvalSetColumnValue(&sEval, i, NULL);
                Jsi_ObjArrayAdd(interp, ocur, vcur);
            }
            Jsi_ObjArrayAdd(interp, oall, vrow);
            cnt++;
            if (opts.limit && cnt>=opts.limit) break;
        }
        dbEvalFinalize(&sEval);
        if (rc != JSI_ERROR)
            rc = JSI_OK;
        goto bail;
        break;
    }
    case _JSI_EF_ARRAY1D:
    {
        Jsi_Value *vcur;
        int cnt = 0;
        Jsi_Obj *oall;
        Jsi_ValueMakeArrayObject(interp, ret, oall = Jsi_ObjNewType(interp, JSI_OT_ARRAY));

        while( JSI_OK==(rc = dbEvalStep(&sEval)) ) {
            int i;
            int nCol;
            dbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
            if (cnt == 0 && opts.headers) {
                for(i=0; i<nCol; i++) {
                    vcur = Jsi_ValueNewStringDup(interp, apColName[i]);
                    Jsi_ObjArrayAdd(interp, oall, vcur);
                }
            }
            for(i=0; i<nCol; i++) {
                vcur = dbEvalSetColumnValue(&sEval, i, NULL);
                Jsi_ObjArrayAdd(interp, oall, vcur);
            }
            cnt++;
            if (opts.limit && cnt>=opts.limit) break;
        }
        dbEvalFinalize(&sEval);
        if (rc != JSI_ERROR)
            rc = JSI_OK;
        goto bail;
        break;
    }
    }
    if (opts.retChanged)
        Jsi_ValueMakeNumber(interp, ret, (Jsi_Number)sqlite3_changes(jdb->db));
    else
        Jsi_ValueFromDS(interp, dStr, ret);
    if( rc==JSI_BREAK )
        rc = JSI_OK;
bail:
    dbEvalFinalize(&sEval);
    if (isopts) {
        Jsi_OptionsFree(interp, ExecFmtOptions, &opts, 0);
    }
    Jsi_DSFree(dStr);
    Jsi_DSFree(&eStr);
    jdb->optPtr = oEopt;
    return rc;
}

Project: jsish

static Jsi_RC WebSocketIdCmdOp(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr, int op)
{
    Jsi_RC rc = JSI_OK;
    jsi_wsCmdObj *cmdPtr = (jsi_wsCmdObj*)Jsi_UserObjGetData(interp, _this, funcPtr);
    if (!cmdPtr)
        return Jsi_LogError("Apply in a non-websock object");
    Jsi_Value *v, *valPtr = Jsi_ValueArrayIndex(interp, args, 0);
    Jsi_Number vid;
    if (Jsi_ValueGetNumber(interp, valPtr, &vid) != JSI_OK || vid < 0)
        return Jsi_LogError("Expected connection number id");
    int id = (int)vid;
    jsi_wsPss *pss = NULL;
    Jsi_HashEntry *hPtr;
    Jsi_HashSearch cursor;
    for (hPtr = Jsi_HashSearchFirst(cmdPtr->pssTable, &cursor);
        hPtr != NULL; hPtr = Jsi_HashSearchNext(&cursor)) {
        jsi_wsPss* tpss = (jsi_wsPss*)Jsi_HashValueGet(hPtr);
        WSSIGASSERT(tpss, PWS);
        if (tpss->wid == id && tpss->state != PWS_DEAD) {
            pss = tpss;
            break;
        }
    }

    if (!pss)
        return Jsi_LogError("No such id: %d", id);
    switch (op) {
        case 0:
            v = Jsi_ValueArrayIndex(interp, args, 1);
            rc = Jsi_OptionsConf(interp, WPSOptions, pss, v, ret, 0);
            break;
        case 1:
            jsi_wsDumpHeaders(cmdPtr, pss, Jsi_ValueArrayIndexToStr(interp, args, 1, NULL), ret);
            break;
        case 2:
            if (!pss->spa) return JSI_OK;
            jsi_wsDumpQuery(cmdPtr, pss, Jsi_ValueArrayIndexToStr(interp, args, 1, NULL), ret);
            break;
    }
    return rc;
}

Project: jsish

Jsi_RC Jsi_DeleteData(Jsi_Interp* interp, void *m)
{
    Jsi_Free(m);
    return JSI_OK;
}

Project: jsish

Jsi_Value* Jsi_ValueNewStringConst(Jsi_Interp *interp, const char *s, int len) {
    Jsi_Value *v = Jsi_ValueNew(interp);
    v->vt = JSI_VT_STRING;
    v->d.s.str = (char*)s;
    v->d.s.len = (len<0?Jsi_Strlen(s):(uint)len);
    v->f.bits.isstrkey = 1;
    return v;
}

Project: jsish

static Jsi_RC InterpAliasCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    InterpObj *udf = (InterpObj *)Jsi_UserObjGetData(interp, _this, funcPtr);
    Jsi_Interp *sinterp = (udf ? udf->subinterp : interp );
    Jsi_Hash *aliases = sinterp->aliasHash;
    if (!aliases)
        return Jsi_LogError("Sub-interp gone");
    int argc = Jsi_ValueGetLength(interp, args);
    if (argc == 0)
        return Jsi_HashKeysDump(interp, aliases, ret, 0);
    Jsi_HashEntry *hPtr;
    char *key = Jsi_ValueArrayIndexToStr(interp, args, 0, NULL);
    if (!key)
        return Jsi_LogError("expected string");
    AliasCmd* ac;
    if (argc == 1) {
        hPtr = Jsi_HashEntryFind(aliases, (void*)key);
        if (!hPtr)
            return JSI_OK;
        ac = (AliasCmd*)Jsi_HashValueGet(hPtr);
        if (!ac) return JSI_ERROR;
        SIGASSERT(ac,ALIASCMD);
        Jsi_ValueDup2(interp, ret, ac->func);
        return JSI_OK;
    }
    Jsi_Value *afunc = Jsi_ValueArrayIndex(interp, args, 1);
    if (argc == 2) {
        hPtr = Jsi_HashEntryFind(aliases, (void*)key);
        if (!hPtr)
            return JSI_OK;
        ac = (AliasCmd*)Jsi_HashValueGet(hPtr);
        if (!Jsi_ValueIsNull(interp, afunc))
            return Jsi_LogError("arg 2: expected null to query args");
        if (!ac) return JSI_ERROR;
        SIGASSERT(ac,ALIASCMD);
        Jsi_ValueDup2(interp, ret, ac->args); //TODO: JSON??
        return JSI_OK;
    }
    
    if (argc < 3)
        return JSI_ERROR;
    bool isthrd = (interp->threadId != sinterp->threadId);
    //if (isthrd)
        //return Jsi_LogError("alias not supported with threads");
    bool isNew;
    Jsi_Value *aargs = Jsi_ValueArrayIndex(interp, args, 2);
    if (Jsi_ValueIsNull(interp, afunc) && Jsi_ValueIsNull(interp, aargs)) {
        hPtr = Jsi_HashEntryFind(aliases, (void*)key);
        if (hPtr == NULL)
            return JSI_OK;
        ac = (AliasCmd*)Jsi_HashValueGet(hPtr);
        if (!ac) return JSI_ERROR;
        if (0 && ac->cmdVal)
            Jsi_DecrRefCount(interp, ac->cmdVal);
        jsi_AliasFree(interp, NULL, ac);
        Jsi_HashValueSet(hPtr, NULL);
        Jsi_HashEntryDelete(hPtr);
        return JSI_OK;
    }
    hPtr = Jsi_HashEntryNew(aliases, (void*)key, &isNew);
    if (!hPtr)
        return Jsi_LogError("create failed: %s", key);
    if (!Jsi_ValueIsFunction(interp, afunc))
        return Jsi_LogError("arg 2: expected function");
    if (Jsi_ValueIsNull(interp, aargs) == 0 && Jsi_ValueIsArray(interp, aargs) == 0)
        return Jsi_LogError("arg 3: expected array or null");
    if (!isNew) {
        jsi_AliasFree(interp, NULL, Jsi_HashValueGet(hPtr));
    }
    ac = (AliasCmd*)Jsi_Calloc(1, sizeof(AliasCmd));
    SIGINIT(ac, ALIASCMD);
    ac->cmdName = (const char*)Jsi_HashKeyGet(hPtr);
    ac->func = afunc;
    Jsi_IncrRefCount(interp, afunc);
    if (!Jsi_ValueIsNull(interp, aargs)) {
        ac->args = aargs;
        Jsi_IncrRefCount(interp, aargs);
    }
    ac->intobj = udf;
    ac->dinterp = interp;
    ac->subinterp = sinterp;
    Jsi_HashValueSet(hPtr, ac);
    if (!isthrd)
        return jsi_AliasCreateCmd(sinterp, key, ac);

    Jsi_Value *vasync = Jsi_ValueArrayIndex(interp, args, 3);
    bool async = 0;
    if (vasync && Jsi_GetBoolFromValue(interp, vasync, &async))
        return JSI_ERROR;
        
    if (!async) {
        if (Jsi_MutexLock(interp, sinterp->Mutex) != JSI_OK)
            return JSI_ERROR;
        Jsi_RC rc = jsi_AliasCreateCmd(sinterp, key, ac);
        Jsi_MutexUnlock(interp, sinterp->Mutex);
        return rc;
    }

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

    /* Is an async call. */
    InterpStrEvent *se, *s = (InterpStrEvent *)Jsi_Calloc(1, sizeof(*s));
    // TODO: is s->data inited?
    s->acdata = ac;
    Jsi_DSInit(&s->func);
    Jsi_DSAppend(&s->func, ac->cmdName, NULL);
    se = sinterp->interpStrEvents;
    if (!se)
        sinterp->interpStrEvents = s;
    else {
        while (se->next)
            se = se->next;
        se->next = s;
    }

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

Project: jsish

Jsi_Value* Jsi_ValueNewStringDup(Jsi_Interp *interp, const char *s) {
    return Jsi_ValueNewString(interp, Jsi_Strdup(s), -1);
}

Project: jsish

const char *jsi_ObjectTypeName(Jsi_Interp *interp, Jsi_otype otyp)
{
    switch (otyp) {
        case JSI_OT_NUMBER:     return "number"; 
        case JSI_OT_STRING:     return "string"; 
        case JSI_OT_BOOL:       return "boolean"; 
        case JSI_OT_ARRAY:      return "array"; 
        case JSI_OT_FUNCTION:   return "function"; 
        case JSI_OT_OBJECT:     return "object"; 
        case JSI_OT_REGEXP:     return "regexp"; 
        case JSI_OT_ITER:       return "iter"; 
        case JSI_OT_USEROBJ:    return "userobj"; 
        case JSI_OT_UNDEF:      return "any";
    }
    return "undefined";
}

Project: jsish

static Jsi_RC freeAssocTbl(Jsi_Interp *interp, Jsi_HashEntry *hPtr, void *ptr) {
    if (!ptr) return JSI_OK;
    jsi_DelAssocData(interp, ptr);
    return JSI_OK;
}

Project: jsish

static Jsi_RC MySqlExistsCmd(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);
    const char *zSql;
    Jsi_DString dStr = {};
    MyDbEvalContext sEval = {};
    zSql = Jsi_ValueGetDString(interp, vSql, &dStr, 0);

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


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

Project: jsish

int Jsi_ValueCmp(Jsi_Interp *interp, Jsi_Value *v1, Jsi_Value* v2, int flags)
{
    DECL_VALINIT(res1);
    DECL_VALINIT(res2);
    int r = 1;
    int nocase = (flags&JSI_SORT_NOCASE), dict = ((flags & JSI_SORT_DICT));
    if (v1 == v2)
        return 1;
    if (v1->vt != v2->vt) {
        jsi_ValueToPrimitiveRes(interp, v1, &res1);
        jsi_ValueToPrimitiveRes(interp, v2, &res2);
        v1 = &res1;
        v2 = &res2;
    }
    if (v1->vt != v2->vt) {
        if ((flags&JSI_CMP_EXACT))
            return 1;
        if ((v1->vt == JSI_VT_UNDEF || v1->vt == JSI_VT_NULL) && 
            (v2->vt == JSI_VT_UNDEF || v2->vt == JSI_VT_NULL)) {
            r = 0;
        } else {
            Jsi_Number n1, n2;
            n1 = Jsi_ValueToNumberInt(interp, v1, 0);
            n2 = Jsi_ValueToNumberInt(interp, v2, 0);
            r = (n2 - n1);
        }
    } else {
        switch (v1->vt) {
            case JSI_VT_NUMBER:
                if (v2->d.num == v1->d.num) return 0;
                r = (v2->d.num < v1->d.num ? -1 : 1);
                break;
            case JSI_VT_BOOL:
                r = (v2->d.val - v1->d.val);
                break;
            case JSI_VT_STRING:
                r = (Jsi_StrcmpDict(v2->d.s.str, v1->d.s.str, nocase, dict));
                break;
            case JSI_VT_OBJECT:
                /* TODO: refer to objects joined to each other */
                if (v2->vt != JSI_VT_OBJECT)
                    r = 1;
                else if (v1->d.obj->ot == JSI_OT_STRING && v2->d.obj->ot == JSI_OT_STRING)
                    r = (Jsi_StrcmpDict(v2->d.obj->d.s.str, v1->d.obj->d.s.str, nocase, dict));
                else
                    r = (v2->d.obj - v1->d.obj);
                break;
            case JSI_VT_UNDEF:
            case JSI_VT_NULL:
                r = 0;
                break;
            default:
                Jsi_LogBug("Unexpected value type");
        }
    }
    return r;
}

Project: jsish

static char **jsiRlGetMatches(const char *cstr, int start, int end) {
    char **matches = NULL;
    char *str = rl_line_buffer;
    jsiRlStart = start;
    if (1 || start == 0 || !completeValues) {
        int rc;
        Jsi_Interp* interp = jsi_interactiveInterp;
        if (!completeValues)
            completeValues = Jsi_ValueNew1(interp);
        Jsi_Value *func = interp->onComplete;
        if (func == NULL || !Jsi_ValueIsFunction(interp, func))
            func = Jsi_NameLookup(interp, "Info.completions");
        if (func && Jsi_ValueIsFunction(interp, func)) {
            Jsi_Value *items[3] = {};
            items[0] = Jsi_ValueNewStringDup(interp, str);
            items[1] = Jsi_ValueNewNumber(interp, (Jsi_Number)start);
            items[2] = Jsi_ValueNewNumber(interp, (Jsi_Number)end);;
            Jsi_Value *args = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, items, 3, 0));
            Jsi_IncrRefCount(interp, args);
            rc = Jsi_FunctionInvoke(interp, func, args, &completeValues, interp->csc);
            Jsi_DecrRefCount(interp, args);
            if (rc != JSI_OK)
                fprintf(stderr, "bad completion: %s %d %d\n", str?str:"", start, end);
        }
        matches = rl_completion_matches(str, jsiRlCmdMatches);
    }
    return matches;
}

Project: jsish

static int jsi_wsServeHeader(jsi_wsPss *pss, struct lws *wsi, int strLen,
    int code, const char *extra, const char *mime, Jsi_DString *jStr)
{
    uchar ubuf[JSI_BUFSIZ], *p=ubuf, *end = &ubuf[sizeof(ubuf)-1];
    if (!mime) mime = "text/html";
    if (code<=0) code = 200;
    if (lws_add_http_header_status(wsi, code, &p, end))
        return -1;
    const char *srv = pss->cmdPtr->server;
    if (!srv) srv = "jsiWebSocket";
    if (srv[0] && lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SERVER, (uchar*)srv, Jsi_Strlen(srv), &p, end))
        return -1;
    if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE, (uchar *)mime, Jsi_Strlen(mime), &p, end))
        return -1;
    if (lws_add_http_header_content_length(wsi, strLen, &p, end))
        return -1;
    *p = 0;
    Jsi_DSAppend(jStr, (char*)ubuf, extra, NULL);
    p = ubuf;
    if (lws_finalize_http_header(wsi, &p, end))
        return -1;
    *p = 0;
    Jsi_DSAppend(jStr, (char*)ubuf, NULL);
    return 0;
}

Project: jsish

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

Project: jsish

static int try_open_file(Jsi_Interp *interp, FileObj *udf, Jsi_Value *args)
{
    int ret = JSI_ERROR;
    fileObjErase(udf);
    // TODO: stdin, stdout, stderr, etc.
    Jsi_Value *fname = Jsi_ValueArrayIndex(interp, args, 0);
    if (fname && Jsi_ValueIsString(interp, fname)) {
        Jsi_Value *vmode = Jsi_ValueArrayIndex(interp, args, 1);
        const char *mode = NULL;
        const char *fstr = Jsi_ValueString(interp, fname, NULL);
        if (vmode && Jsi_ValueIsString(interp,vmode)) {
            mode = Jsi_ValueString(interp, vmode, NULL);
        }
        int writ = (mode && (Jsi_Strchr(mode,'w')||Jsi_Strchr(mode,'+')));
        int aflag = (writ ? JSI_INTACCESS_WRITE : JSI_INTACCESS_READ);
        if (interp->isSafe && Jsi_InterpAccess(interp, fname, aflag) != JSI_OK)
            return Jsi_LogError("Safe open failed");
        char *rmode = Jsi_Strdup(mode ? mode : "r");
        Jsi_Channel chan = Jsi_Open(interp, fname, rmode);
        if (chan) {
            udf->chan = chan;
            udf->fname = fname;
            udf->interp = interp;
            Jsi_IncrRefCount(interp, fname);
            udf->filename = Jsi_Strdup(fstr);
            udf->mode = Jsi_Strdup(rmode);
            ret = JSI_OK;
        }
        Jsi_Free(rmode);
    }
    return ret;
}

Project: jsish

Jsi_Channel Jsi_GetStdChannel(Jsi_Interp *interp, int id) {
    if (id<0 || id>2)
        return NULL;
    return jsiIntData.stdChans+id;
}

Project: jsish

static Jsi_RC Jsi_DoneSqlite(Jsi_Interp *interp)
{
    Jsi_UserObjUnregister(interp, &sqliteobject);
    const char *provide = "Sqlite";
    Jsi_PkgProvide(interp, provide, -1, NULL);
    return JSI_OK;
}

Project: jsish

static MySqlObj *_mysql_getDbHandle(Jsi_Interp *interp, Jsi_Value *_this, Jsi_Func *funcPtr)
{
    MySqlObj *jdb = (MySqlObj*)Jsi_UserObjGetData(interp, _this, funcPtr);
    if (!jdb) {
        Jsi_LogError("MySql call to a non-mysql object");
        return NULL;
    }
    if (!jdb->db)
    {
        Jsi_LogError("MySql db is closed");
        return NULL;
    }
    return jdb;
}

Project: jsish

static void mdbEvalRowInfo( MyDbEvalContext *eval, int *pnCol, char ***papColName, Jsi_OptionId **papColType) {
    if (!papColName) {
        //TODO: Array ???
    }
    *papColName = eval->prep->colNames;
    *papColType = eval->prep->colTypes;
    *pnCol = eval->prep->numCol;
}

Project: jsish

static Jsi_OpCodes *code_next() { JSI_NEW_CODES(0,OP_NEXT, 0); }

Project: jsish

static void mdbOutputCsv(QueryOpts *p, const char *z, Jsi_DString *dStr, int bSep)
{
    if( z==0 ) {
        Jsi_DSAppend(dStr,  p->nullvalue?p->nullvalue:"", NULL);
    } else {
        int i;
        int nSep = Jsi_Strlen(p->separator);
        for(i=0; z[i]; i++) {
            if( needCsvQuote[((unsigned char*)z)[i]] || 
                (z[i]==p->separator[0] && (nSep==1 || memcmp(z, p->separator, nSep)==0)) ) {
                i = 0;
                break;
            }
        }
        if( i==0 ) {
            Jsi_DSAppend(dStr, "\"", NULL);
            for(i=0; z[i]; i++) {
                if( z[i]=='"' ) Jsi_DSAppend(dStr, "\"", NULL);
                Jsi_DSAppendLen(dStr, z+i, 1);
            }
            Jsi_DSAppend(dStr, "\"", NULL);
        } else {
            Jsi_DSAppend(dStr, z, NULL);
        }
    }
    if( bSep ) {
        Jsi_DSAppend(dStr, p->separator, NULL);
    }
}

Project: jsish

static Jsi_RC StringLastIndexOfCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    int sLen, bLen;
    const char *v;
    ChkString(_this, funcPtr, v, &sLen, &bLen);

    Jsi_Value *seq = Jsi_ValueArrayIndex(interp, args, skip);
    Jsi_Value *start = Jsi_ValueArrayIndex(interp, args, 1+skip);

    if (!seq) {
        Jsi_ValueMakeNumber(interp, ret, -1);
        return JSI_OK;
    }

    const char *vseq = Jsi_ValueToString(interp, seq, NULL);
    int istart = 0;
    if (start) {
        if (Jsi_GetIntFromValue(interp, start, &istart)) {
            return JSI_ERROR;
        }
        if (istart < 0) istart = 0;
    }

    int r = Jsi_Strrpos(v, istart, vseq, 0);
    Jsi_ValueMakeNumber(interp, ret, r);

    return JSI_OK;;
}

Project: jsish

Jsi_RC Jsi_EvalZip(Jsi_Interp *interp, const char *exeFile, const char *mntDir, int *jsFound)
{
#if JSI__ZVFS==1
    Jsi_StatBuf stat;
    Jsi_Value *vinit, *linit, *vmnt = NULL;
    Jsi_Value *vexe = Jsi_ValueNewStringKey(interp, exeFile);
    Jsi_Value *ret = NULL;
    Jsi_RC rc;
    const char *omntDir = mntDir;
    int pass = 0;
    Jsi_IncrRefCount(interp, vexe);
    Jsi_HashSet(interp->genValueTbl, vexe, vexe);
    if (jsFound)
        *jsFound = 0;
    if (!mntDir)
        ret = Jsi_ValueNew(interp);
    else {
        vmnt = Jsi_ValueNewStringKey(interp, mntDir);
        Jsi_IncrRefCount(interp, vmnt);
        Jsi_HashSet(interp->genValueTbl, vmnt, vmnt);
    }
    bool osafe = interp->isSafe;
    if (interp->startSafe)
        interp->isSafe = 0;
    rc =Jsi_Mount(interp, vexe, vmnt, &ret);
    interp->isSafe = osafe;
    if (rc != JSI_OK)
        return rc;
    Jsi_DString dStr, bStr;
    Jsi_DSInit(&dStr);
    Jsi_DSInit(&bStr);
    if (!mntDir) {
        mntDir = Jsi_KeyAdd(interp, Jsi_ValueString(interp, ret, NULL));
        Jsi_DecrRefCount(interp, ret);
    }
dochk:
    Jsi_DSAppend(&dStr, mntDir, "/main.jsi", NULL);
    if (interp->pkgDirs)
        Jsi_ValueArrayPush(interp, interp->pkgDirs, Jsi_ValueNewStringKey(interp, mntDir));
    else {
        interp->pkgDirs = Jsi_ValueNewArray(interp, &mntDir, 1);
        Jsi_IncrRefCount(interp, interp->pkgDirs);
    }
    vinit = Jsi_ValueNewStringKey(interp,  Jsi_DSValue(&dStr));
    Jsi_IncrRefCount(interp, vinit);
    Jsi_HashSet(interp->genValueTbl, vinit, vinit);
    Jsi_DSFree(&dStr);
    Jsi_DSAppend(&dStr, mntDir, "/lib", NULL);
    const char *str = Jsi_DSValue(&dStr);
    linit = Jsi_ValueNewStringKey(interp, str);
    Jsi_IncrRefCount(interp, linit);
    if (Jsi_Stat(interp, linit, &stat) == 0)
        Jsi_ValueArrayPush(interp, interp->pkgDirs, linit);
    Jsi_DecrRefCount(interp, linit);
    Jsi_DSFree(&dStr);
    Jsi_DSFree(&bStr);
    if (Jsi_Stat(interp, vinit, &stat) == 0) {
        if (jsFound)
            *jsFound |= JSI_ZIP_MAIN;
        interp->execZip = vexe;
        return Jsi_EvalFile(interp, vinit, JSI_EVAL_ARGV0|JSI_EVAL_AUTOINDEX);
    }
    if (Jsi_AddAutoFiles(interp, mntDir) && omntDir)
        *jsFound = JSI_ZIP_INDEX;
    if (!pass++) {
        str = Jsi_Strrchr(exeFile, '/');
        if (str) str++;
        else str = exeFile;
        char *bn = Jsi_DSAppend(&bStr, mntDir, "/", str, NULL);
        bn = Jsi_Strrchr(bn, '.');
        if (bn) *bn = 0;
        mntDir = Jsi_DSValue(&bStr);
        linit = Jsi_ValueNewStringKey(interp, mntDir);
        Jsi_IncrRefCount(interp, linit);
        int bsi = Jsi_Stat(interp, linit, &stat);
        Jsi_DecrRefCount(interp, linit);
        if (bsi == 0)
            goto dochk;
        Jsi_DSFree(&bStr);
    }
#endif
    return JSI_OK;
}

Project: jsish

const char *jsi_ValueTypeName(Jsi_Interp *interp, Jsi_Value *val)
{
    switch (val->vt) {
        case JSI_VT_NUMBER:     return "number"; 
        case JSI_VT_STRING:     return "string"; 
        case JSI_VT_BOOL:       return "boolean"; 
        case JSI_VT_OBJECT:     if (val->d.obj->ot == JSI_OT_OBJECT && val->d.obj->isarrlist) return "array"; return jsi_ObjectTypeName(interp, val->d.obj->ot); 
        case JSI_VT_VARIABLE:   return "variable"; 
        case JSI_VT_NULL:       return "null"; 
        case JSI_VT_UNDEF:      break;
    }
    return "undefined";
}

Project: jsish

char *jsi_utf_tocase(const char *cp, int upper, Jsi_DString *dsPtr)
{
    char unibuf[10];
    while (*cp) {
#if JSI__UTF8
        if (*cp&0x80) {
            int32_t c;
            Jsi_UtfToUniChar(cp, &c);
            int n = Jsi_UniCharToUtf(c, unibuf);
            unibuf[n] = 0;
            cp += n;
        } else
#endif
        {
            unibuf[0] = (upper?toupper(*cp):tolower(*cp));
            unibuf[1] = 0;
            cp++;
        }
        if (upper==2) //totile
            upper = 0;
        Jsi_DSAppend(dsPtr, unibuf, NULL);
    }
    return Jsi_DSValue(dsPtr);
}

Project: jsish

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

Project: jsish

void Jsi_NumberItoA10(Jsi_Wide value, char* buf, int bsiz)
{
    snprintf(buf, bsiz, "%" PRId64, value);
}

Project: jsish

Jsi_RC Jsi_MutexLock(Jsi_Interp *interp, Jsi_Mutex *mtx) { return JSI_OK; }

Project: jsish

void Jsi_ShiftArgs(Jsi_Interp *interp, Jsi_Value *v) {
    if (!v)
        v = interp->args;
    if (v==NULL || v->vt != JSI_VT_OBJECT || v->d.obj->arr == NULL || v->d.obj->arrCnt <= 0)
        return;
    Jsi_Obj *obj = v->d.obj;
    int n = v->d.obj->arrCnt;
    n--;
    v = obj->arr[0];
    if (v)
        Jsi_DecrRefCount(interp, v);
    if (n>0)
        memmove(obj->arr, obj->arr+1, n*sizeof(Jsi_Value*));
    obj->arr[n] = NULL;
    Jsi_ObjSetLength(interp, obj, n);
}

Project: jsish

static void dbReleaseColumnNames(DbEvalContext *p) {
    //Jsi_Interp *interp = p->jdb->interp;

    if( p->apColName && p->apColName != (char**)p->staticColNames) {
        int i;
        for(i=0; i<p->nCol; i++) {
            Jsi_Free(p->apColName[i]);
        }
        Jsi_Free((char *)p->apColName);
    }
    if( p->apColType && p->apColType != p->staticColTypes) {
        Jsi_Free((char *)p->apColType);
    }
    p->apColName = NULL;
    p->apColType = NULL;
    p->nCol = 0;
}

Project: jsish

static Jsi_RC jsi_BitfieldToValue(Jsi_Interp *interp, Jsi_OptionSpec* spec, Jsi_Value **outValue,
    Jsi_DString *outStr, void *record, Jsi_Wide flags)
{
    Jsi_csgset *bsget = spec->init.OPT_BITS;
    Jsi_Interp *d = interp;
    int idx = spec->idx;
    uchar *data = (uchar*)record;
    int64_t inum;
    Jsi_OptionSpec* enumSpec = (typeof(enumSpec))spec->data;

    if (!d || !bsget || idx<0) 
        return Jsi_LogBug("invalid bitfield");
    Jsi_RC rc = (*bsget)(interp, data, &inum, spec, idx, 0);
    if (rc != JSI_OK)
        return JSI_ERROR;

    if (enumSpec) {
        struct numStruct { int64_t numVal; } nval = { inum };
        Jsi_OptionSpec eSpec[] = {
            JSI_OPT(CUSTOM, struct numStruct, 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) ), //TODO: extra
            JSI_OPT_END(struct numStruct)
        };
        if (JSI_OK != jsi_EnumToValue(interp, eSpec, outValue, outStr, (void*)&nval, flags))
            return JSI_ERROR;
    } else if (outStr) {
        char obuf[JSI_MAX_NUMBER_STRING];
        snprintf(obuf, sizeof(obuf), "%" PRId64, inum);
        Jsi_DSAppend(outStr, obuf, NULL);
    } else {
        Jsi_Number num = (Jsi_Number)inum;
        Jsi_ValueMakeNumber(interp, outValue, num);
    }
    return JSI_OK;
}

Project: jsish

static Jsi_RC NumberConstructor(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    if (Jsi_FunctionIsConstructor(funcPtr)) {
        Jsi_Number nv = 0.0;
        Jsi_Value *v = Jsi_ValueArrayIndex(interp, args, 0);
        if (v) {
            Jsi_ValueToNumber(interp, v);
            nv = v->d.num;
        }
        _this->d.obj->ot = JSI_OT_NUMBER;
        _this->d.obj->d.num = nv;
        Jsi_ValueToObject(interp, _this);
        Jsi_ValueMakeNumber(interp, ret, nv);
        return JSI_OK;
    }
    Jsi_Value *v = Jsi_ValueArrayIndex(interp, args, 0);
    if (v) {
        Jsi_ValueToNumber(interp, v);
        Jsi_ValueDup2(interp, ret, v);
        Jsi_ValueToObject(interp, *ret);
        return JSI_OK;
    }
    Jsi_ValueMakeNumber(interp, ret, 0.0);
    return JSI_OK;
}

Project: jsish

static Jsi_RC StringSplitCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this, //TODO: UTF
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    char **argv; int argc;
    int sLen, bLen, spLen = 1, noEmpty=0;
    Jsi_RC rc = JSI_OK;
    const char *v, *split = "";
    
    ChkString(_this, funcPtr, v, &sLen, &bLen);

    Jsi_Value *spliton = Jsi_ValueArrayIndex(interp, args, skip);
    
    if (spliton) {
        if (Jsi_ValueIsNull(interp, spliton))
            noEmpty = 1;
        else if (Jsi_ValueIsString(interp, spliton))
            split = Jsi_ValueString(interp, spliton, &spLen);
    }

    Jsi_DString sStr;
    Jsi_DSInit(&sStr);
    if (spLen)
        Jsi_SplitStr(v, &argc, &argv, split, &sStr);
    Jsi_Obj *obj = Jsi_ObjNewType(interp, JSI_OT_ARRAY);
    int i, n = 0, siz = argc;
    if (noEmpty) {
        for (i=0, siz=0; i<argc; i++) if (argv[i][0]) siz++;
    } else if (!spLen)
        argc = siz = bLen;
    if (Jsi_ObjArraySizer(interp, obj, siz) <= 0) {
        rc = Jsi_LogError("index too large: %d", siz);
        Jsi_ObjFree(interp, obj);
        goto bail;
    }
    Jsi_ValueMakeArrayObject(interp, ret, obj);
    for (i = 0; i < argc; ++i) {
        if (noEmpty && !argv[i][0]) continue;
        char ctmp[2] = " ",  *cst = ctmp;
        if (!spLen)
            ctmp[0] = v[i];
        else
            cst = argv[i];
        Jsi_Value *v = Jsi_ValueNewStringDup(interp, cst);
        Jsi_IncrRefCount(interp, v);
        obj->arr[n++] = v;
    }
    Jsi_ObjSetLength(interp, obj, siz);
    
bail:
    Jsi_DSFree(&sStr);
    return rc;
}

Project: jsish

static Jsi_RC StringRepeatCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    int sLen, bLen;
    const char *v;
    ChkString(_this, funcPtr, v, &sLen, &bLen);
    
    Jsi_Value *start = Jsi_ValueArrayIndex(interp, args, skip);
    
    Jsi_Number ncnt;
    if (!start || Jsi_GetNumberFromValue(interp,start, &ncnt) != JSI_OK || ncnt>MAX_LOOP_COUNT || ncnt<0 || bLen<=0) {
        return JSI_ERROR;
    }
    int cnt = (int)ncnt;
    Jsi_DString dStr = {};
    while (cnt-- > 0) {
        Jsi_DSAppendLen(&dStr, v, bLen);
        if (Jsi_DSLength(&dStr)>JSI_MAX_ALLOC_BUF) {
            Jsi_DSFree(&dStr);
            return Jsi_LogError("too long");
        }
    }
    Jsi_ValueFromDS(interp, &dStr, ret);
    return JSI_OK;
}

Project: jsish

bool Jsi_OptionsValid(Jsi_Interp *interp,  Jsi_OptionSpec* spec)
{
    int i = 0;
    while (spec[i].id>=JSI_OPTION_BOOL && spec[i].id < JSI_OPTION_END) {
        SIGASSERTMASK(spec+i, OPTS, 0xff);
        if (spec[i].help && Jsi_Strchr(spec[i].help, '\n')) {
            if (interp)
                Jsi_LogError("item \"%s\": help contains newline", spec[i].name);
            return 0;
        }
        i++;
    }
    return (i>0);
}

Project: jsish

static Jsi_RC jsi_wsfreePss(Jsi_Interp *interp, Jsi_HashEntry* hPtr, void *ptr) {
    jsi_wsPss *pss = (jsi_wsPss*)ptr;
    WSSIGASSERT(pss, PWS);
    if (pss) {
        pss->hPtr = NULL;
        jsi_wsdeletePss(pss);
    }
    return JSI_OK;
}

Project: jsish

static Jsi_RC mdbEvalCallCmd( MyDbEvalContext *p, Jsi_Interp *interp, Jsi_RC result)
{
    int cnt = 0;
    Jsi_RC rc = result;
    Jsi_Value *varg1;
    Jsi_Obj *argso;
    char **apColName = NULL;
    Jsi_OptionId *apColType = NULL;
    if (p->jdb->debug & mdbTMODE_EVAL)
        JSI_DBQUERY_PRINTF( "DEBUG: eval\n");

    while( (rc==JSI_OK) && JSI_OK==(rc = mdbEvalStep(p)) ) {
        int i;
        int nCol;

        cnt++;
        mdbEvalRowInfo(p, &nCol, &apColName, &apColType);
        if (nCol<=0)
            continue;
        if (Jsi_ValueIsNull(interp,p->tocall))
            continue;
        /* Single object containing sql result members. */
        varg1 = Jsi_ValueMakeObject(interp, NULL, argso = Jsi_ObjNew(interp));
        for(i=0; i<nCol; i++) {
            Jsi_Value *nnv = mdbEvalSetColumnValue(p, i, NULL);
            Jsi_ObjInsert(interp, argso, apColName[i], nnv, 0);
        }
        Jsi_IncrRefCount(interp, varg1);
        bool rb = Jsi_FunctionInvokeBool(interp, p->tocall, varg1);
        if (Jsi_InterpGone(interp))
            return JSI_ERROR;
        Jsi_DecrRefCount(interp, varg1);
        if (rb)
            break;
    }
    //mdbEvalFinalize(p);

    if( rc==JSI_OK || rc==JSI_BREAK ) {
        //Jsi_ResetResult(interp);
        rc = JSI_OK;
    }
    return rc;
}

Project: jsish

static int tree_postorder(Jsi_Tree *treePtr, Jsi_TreeEntry *hPtr, Jsi_TreeWalkProc *callback, void *data) {
    uint epoch = treePtr->epoch;
    if (hPtr == NULL) return JSI_OK;
    if (hPtr->right != NULL) {
        if (tree_postorder(treePtr, hPtr->right, callback, data) != JSI_OK || epoch != treePtr->epoch)
            return JSI_ERROR;
    }
    if (hPtr->left != NULL) {
        if (tree_postorder(treePtr, hPtr->left, callback, data) != JSI_OK || epoch != treePtr->epoch)
            return JSI_ERROR;
    }
    if (callback(treePtr, hPtr, data) != JSI_OK || epoch != treePtr->epoch)
        return JSI_ERROR;
    return JSI_OK;
}

Project: jsish

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

Project: jsish

static bool jsi_wswebsocketObjIsTrue(void *data)
{
    return 1;
}

Project: jsish

int jsi_PstateSetString(jsi_Pstate *ps, const char *str)
{
    Jsi_Interp *interp = ps->interp;
    jsi_Lexer *l = ps->lexer;
    jsi_PstateClear(ps);
    l->ltype = LT_STRING;
    Jsi_HashEntry *hPtr = Jsi_HashEntryNew(interp->codeTbl, (void*)str, NULL);
    assert(hPtr);
    l->d.str = (char*)Jsi_HashKeyGet(hPtr);
    return JSI_OK;
}

Project: jsish

Jsi_Interp* Jsi_InterpNew(Jsi_InterpOpts *opts)
{
    return jsi_InterpNew(NULL, NULL, opts);
}

Project: jsish

static Jsi_OpCodes *code_dec(jsi_Pstate *p, jsi_Pline *line, int e) { JSI_NEW_CODESLN(0,OP_DEC, e); }

Project: jsish

static void jsi_PushFunc(jsi_Pstate *ps, jsi_OpCode *ip, jsi_ScopeChain *scope, Jsi_Value *currentScope) {
    /* TODO: now that we're caching ps, may need to reference function ps for context_id??? */
    Jsi_Interp *interp = ps->interp;
    Jsi_FuncObj *fo = jsi_FuncObjNew(interp, (Jsi_Func *)ip->data);
    fo->scope = jsi_ScopeChainDupNext(interp, scope, currentScope);
    Jsi_Obj *obj = Jsi_ObjNewType(interp, JSI_OT_FUNCTION);
    obj->d.fobj = fo;
    
    Jsi_Value *v = _jsi_STACKIDX(interp->framePtr->Sp), *fun_prototype = jsi_ObjValueNew(interp);
    fun_prototype->d.obj->__proto__ = interp->Object_prototype;                
    Jsi_ValueMakeObject(interp, &v, obj);
    Jsi_ValueInsert(interp, v, "prototype", fun_prototype, JSI_OM_DONTDEL|JSI_OM_DONTENUM);
    /* TODO: make own prototype and prototype.constructor */
    
    bool isNew;
    Jsi_HashEntry *hPtr;  Jsi_Value *vv;
    if (interp->framePtr->Sp == 1 && (vv=_jsi_STACKIDX(0))->vt == JSI_VT_VARIABLE) {
        const char *varname = NULL;
        vv = vv->d.lval;
        if (vv && vv->f.bits.lookupfailed && vv->d.lookupFail) {
            varname = vv->d.lookupFail;
            vv->f.bits.lookupfailed = 0;
        }
        if (varname) {
            if (!fo->func->name)
                fo->func->name = varname;
            hPtr = Jsi_HashEntryNew(interp->varTbl, varname, &isNew);
            if (hPtr)
                Jsi_HashValueSet(hPtr, obj);
        }
    }
    hPtr = Jsi_HashEntryNew(interp->funcObjTbl, fo, &isNew);
    if (hPtr && isNew) {
        Jsi_ObjIncrRefCount(interp, obj);
        Jsi_HashValueSet(hPtr, obj);
    }
    jsiPush(interp,1);
}

Project: jsish

static Jsi_RC jsi_StringFree(Jsi_Interp *interp, Jsi_HashEntry *hPtr, void *d) {
    Jsi_String *s = (Jsi_String *)d;
    if (s->flags&1)
        Jsi_Free(s->str);
    Jsi_Free(s);
    return JSI_OK;
}

Project: jsish

static Jsi_RC InterpInfoCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    InterpObj *udf = (InterpObj *)Jsi_UserObjGetData(interp, _this, funcPtr);
    Jsi_Interp *subinterp = interp;
    if (udf) {
        if (!udf->subinterp)
            return Jsi_LogError("Sub-interp gone");
        subinterp = udf->subinterp;
    }
    return jsi_InterpInfo(subinterp, args, _this, ret, funcPtr);
}

Project: jsish

static Jsi_RC jsi_wswebsockUpdate(Jsi_Interp *interp, void *data)
{
    jsi_wsCmdObj *cmdPtr = (jsi_wsCmdObj*)data;
    WSSIGASSERT(cmdPtr,OBJ);
    jsi_wsService(cmdPtr);
    return JSI_OK;
}

Project: jsish

static Jsi_OpCodes *code_mod() { JSI_NEW_CODES(0,OP_MOD, 0); }

Project: jsish

static void jsonNewDString(Jsi_Interp *interp, Jsi_DString *dStr, const char* str, int len)
{
    char buf[JSI_MAX_NUMBER_STRING], *dp = buf;
    const char *cp = str;
    int ulen;
    
    while ((cp-str)<len) {
        if (*cp == '\\') {
            switch (cp[1]) {
                case 'b': *dp++ = '\b'; break;
                case 'n': *dp++ = '\n'; break;
                case 'r': *dp++ = '\r'; break;
                case 'f': *dp++ = '\f'; break;
                case 't': *dp++ = '\t'; break;
                case '\"': *dp++ = '\"'; break;
                case '\\': *dp++ = '\\'; break;
                case 'u': 
                    if ((ulen=Jsi_UtfDecode(cp+2, dp))) {
                        dp += ulen;
                        cp += 4;
                    } else {                    
                        *dp++ = '\\';
                        *dp++ = 'u';
                    }
                    break;
            }
            cp+=2;
        } else {
            *dp++ = *cp++;
        }
        if ((dp-buf)>90) {
            *dp = 0;
            Jsi_DSAppendLen(dStr, buf, dp-buf);
            dp = buf;
        }
    }
    *dp = 0;
    Jsi_DSAppendLen(dStr, buf, dp-buf);
}

Project: jsish

void jsi_memDebugBreak() {
}

Project: jsish

static void dbOutputCsv(QueryOpts *p, const char *z, Jsi_DString *dStr, int bSep)
{
    if( z==0 ) {
        Jsi_DSAppend(dStr,  p->nullvalue?p->nullvalue:"", NULL);
    } else {
        int i;
        int nSep = Jsi_Strlen(p->separator);
        for(i=0; z[i]; i++) {
            if( needCsvQuote[((unsigned char*)z)[i]] || 
                (z[i]==p->separator[0] && (nSep==1 || memcmp(z, p->separator, nSep)==0)) ) {
                i = 0;
                break;
            }
        }
        if( i==0 ) {
            Jsi_DSAppend(dStr, "\"", NULL);
            for(i=0; z[i]; i++) {
                if( z[i]=='"' ) Jsi_DSAppend(dStr, "\"", NULL);
                Jsi_DSAppendLen(dStr, z+i, 1);
            }
            Jsi_DSAppend(dStr, "\"", NULL);
        } else {
            Jsi_DSAppend(dStr, z, NULL);
        }
    }
    if( bSep ) {
        Jsi_DSAppend(dStr, p->separator, NULL);
    }
}

Project: jsish

int Jsi_ValueGetLength(Jsi_Interp *interp, Jsi_Value *v) {
    if (Jsi_ValueIsArray(interp, v))
        return v->d.obj->arrCnt;
    Jsi_LogWarn("expected array");
    return 0;
}

Project: jsish

Jsi_RC Jsi_LogMsg(Jsi_Interp *interp, uint code, const char *format,...) {
    if (Jsi_InterpGone(interp))
        return JSI_ERROR;
    va_list va;
    va_start (va, format);
    char pbuf[JSI_BUFSIZ/8] = "";
    char buf[JSI_BUFSIZ/2];
    const char *term = "", *pterm=pbuf;
    static char lastMsg[JSI_BUFSIZ/2] = "";
    static int lastCnt = 0;
    static Jsi_Interp *LastInterp = NULL;
    Jsi_Interp *lastInterp = LastInterp;
    const char *emsg = buf, *mt;
    int islog, line = 0, lofs = 0, noDups=0;
    bool isHelp = (format[0]=='.' && !Jsi_Strncmp(format, "...", 3));
    Jsi_OptionSpec *oep = interp->parseMsgSpec;
    const char *pps = "", *curFile = "";
    char *ss = interp->lastPushStr;
    
    if (interp==NULL)
        interp = jsiIntData.mainInterp;
    LastInterp = interp;
    if (lastInterp != interp)
        noDups = 1;
    
    /* Filter out try/catch (TODO: and non-syntax errors??). */
    if (interp == NULL) {
//nullInterp:
        if (logHook)
            (*logHook)(format, va);
        else {
            vfprintf(stderr, format, va);
            fputc('\n', stderr);
        }
        va_end(va);
        return JSI_ERROR;
    }
    curFile = jsi_GetCurFile(interp);
    switch (code) {
        case JSI_LOG_INFO:  if (!interp->logOpts.Info) goto bail; break;
        case JSI_LOG_WARN:  if (!interp->logOpts.Warn) goto bail; break;
        case JSI_LOG_DEBUG: if (!interp->logOpts.Debug && !jsi_LogEnabled(interp, code)) goto bail; break;
        case JSI_LOG_TRACE: if (!interp->logOpts.Trace && !jsi_LogEnabled(interp, code)) goto bail; break;
        case JSI_LOG_TEST:  if (!interp->logOpts.Test && !jsi_LogEnabled(interp, code)) goto bail; break;
        case JSI_LOG_PARSE: break; //if (!interp->parent) goto nullInterp; break;
        case JSI_LOG_ERROR: {
            if (!interp->logOpts.Error) goto bail;
            if ((interp->framePtr->tryDepth - interp->framePtr->withDepth)>0 && interp->inParse<=0 
                && (!interp->tryList || !(interp->tryList->inCatch|interp->tryList->inFinal))) { 
                /* Should only do the first or traceback? */
                if (!interp->errMsgBuf[0]) {
                    vsnprintf(interp->errMsgBuf, sizeof(interp->errMsgBuf), format, va);
                    //interp->errMsgBuf[sizeof(interp->errMsgBuf)-1] = 0;
                    interp->errFile =  jsi_GetCurFile(interp);
                    interp->errLine = (interp->curIp?interp->curIp->Line:0);
                    emsg = interp->errMsgBuf;
                }
                goto done;
            }
            interp->logErrorCnt++;
            break;
        }
    }
    mt = (code <= JSI__LOGLAST ? jsi_LogCodes[code] : "");
    if (isHelp) mt = "help";
    assert((JSI__LOGLAST+2) == (sizeof(jsi_LogCodes)/sizeof(jsi_LogCodes[0])));
    if (!Jsi_Strchr(format,'\n')) term = "\n";
    if (interp->strict && interp->lastParseOpt)
        ss = (char*)Jsi_ValueToString(interp, interp->lastParseOpt, NULL);
    if (code != JSI_LOG_INFO && code < JSI_LOG_TEST && interp && ss && ss[0]) {
        char psbuf[JSI_BUFSIZ/6];
        if (Jsi_Strchr(ss,'%')) {
            char *s = ss, *sd = psbuf;
            int plen=0, llen = sizeof(psbuf)-2;
            while (*s && plen<llen) {
                if (*s == '%')
                    sd[plen++] = '%';
                sd[plen++] = *s;
                s++;
            }
            sd[plen] = 0;
            ss = psbuf;
        }
        while (*ss && isspace(*ss))
            ss++;
        if (*ss && !isHelp)
            snprintf(pbuf, sizeof(pbuf), "    (at or near \"%s\")\n", ss);
    }
    pbuf[sizeof(pbuf)-1] = 0;
    if (interp->inParse && interp->parseLine) {
        line = interp->parseLine->first_line;
        lofs = interp->parseLine->first_column;
    } else if (interp->inParse && interp->parsePs) {
        line = interp->parsePs->lexer->cur_line;
        lofs = interp->parsePs->lexer->cur_char;
    } else if (interp->curIp) {
        if (interp->callerErr && interp->framePtr && interp->framePtr->parent) {
            jsi_Frame *fptr = interp->framePtr->parent;
            line = fptr->line;
            lofs = 0;
            curFile = fptr->fileName;
        } else {
            line = interp->curIp->Line;
            lofs = interp->curIp->Lofs;
            if (line<=0)
                line = interp->framePtr->line;
        }
    }
    islog = (interp->parent && interp->debugOpts.msgCallback && code != JSI_LOG_BUG);
    Jsi_DString pStr;
    Jsi_DSInit(&pStr);
    if (oep) {
        if (oep->id != JSI_OPTION_CUSTOM || !oep->custom)
            pps = Jsi_DSPrintf(&pStr, "for option \"%s\": ", oep->name);
        else {
            Jsi_OptionCustom* cust = Jsi_OptionCustomBuiltin(oep->custom);
            pps = Jsi_DSPrintf(&pStr, "for %s option \"%s\": ", (cust?cust->name:""), oep->name);
        }
    }
    char *cpt;
    if (curFile && interp->logOpts.ftail && (cpt =Jsi_Strrchr(curFile, '/')) && cpt[1])
        curFile = cpt+1;
    if (curFile && curFile[0] && Jsi_Strchr(curFile,'%')==0 && !islog) {
        if (!interp->subOpts.logColNums)
            snprintf(buf, sizeof(buf), "%s:%d: %s: %s%s%s%s",  curFile, line, mt, pps, format, pterm, term);
        else
            snprintf(buf, sizeof(buf), "%s:%d.%d: %s: %s%s%s%s",  curFile, line, lofs, pps, mt,format, pterm, term);
    } else {
        snprintf(buf, sizeof(buf), "%s: %s%s%s%s", mt, pps, format, pterm, term);
    }
    Jsi_DSFree(&pStr);
    buf[sizeof(buf)-1]=0;

    if (logHook)
        (*logHook)(buf, va);
    else if (interp->subOpts.logAllowDups)
        vfprintf(stderr, buf, va);
    else {
        char buf1[JSI_BUFSIZ/2];
        vsnprintf(buf1, sizeof(buf1), buf, va);
        if (!isHelp && !noDups) {
            if (buf1[0] && lastCnt && Jsi_Strcmp(buf1, lastMsg)==0) {
                lastCnt++;
                goto done;
            } else if (lastMsg[0] && lastCnt>1 ) {
                fprintf(stderr, "REPEAT: Last msg repeated %d times...\"\n" ,lastCnt);
            }
            if (buf1[0] == 0 || (buf1[0] == '.' && buf1[1] == 0))
                goto done;
        }
        lastCnt = 1;
        Jsi_Strcpy(lastMsg, buf1);
        if (!islog)
            Jsi_Puts(interp, jsi_Stderr, buf1, -1);
            //fputs(buf1, stderr);
        else {
            Jsi_DString jStr={}, kStr={};
            Jsi_DSPrintf(&kStr, "[%s, \"%s\", \"%s\", %d, %d ]",
                Jsi_JSONQuote(interp, buf1, -1, &jStr), mt, curFile, line, lofs);
            if (Jsi_FunctionInvokeJSON(interp->parent, interp->debugOpts.msgCallback, Jsi_DSValue(&kStr), NULL) != JSI_OK)
                code = 1;
            Jsi_DSFree(&jStr);
            Jsi_DSFree(&kStr);
        }
    }
done:
    va_end(va);
    if (interp->debugOpts.hook) {
        static int inhook = 0;
        if (!inhook) {
            inhook = 1;
            (*interp->debugOpts.hook)(interp, curFile, interp->curIp?interp->curIp->Line:0, interp->level, interp->curFunction, "DEBUG", NULL, emsg);
        }
        inhook = 0;
    }
    if ((code & jsi_fatalexit) && !interp->opts.no_exit)
        jsi_DoExit(interp, 1);
    return (code==JSI_LOG_ERROR?JSI_ERROR:JSI_OK);
bail:
    va_end(va);
    return JSI_OK;
}

Project: jsish

Jsi_RC jsi_InitFilesys(Jsi_Interp *interp, int release)
{
    if (release) {
        jsi_FilesysDone(interp);
        return JSI_OK;
    }
    Jsi_DSInit(&jsiIntData.pwdStr);
    Jsi_Hash *fsys = Jsi_UserObjRegister(interp, &fileobject);
    if (!fsys)
        return Jsi_LogBug("Can not init file system");
    Jsi_CommandCreateSpecs(interp, fileobject.name, filesysCmds, fsys, JSI_CMDSPEC_ISOBJ);
    if (jsiFSList == NULL) {
        Jsi_FSRegister(&jsiFilesystem, NULL);
        SetupStdChan(jsiIntData.stdChans, stdin, &jsiFilesystem, JSI_FS_READONLY);
        SetupStdChan(jsiIntData.stdChans+1, stdout, &jsiFilesystem, JSI_FS_WRITEONLY);
        SetupStdChan(jsiIntData.stdChans+2, stderr, &jsiFilesystem, JSI_FS_WRITEONLY);
    }
#if JSI__ZVFS==1
    Jsi_InitZvfs(interp, 0);
#endif
#ifndef JSI_OMIT_VFS
    jsi_InitVfs(interp, 0);
#endif
    return JSI_OK;
}

Project: jsish

Jsi_ListEntry* Jsi_ListPush(Jsi_List *list, Jsi_ListEntry *item, Jsi_ListEntry *before)
{
    Assert(item && list);
    if (item->list && (item->list->head == item || item->prev || item->next)) {
        Assert(list->opts.freeListProc == item->list->opts.freeListProc);
        Jsi_ListPop(item->list, item);
    }
        
    if (!item->list)
         item->list = list;
    else if (list != item->list) {
        Assert(list->opts.freeListProc == item->list->opts.freeListProc);
        item->list = list;
    }
    if (!list->head) {
        list->head = list->tail = item;
    } else if (item == list->head) {
        assert(0);
    } else if (before == NULL) {
        item->prev = list->tail;
        list->tail->next = item;
        list->tail = item;
    } else if (before == list->head) {
        item->next = list->head;
        list->head->prev = item;
        list->head = item;
    } else {
        item->next = before;
        item->prev = before->prev;
        before->prev->next = item;
        before->prev = item;
    }
    list->numEntries++;
    item->list = list;
    return item;
}

Project: jsish

static Jsi_RC MySqlLastQueryCmd(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;
    const char *str = mysql_info(jdb->db);
    if (str)
        Jsi_ValueMakeStringDup(interp, ret, str);
    return JSI_OK;
}

Project: jsish

Jsi_RC Jsi_PrototypeObjSet(Jsi_Interp *interp, const char *key, Jsi_Obj *obj)
{
    Jsi_Value *val;
    Jsi_HashEntry *hPtr = Jsi_HashEntryFind(interp->protoTbl, key);
    if (!hPtr)
        return JSI_ERROR;
    val = (Jsi_Value *)Jsi_HashValueGet(hPtr);
    obj->__proto__ = val;
    return JSI_OK;
}

Project: jsish

static Jsi_RC jsi_wsHandlerAdd(Jsi_Interp *interp, jsi_wsCmdObj *cmdPtr, const char *ext, const char *cmd, int flags)
{
    Jsi_HashEntry *hPtr;
    jsi_wsHander *hdlPtr;
    Jsi_Value *valPtr = Jsi_ValueNewStringDup(interp, cmd);
    hPtr = Jsi_HashEntryNew(cmdPtr->handlers, ext, NULL);
    if (!hPtr)
        return JSI_ERROR;
    hdlPtr = (jsi_wsHander *)Jsi_Calloc(1, sizeof(*hdlPtr));
    hdlPtr->val = valPtr;
    hdlPtr->flags = flags;
    Jsi_HashValueSet(hPtr, hdlPtr);
    Jsi_IncrRefCount(interp, valPtr);
    return JSI_OK;
}

Project: jsish

Jsi_RC Jsi_InitSqlite(Jsi_Interp *interp, int release)
{
    if (release) return Jsi_DoneSqlite(interp);
    Jsi_Hash* dbSys;
#if JSI_USE_STUBS
    if (Jsi_StubsInit(interp, 0) != JSI_OK)
        return JSI_ERROR;
#endif

    if (!(dbSys = Jsi_UserObjRegister(interp, &sqliteobject))) 
        return Jsi_LogError("Failed to init sqlite extension");

    Jsi_Value *info = Jsi_ValueNew1(interp);
    Jsi_JSONParseFmt(interp, &info, "{libVer:\"%s\", hdrVer:\"%s\", hdrNum:%d, hdrSrcId:\"%s\", pkgVer:%d}",
        (char *)sqlite3_libversion(), SQLITE_VERSION, SQLITE_VERSION_NUMBER, SQLITE_SOURCE_ID, jsi_DbPkgVersion);
    Jsi_PkgOpts dbPkgOpts = { db_ObjCmd_Specs, &dbObjCmd, sqliteCmds, info };
    Jsi_RC rc = Jsi_PkgProvideEx(interp, "Sqlite", jsi_DbPkgVersion, Jsi_InitSqlite, &dbPkgOpts);
    Jsi_DecrRefCount(interp, info);
    if (rc != JSI_OK)
        return JSI_ERROR;

    if (!Jsi_CommandCreateSpecs(interp, sqliteobject.name, sqliteCmds, dbSys, JSI_CMDSPEC_ISOBJ))
        return JSI_ERROR;        

#ifdef JSI_DB_TEST
    if (getenv("RUN_DB_TEST"))
        TestSqlite(interp);
#endif
    jsi_DbVfs **dbVfsPtrPtr = (jsi_DbVfs **)Jsi_InterpGetData(interp, JSI_SQLITE_DB_VFS, NULL);
    if (dbVfsPtrPtr)
        *dbVfsPtrPtr = &SqliteDbVfs;
    return JSI_OK;
}

Project: jsish

void Jsi_Release(Jsi_Interp* interp, void *data) {
    Jsi_HashEntry *hPtr = Jsi_HashEntryFind(interp->preserveTbl, data);
    if (!hPtr) return;
    PreserveData *ptr = (PreserveData*)Jsi_HashValueGet(hPtr);
    if (!ptr) return;
    assert(ptr->interp == interp);
    if (--ptr->refCnt > 0) return;
    if (ptr->proc)
        (*ptr->proc)(interp, data);
    Jsi_Free(ptr);
    Jsi_HashEntryDelete(hPtr);
}

Project: jsish

static void IterObjInsertKey(Jsi_IterObj *io, const char *key)
{
    assert(!io->isArrayList);
    if (io->depth) {
        uint i;
        for (i=0; i<io->count; i++) {
            if (!Jsi_Strcmp(key, io->keys[i]))
                return;
        }
    }

    if (io->count >= io->size) {
        io->size += 10;
        io->keys = (const char**)Jsi_Realloc(io->keys, io->size * sizeof(io->keys[0]));
    }
    io->keys[io->count] = key;
    io->count++;
}

Project: jsish

static Jsi_RC InterpEvalCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    return InterpEvalCmd_(interp, args, _this, ret, funcPtr, 0);
}

Project: jsish

static Jsi_RC MySqlAffectedRowsCmd(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_ValueMakeNumber(interp, ret, (Jsi_Number)mysql_affected_rows(jdb->db));
    return JSI_OK;
}

Project: jsish

static void dbPrepStmtLimit( Jsi_Db *jdb)
{
    while(jdb->stmtCacheCnt>jdb->stmtCacheMax ) {
        Jsi_ListEntry *l = Jsi_ListPopBack(jdb->stmtCache);
        dbPrepStmtFree(jdb, (SqlPreparedStmt*)Jsi_ListValueGet(l));
        jdb->stmtCacheCnt = Jsi_ListSize(jdb->stmtCache);
    }
}

Project: jsish

Jsi_ListEntry* Jsi_ListPop(Jsi_List *list, Jsi_ListEntry *item)
{
    Assert(item && list->head && list->tail && item->list);
    SIGASSERT(list, LIST);
    SIGASSERT(item, LISTENTRY);
    if (item == list->head) {
        if (list->head == list->tail)
            list->head = list->tail = NULL;
        else
            list->head = list->head->next;
    }
    else if (item == list->tail) {
        list->tail = list->tail->prev;
        list->tail->next = NULL;
    } else {
        item->prev->next = item->next;
        if (item->next)
            item->next->prev = item->prev;
    }
    list->numEntries--;
    item->next = item->prev = NULL;
    return item;
}

Project: jsish

static Jsi_OpCodes *code_add() { JSI_NEW_CODES(0,OP_ADD, 0); }

Project: jsish

static Jsi_RC freeFuncObjTbl(Jsi_Interp *interp, Jsi_HashEntry *hPtr, void *ptr) {
    Jsi_Obj *v = (Jsi_Obj *)ptr;
    if (!v) return JSI_OK;
    SIGASSERT(v,OBJ);
    if (v->ot != JSI_OT_FUNCTION)
        fprintf(stderr, "invalid func obj\n");
    else if (v->d.fobj) {
        if (v->d.fobj->scope) {
            jsi_ScopeChain *scope = v->d.fobj->scope;
            v->d.fobj->scope = NULL;
            jsi_ScopeChainFree(interp, scope);
        }
    }
    Jsi_ObjDecrRefCount(interp, v);
    return JSI_OK;
}

Project: jsish

static Jsi_OpCodes *codes_join(Jsi_OpCodes *a, Jsi_OpCodes *b)
{
    Jsi_OpCodes *ret = codes_new(a->code_len + b->code_len);
    memcpy(ret->codes, a->codes, a->code_len * sizeof(jsi_OpCode));
    memcpy(&ret->codes[a->code_len], b->codes, b->code_len * sizeof(jsi_OpCode));
    ret->code_size = a->code_len + b->code_len;
    ret->code_len = ret->code_size;
    ret->expr_counter = a->expr_counter + b->expr_counter;
#if 0
    a->code_len=0;
    jsi_FreeOpcodes(a);
    b->code_len=0;
    jsi_FreeOpcodes(b);
#else
    Jsi_Free(a->codes);
    Jsi_Free(b->codes);
#ifdef JSI_MEM_DEBUG
    if (a->hPtr)
        Jsi_HashEntryDelete(a->hPtr);
    if (b->hPtr)
        Jsi_HashEntryDelete(b->hPtr);
#endif
    Jsi_Free(a);
    Jsi_Free(b);
#endif
    jsiOpCodesCnt[1]++;
    jsiOpCodesCnt[2]-=2;
    return ret;
}

Project: jsish

void *Jsi_PrototypeGet(Jsi_Interp *interp, const char *key)
{
    Jsi_HashEntry *hPtr = Jsi_HashEntryFind(interp->protoTbl, key);
    if (!hPtr)
        return NULL;
    return Jsi_HashValueGet(hPtr);
}

Project: jsish

static Jsi_RC SqliteInterruptCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                              Jsi_Value **ret, Jsi_Func *funcPtr)
{
    Jsi_Db *jdb;
    if (!(jdb = dbGetDbHandle(interp, _this, funcPtr))) return JSI_ERROR;
    sqlite3_interrupt(jdb->db);
    return JSI_OK;
}

Project: jsish

Jsi_RC Jsi_Mount( Jsi_Interp *interp, Jsi_Value *archive, Jsi_Value *mount, Jsi_Value **ret)
{
#if JSI__ZVFS==1
    return Zvfs_Mount(interp, archive, mount, ret);
#else
    return JSI_ERROR;
#endif
}

Project: jsish

static Jsi_RC StringTrimLeftCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    return _StringTrimCmd(interp, args, _this, ret, funcPtr, 1);
}

Project: jsish

void jsi_ScopeChainFree(Jsi_Interp *interp, jsi_ScopeChain *sc)
{
    int i;
    for (i = 0; i < sc->chains_cnt; ++i) {
        Jsi_DecrRefCount(interp, sc->chains[i]);
    }
    Jsi_Free(sc->chains);
    _JSI_MEMCLEAR(sc);
    Jsi_Free(sc);
}

Project: jsish

static Jsi_RC FilesysTellCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    UdfGet(udf, _this, funcPtr);
    Jsi_Wide pos = Jsi_Tell(interp, udf->chan);
    Jsi_ValueMakeNumber(interp, ret, (Jsi_Number)pos);
    return JSI_OK;
}

Project: jsish

Jsi_MapEntry* Jsi_MapSearchFirst (Jsi_Map *mapPtr, Jsi_MapSearch *searchPtr, int flags){
    SIGASSERT(mapPtr, MAP);
    searchPtr->typ = mapPtr->typ;
    switch (mapPtr->typ) {
        case JSI_MAP_HASH: return (Jsi_MapEntry*)Jsi_HashSearchFirst(mapPtr->v.hash, &searchPtr->v.hash);
        case JSI_MAP_TREE: return (Jsi_MapEntry*)Jsi_TreeSearchFirst(mapPtr->v.tree, &searchPtr->v.tree, flags, NULL);
        case JSI_MAP_LIST: return (Jsi_MapEntry*)Jsi_ListSearchFirst(mapPtr->v.list, &searchPtr->v.list, flags);
        case JSI_MAP_NONE: break;
    }
    return NULL;
}

Project: jsish

static jsi_ForinVar *forinvar_new(jsi_Pstate *pstate, const char *varname, Jsi_OpCodes *local, Jsi_OpCodes *lval)
{
    jsi_ForinVar *r = (jsi_ForinVar*)Jsi_Calloc(1,sizeof(*r));
    r->sig = JSI_SIG_FORINVAR;
    r->varname = varname;
    r->local = local;
    r->lval = lval;
    return r;
}

Project: jsish

static Jsi_RC ThreadEvalCallback(Jsi_Interp *interp, void* data) {
    Jsi_RC rc;

    if ((rc=SubInterpEvalCallback(interp, data)) != JSI_OK)
        interp->threadErrCnt++;
    return rc;
}

Project: jsish

static Jsi_RC FilesysLstatCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    UdfGet(udf, _this, funcPtr);
#if JSI__FILESYS==1
    return jsi_FileStatCmd(interp, udf->fname, _this, ret, funcPtr, 1);
#else
    return JSI_ERROR;
#endif
}

Project: jsish

static void IterObjInsert(Jsi_IterObj *io, Jsi_TreeEntry *hPtr)
{
    IterObjInsertKey(io, (const char*)Jsi_TreeKeyGet(hPtr));
}

Project: jsish

static const char *jsi_evalprint(Jsi_Value *v)
{
    static char buf[JSI_MAX_NUMBER_STRING];
    if (!v)
        return "nil";
    if (v->vt == JSI_VT_NUMBER) {
        snprintf(buf, sizeof(buf), "NUM:%" JSI_NUMGFMT " ", v->d.num);
    } else if (v->vt == JSI_VT_BOOL) {
        snprintf(buf, sizeof(buf), "BOO:%d", v->d.val);
    } else if (v->vt == JSI_VT_STRING) {
        snprintf(buf, sizeof(buf), "STR:'%s'", v->d.s.str);
    } else if (v->vt == JSI_VT_VARIABLE) {
        snprintf(buf, sizeof(buf), "VAR:%p", v->d.lval);
    } else if (v->vt == JSI_VT_NULL) {
        snprintf(buf, sizeof(buf), "NULL");
    } else if (v->vt == JSI_VT_OBJECT) {
        snprintf(buf, sizeof(buf), "OBJ:%p", v->d.obj);
    } else if (v->vt == JSI_VT_UNDEF) {
        snprintf(buf, sizeof(buf), "UNDEFINED");
    }
    return buf;
}

Project: jsish

static void jsi_wsDumpHeaders(jsi_wsCmdObj *cmdPtr, jsi_wsPss *pss, const char *name, Jsi_Value **ret)
{
    Jsi_Interp *interp = cmdPtr->interp;
    Jsi_Obj *nobj;
    Jsi_Value *nv;
    if (pss->hdrNum<=0)
        return;
    if (!name) {
        nobj = Jsi_ObjNewType(interp, JSI_OT_ARRAY);
        Jsi_ValueMakeObject(interp, ret, nobj);
        int nsiz = Jsi_ObjArraySizer(interp, nobj, pss->hdrNum);
        if (nsiz < pss->hdrNum) {
            printf("header failed, %d != %d", nsiz, pss->hdrNum);
            return;
        }
    }
    Jsi_DString dStr = {}, vStr = {};
    int i;
    const char *nam, *val, *cp = Jsi_DSValue(&pss->dHdrs);
    for (i=0; i<pss->hdrNum; i+=2) {
        int sz = pss->hdrSz[i], sz2 = pss->hdrSz[i+1];
        Jsi_DSSetLength(&dStr, 0);
        Jsi_DSSetLength(&vStr, 0);
        nam = Jsi_DSAppendLen(&dStr, cp, sz);
        cp += 1 + sz;
        val = Jsi_DSAppendLen(&vStr, cp, sz2);
        if (name) {
            if (!Jsi_Strcmp(nam, name)) {
                Jsi_ValueMakeStringDup(interp, ret, val);
                break;
            }
        } else {
            nv = Jsi_ValueNewStringDup(interp, val);
            Jsi_ObjArraySet(interp, nobj, Jsi_ValueNewStringDup(interp, nam), i);
            Jsi_ObjArraySet(interp, nobj, nv, i+1);
        }
        cp += (1 + sz2);
    }
    Jsi_DSFree(&dStr);
    Jsi_DSFree(&vStr);
}

Project: jsish

static Jsi_RC mdbEvalPrep(MyDbEvalContext *p) {
    MysqlPrep *prep = p->prep;
    Jsi_Interp *interp = p->jdb->interp;
    Jsi_RC rc = JSI_OK;
    if( p->prep==0 ) {
        rc = mdbPrepareAndBind(p); //p->jdb, p->zSql, &p->zSql, &p->prep);
        if( rc!=JSI_OK )
            return rc;
        prep = p->prep;
        if (p->jdb->optPtr->prefetch) {
            my_bool aBool = 1;
            mysql_stmt_attr_set(prep->myStmt, STMT_ATTR_UPDATE_MAX_LENGTH, &aBool);
        }
        if (mysql_stmt_execute(prep->myStmt)) {
            Jsi_Interp *interp = p->jdb->interp;
            rc = Jsi_LogError("execute failed: %s", mysql_error(p->jdb->db));
        }
        if (p->jdb->optPtr->prefetch && mysql_stmt_store_result(prep->myStmt)) {
            Jsi_LogWarn("prefetch failed, disabling: %s", mysql_error(p->jdb->db));
            p->jdb->optPtr->prefetch = 0;
        }
        MYSQL_RES *res = mysql_stmt_result_metadata(prep->myStmt);
        MYSQL_FIELD *field;
        if (res) {
            // Setup field mappings to/from Jsi.
            prep->fieldResult = (SqlFieldResults*)Jsi_Calloc(res->field_count, sizeof(*prep->fieldResult));
            prep->colNames = (char**)Jsi_Calloc(res->field_count, sizeof(char*));
            prep->colTypes = (Jsi_OptionId*)Jsi_Calloc(res->field_count, sizeof(int));
            int iCnt = 0;
            while((field = mysql_fetch_field(res)))
            {
                assert(iCnt<prep->numCol);
                SqlFieldResults *fres = prep->fieldResult+iCnt;
                MYSQL_BIND *bindResult = prep->bindResult+iCnt;
                
                bindResult->buffer = &fres->buffer.vchar;
                fres->field = field;
                prep->colNames[iCnt] = field->name;
                switch (field->type) {
                    case MYSQL_TYPE_TINY:
                    case MYSQL_TYPE_BIT:
                        if (field->length == 1) {
                            fres->jsiTypeMap = JSI_OPTION_BOOL;
                            fres->mapType = MYSQL_TYPE_DOUBLE;
                            fres->vsize = 1;
                            break;
                            
                        }
                    case MYSQL_TYPE_SHORT:
                    case MYSQL_TYPE_LONG:
                    case MYSQL_TYPE_DECIMAL:
                        fres->jsiTypeMap = JSI_OPTION_INT64;
                        fres->mapType = MYSQL_TYPE_LONG;
                        fres->vsize = sizeof(long long);
                        break;
                    case MYSQL_TYPE_LONGLONG:
                        fres->jsiTypeMap = JSI_OPTION_INT64;
                        fres->mapType = MYSQL_TYPE_LONGLONG;
                        fres->vsize = sizeof(long long);
                        break;
                    
                    case MYSQL_TYPE_FLOAT:
                    case MYSQL_TYPE_DOUBLE:
                        fres->jsiTypeMap = JSI_OPTION_DOUBLE;
                        fres->mapType = MYSQL_TYPE_DOUBLE;
                        fres->vsize = sizeof(double);
                        break;
                    case MYSQL_TYPE_TIME:
                    case MYSQL_TYPE_DATE:
                    case MYSQL_TYPE_DATETIME:
                    case MYSQL_TYPE_TIMESTAMP:
                        fres->jsiTypeMap = JSI_OPTION_TIME_D; //TODO: time
                        fres->mapType = MYSQL_TYPE_DATETIME;
                        fres->vsize = sizeof(MYSQL_TIME);
                        break;
                    case MYSQL_TYPE_STRING:
                    default:
                        if (IS_NUM(field->type)) {
                            fres->mapType = MYSQL_TYPE_LONGLONG;
                            fres->jsiTypeMap = JSI_OPTION_DOUBLE;
                            fres->vsize = sizeof(double);
                        } else {
                            fres->jsiTypeMap = JSI_OPTION_STRING;
                            fres->mapType = MYSQL_TYPE_STRING;
                            if (p->jdb->optPtr->prefetch)
                                fres->vsize = field->max_length;
                            else
                                fres->vsize = p->jdb->optPtr->maxString;
                            if (fres->vsize <= 0)
                                fres->vsize = JSI_BUFSIZ;
                            bindResult->buffer = fres->buffer.vstring = (char*)Jsi_Malloc(fres->vsize);
                            fres->buffer.vstring[0] = 0;
                        }
                        break;
                }
                prep->colTypes[iCnt] = fres->jsiTypeMap;
                bindResult->buffer_type = fres->mapType;
                bindResult->buffer_length = fres->vsize;
                bindResult->length = &fres->len;
                bindResult->is_null = &fres->isnull;
                iCnt++;
            }
        }
        else return JSI_BREAK;
        if (mysql_stmt_bind_result(prep->myStmt, prep->bindResult)) {
            fprintf(stderr, "mysql_stmt_bind_Result(), failed. Error:%s\n", mysql_stmt_error(prep->myStmt));
            return JSI_ERROR;
        }
        mysql_free_result(res);
    }
    return rc;
}

Project: jsish

void Jsi_InterpFreeData(Jsi_Interp *interp, const char *key)
{
    Jsi_HashEntry *hPtr;
    hPtr = Jsi_HashEntryFind(interp->assocTbl, key);
    if (!hPtr)
        return;
    Jsi_HashEntryDelete(hPtr);
}

Project: jsish

static int jsi_FSEofProc(Jsi_Channel chan) { return feof(_JSI_GETFP(chan,1));}

Project: jsish

static Jsi_Value** jsi_ValuesAlloc(Jsi_Interp *interp, int cnt, Jsi_Value**old, int oldsz) {
    int i;
    Jsi_Value **v = (Jsi_Value **)Jsi_Realloc(old, cnt* sizeof(Jsi_Value*));
    for (i=oldsz; i<cnt; i++)
        v[i] = NULL;
    return v;
}

Project: jsish

Jsi_MapEntry* Jsi_MapEntryNew (Jsi_Map *mapPtr, const void *key, bool *isNew){
    SIGASSERT(mapPtr, MAP);
    switch (mapPtr->typ) {
        case JSI_MAP_HASH: return (Jsi_MapEntry*)Jsi_HashEntryNew(mapPtr->v.hash, key, isNew);
        case JSI_MAP_TREE: return (Jsi_MapEntry*)Jsi_TreeEntryNew(mapPtr->v.tree, key, isNew);
        case JSI_MAP_LIST: {
            Jsi_ListEntry *lptr = Jsi_ListEntryNew(mapPtr->v.list, NULL, (key?mapPtr->v.list->head:NULL));
            if (isNew) *isNew = 1;
            return (Jsi_MapEntry*)lptr;
        }
        break;
        case JSI_MAP_NONE: break;
    }
    return NULL;
}

Project: jsish

void jsi_VALCHK(Jsi_Value *val) {
    SIGASSERTV(val,VALUE);
    assert(val->vt <= JSI_VT__MAX);
    if (val->vt == JSI_VT_OBJECT)
        OBJCHK(val->d.obj);
}

Project: jsish

static Jsi_RC jsi_wsEvalSSI(Jsi_Interp *interp, jsi_wsCmdObj *cmdPtr, Jsi_Value *fn, Jsi_DString *dStr,
    int lvl, jsi_wsPss *pss) {
    int flen, rlen;
    char fbuf[PATH_MAX];
    char *fs, *fname = Jsi_ValueString(interp, fn, &flen), *fend = fname;
    if (lvl>10 || !fname || !pss)
        return JSI_ERROR;
    Jsi_Value *fval;
    Jsi_RC rc = JSI_OK;
    Jsi_DString tStr = {}, lStr = {};
    const char *cs = NULL;
    char *root = Jsi_ValueString(interp, cmdPtr->rootdir, &rlen);
    if (!Jsi_Strncmp(root, fname, rlen))
        fname = fname + rlen;
    fs = Jsi_Strrchr(fname, '/');
    if (fs) {
        flen = fs-fname;
        fend = fs+1;
    }
    if (lvl>0) {
        rc = jsi_wsFileRead(interp, fn, &tStr, cmdPtr, pss);
        cs = Jsi_DSValue(&tStr);
    } else {
        snprintf(fbuf, sizeof(fbuf), "%s/%.*s/%s", root, flen, fname, cmdPtr->includeFile);
        fval = Jsi_ValueNewStringConst(interp, fbuf, -1);
        Jsi_IncrRefCount(interp, fval);
        rc = jsi_wsFileRead(interp, fval, &tStr, cmdPtr, pss);
        if (rc == JSI_OK)
            cs = Jsi_DSValue(&tStr);
        Jsi_DecrRefCount(interp, fval);
    }
    
    char *cp, *sp, *se, pref[] = "<!--#", suffix[] = "-->", *msg = NULL;
    struct {
        int inif, inelse, matched, elide;
    } II[11] = {};
    const int maxNest = 10;
    int ii = 0;
    int plen = 5, elen, llen;

    while (rc == JSI_OK && cs) {
        char *ext = NULL, *sfname = fname;
        int sflen = flen;
        sp = Jsi_Strstr(cs, pref);
        if (!sp || !(se=Jsi_Strstr(sp+plen, suffix))) {
            Jsi_DSAppend(dStr, cs, NULL);
            break;
        }
        sp += plen-1;
        llen = se-sp;
        Jsi_DSSetLength(&lStr, 0);
        cp = Jsi_DSAppendLen(&lStr, sp, llen);
        if (Jsi_Strchr(cp, '\n')) { rc = Jsi_LogError("unexpected newline in directive \"%.10s\"", cp); break; }
        if (!II[ii].elide)
            Jsi_DSAppendLen(dStr, cs, sp-cs-4);
        
        if (!Jsi_Strncmp(cp, "#include file=\"", 12) || !Jsi_Strncmp(cp, "#include virtual=\"", 15)) {
            if (cp[llen-1] != '"' || cp[llen-2] == '=') { msg = "missing end quote in"; break; }
            if (!II[ii].elide) {
                Jsi_DSSetLength(&lStr, llen-1);
                int isvirt = (cp[9]=='v');
                cp += (isvirt ? 18 : 15);
                if (cp[0] == '$' && lvl == 0) { // substitute file suffix
                    char sfx[20] = {};
                    uint i;
                    for (i=0; i<sizeof(sfx); i++) {
                        if ((sfx[i] = cp[i+1]) == '"' || !sfx[i]) {
                            sfx[i] = 0;
                            break;
                        }
                    }
                    snprintf(fbuf, sizeof(fbuf), "%s/%.*s/%s/%s%s", root, flen, fname, sfx, fend, sfx);
                } else {
                    snprintf(fbuf, sizeof(fbuf), "%s/%.*s/%s", root, sflen, sfname, cp);
                    ext = Jsi_Strrchr(fbuf, '.');
                }
                fval = Jsi_ValueNewStringConst(interp, fbuf, -1);
                Jsi_IncrRefCount(interp, fval);
                if (!ext || ext[0] != '.' || !jsi_wsIsSSIExt(interp, cmdPtr, pss, ext+1))
                    rc = jsi_wsFileRead(interp, fval, dStr, cmdPtr, pss);
                else
                    rc = jsi_wsEvalSSI(interp, cmdPtr, fval, dStr, lvl+1, pss);
                if (cmdPtr->noWarn)
                    rc = JSI_OK;
                Jsi_DecrRefCount(interp, fval);
            }
        } else if (!Jsi_Strncmp(cp, "#echo \"${", 9)) {
            if (cp[llen-1] != '"' || cp[llen-2] != '}') { msg = "missing end quote"; break; }
            Jsi_DSSetLength(&lStr, llen-2);
            cp += 9;
            llen -= 9;
            if (!Jsi_Strcmp(cp, "#")) {
                if (!pss->key[0])
                    snprintf(pss->key, sizeof(pss->key), "%d%p%d", pss->wid, pss, (int)cmdPtr->startTime);
                Jsi_DSPrintf(dStr, "'%s'", pss->key);
            } else {
                Jsi_Value *val = NULL;
                if (!cmdPtr->udata) {
                    val = Jsi_ValueObjLookup(interp, cmdPtr->udata, cp, 0);
                    if (!val) { msg = "udata lookup failure"; break; }
                    cp = Jsi_ValueString(interp, val, NULL);
                    Jsi_DSPrintf(dStr, "'%s'", cp);
                }
            }

        } else if (!Jsi_Strncmp(cp, "#if expr=\"", 10) || !Jsi_Strncmp(cp, "#elif expr=\"", 12)) {
            if (llen<11 || cp[llen-1] != '"' || cp[llen-2] == '=') { msg = "missing end quote"; break; }
            Jsi_DSSetLength(&lStr, llen-1);
            bool iselif = (cp[1]=='e');
            cp += (iselif?12:10);
            if (!iselif) {
                if (II[ii].inif) {
                    if ((ii+1)>=maxNest) { msg = "nested \"#if\" too deep"; break; }
                    ii++;
                    II[ii] = II[maxNest];
                    II[ii].elide = II[ii-1].elide;
                }
                II[ii].inif = 1;
            } else {
                if (!II[ii].inif) { msg = "unexpected \"#elif\""; break; }
            }
            elen = Jsi_Strlen(cp);
            if (elen<4|| cp[0] != '$' || cp[1] != '{' || cp[elen-1] != '}') {
                msg = "expr must be of form ${X}"; break;
            }
            Jsi_DSSetLength(&lStr, llen-2);
            cp += 2;
            // Extract modifiers before bool var name to lookup.
            bool warn = 0, req = 0, nifval = 0, not = 0, isq=0, isu=0, qfirst=0;
            while (*cp &&  !isalpha(*cp)) {
                bool fail = 0;
                switch (*cp) {
                    case '~': qfirst = 1; break;
                    case ':': isu = 1; break;
                    case '?': isq = 1; break;
                    case '@': warn = !II[ii].matched; break;
                    case '*': req = !II[ii].matched; break;
                    case '!': not = 1; break;
                    default: fail=1; break;
                }
                if (fail) { msg = "modifier must be one of: !:=?@*"; break; }
                cp++;
            }
            Jsi_Value *val = NULL;
            if (!val && qfirst && pss->queryObj)
                val = Jsi_ValueObjLookup(interp, pss->queryObj, cp, 0);
            if (!val && !isq && cmdPtr->udata)
                val = Jsi_ValueObjLookup(interp, cmdPtr->udata, cp, 0);
            if (!val && !qfirst && !isu && pss->queryObj)
                val = Jsi_ValueObjLookup(interp, pss->queryObj, cp, 0);
            if (!val) {
                if (req) { msg = "symbol not found"; break; }
                if (warn) Jsi_LogWarn("symbol \"%s\" not found: %s", cp, fbuf);
            } else if (Jsi_ValueGetBoolean(interp, val, &nifval) != JSI_OK) {
                const char *valStr = NULL;
                if (val) valStr = Jsi_ValueString(interp, val, NULL);
                if (!valStr || Jsi_GetBool(interp, valStr, &nifval) != JSI_OK) {
                    if (!warn) { msg = "symbol not a boolean"; break; }
                    Jsi_LogWarn("symbol \"%s\" should be a boolean: %s", cp, fbuf);
                }
            }
            if (not) nifval = !nifval;
            if (!iselif) {
                if (nifval)
                    II[ii].matched = 1;
                else
                    II[ii].elide = 1;
            } else {
                if (II[ii].matched || !nifval)
                    II[ii].elide = 1;
                else if (nifval) {
                    II[ii].matched = 1;
                    II[ii].elide = (ii?II[ii-1].elide:0);
                }
            }
        } else if (!Jsi_Strncmp(cp, "#else", 5)) {
            if (!II[ii].inif || II[ii].inelse) { msg = "unexpected \"#else\""; break; }
            II[ii].inelse = 1;
            II[ii].elide = (ii&&II[ii-1].elide?1:II[ii].matched);
        } else if (!Jsi_Strncmp(cp, "#endif", 6)) {
            if (!II[ii].inif) { msg = "unexpected \"#endif\"";  break; }
            II[ii].inelse = II[ii].inif = II[ii].elide = II[ii].matched = 0;
            if (ii>0)
                ii--;
        } else {
            msg = "expected directive #include/#if/#elif/#else/#endif";
            break;
        }
        cs = se + 3;
        if (*cs == '\n')
            cs++;
    }
    if (rc == JSI_OK && II[ii].inif && !msg) {
         msg = "missing \"#endif\"";
         sp = "";
    }
    if (msg) {
        while (*fname=='/') fname++;
        rc = Jsi_LogError("SHTML Error in \"%s\": %s: at \"%.40s\" ", fname, msg, sp);
    }
    Jsi_DSFree(&tStr);
    Jsi_DSFree(&lStr);
    return rc;

}

Project: jsish

Jsi_RC jsi_JsPreprocessLineCB(Jsi_Interp* interp, char *buf, size_t bsiz, uint ilen, int jOpts[4], int lineNo) {
    const char *jpp = interp->jsppChars;
    if (!jpp[0] || !jpp[1])
        return JSI_OK;
    if (buf[0] && jpp[0] == buf[0] && ilen>2 && buf[ilen-2]==jpp[1]) {
        Jsi_DString dStr = {};
        buf[ilen-2] = 0; // Remove last char and newline.
        Jsi_Value *inStr = Jsi_ValueNewStringDup(interp, buf+1);
        Jsi_IncrRefCount(interp, inStr);
        Jsi_RC rc = Jsi_FunctionInvokeString(interp, interp->jsppCallback, inStr, &dStr);
        if (Jsi_InterpGone(interp))
            return JSI_ERROR;
        if (rc != JSI_OK) {
            Jsi_DSFree(&dStr);
            Jsi_DecrRefCount(interp, inStr);
            return JSI_ERROR;
        }
        Jsi_DecrRefCount(interp, inStr);
        Jsi_DSAppendLen(&dStr, "\n", 1);
        Jsi_Strncpy(buf, Jsi_DSValue(&dStr), bsiz);
        buf[bsiz-1] = 0;
        Jsi_DSFree(&dStr);
    }
    return JSI_OK;
}

Project: jsish

Jsi_RC Jsi_MapKeysDump(Jsi_Interp *interp, Jsi_Map *mapPtr, Jsi_Value **ret, int flags){
    SIGASSERT(mapPtr, MAP);
    switch (mapPtr->typ) {
        case JSI_MAP_HASH: return Jsi_HashKeysDump(interp, mapPtr->v.hash, ret, flags);
        case JSI_MAP_TREE: return Jsi_TreeKeysDump(interp, mapPtr->v.tree, ret, flags);
        case JSI_MAP_LIST: break; // TODO: dump numbers?
        case JSI_MAP_NONE: break;
    }
    return JSI_ERROR;
}

Project: jsish

static int jsi_cmpstringp(const void *p1, const void *p2)
{
   return Jsi_Strcmp(* (char * const *) p1, * (char * const *) p2);
}

Project: jsish

Jsi_RC Jsi_SqlObjBinds(Jsi_Interp* interp, Jsi_DString* zStr, const char *varName, bool addTypes, bool addDefaults, bool nullDefaults) {
    Jsi_Value *v, *vnPtr = Jsi_VarLookup(interp, varName);
    if (!vnPtr || !Jsi_ValueIsObjType(interp, vnPtr, JSI_OT_OBJECT))
        return Jsi_LogError("varName must be an Object: %s", varName);
    char *cp, *zSql = Jsi_DSValue(zStr);
    int create = !Jsi_Strncasecmp(zSql,"create",6);
    int insert = !Jsi_Strncasecmp(zSql,"insert",6);
    if (!create && !insert) return JSI_OK;
    const char *cPtr = Jsi_Strstr(zSql, " %s");
    if (!cPtr) cPtr = Jsi_Strstr(zSql, "\t%s");
    if (!cPtr)
        return Jsi_LogError("Object varName must contain a ' %%s': %s", varName);
    Jsi_DString sStr = {}, vStr = {}, jStr = {};
    Jsi_DSAppendLen(&sStr, zSql, cPtr?(cPtr-zSql):-1);
    Jsi_DSAppend(&sStr, " (", NULL);

    Jsi_IterObj *io = Jsi_IterObjNew(interp, NULL);
    Jsi_IterGetKeys(interp, vnPtr, io, 0);
    uint i;
    const char *pre = "", *kstr;
    if (!create)
        Jsi_DSAppend(&vStr, " VALUES(", NULL);
    for (i=0; i<io->count; i++) {
        kstr = io->keys[i];
        const char *qs = "", *qe = "";
        if (!Jsi_StrIsAlnum(kstr) || Jsi_IsReserved(interp, kstr, 1)) {
            qe = qs = "'";
        }
        Jsi_DSAppend(&sStr, pre, qs, kstr, qe, NULL);
        if (create) {
            const char *typ = NULL, *dflt=(nullDefaults?"NULL":NULL);
            if (addTypes && ((v = Jsi_ValueObjLookup(interp, vnPtr, kstr, 1)))) {
                if (Jsi_ValueIsBoolean(interp, v)) {
                    typ = "BOOLEAN";
                    if (!nullDefaults && addDefaults) {
                        bool bv = 0;
                        Jsi_ValueGetBoolean(interp, v, &bv);
                        dflt = (bv?"1":"0");
                    }
                } else if (Jsi_ValueIsNumber(interp, v)) {
                    typ = "NUMERIC";
                    if (!Jsi_Strcmp(kstr,"rowid"))
                        typ = "INTEGER PRIMARY KEY";
                    else if (!nullDefaults && addDefaults) {
                        Jsi_Number nv = 0;
                        Jsi_DSFree(&jStr);
                        Jsi_ValueGetNumber(interp, v, &nv);
                        dflt = Jsi_DSPrintf(&jStr, "%" JSI_NUMGFMT, nv);
                    }
                } else if (Jsi_ValueIsArray(interp, v) || Jsi_ValueIsObjType(interp, v, JSI_OT_OBJECT)) {
                    typ = "CHARJSON";
                    if (!nullDefaults && addDefaults) {
                        Jsi_DSFree(&jStr);
                        Jsi_DSAppend(&jStr, "'", NULL);
                        Jsi_ValueGetDString(interp, v, &jStr, JSI_OUTPUT_JSON|JSI_JSON_STRICT);
                        Jsi_DSAppend(&jStr, "'", NULL);
                        dflt = Jsi_DSValue(&jStr);
                    }
                } else {
                    typ = "TEXT";
                    if (!nullDefaults && addDefaults) {
                        if ((cp=Jsi_ValueString(interp, v, NULL))) {
                            Jsi_DSFree(&jStr);
                            dflt = Jsi_DSAppend(&jStr, "'", cp, "'", NULL);
                        } else
                        dflt = "NULL";
                    }
                }
            }
            if (typ)
                Jsi_DSAppend(&sStr, " ", typ, (dflt?" DEFAULT ":""), dflt, NULL);
        } else {
            Jsi_DSAppend(&vStr, pre, "$", varName, "(", kstr, ")", NULL);
        }
        pre = ",";
    }
    if (!create)
        Jsi_DSAppend(&vStr, ")", NULL);
    Jsi_IterObjFree(io);
    Jsi_DSAppend(&sStr, ") ", Jsi_DSValue(&vStr), cPtr+3, NULL);
    Jsi_DSFree(zStr);
    Jsi_DSAppend(zStr, Jsi_DSValue(&sStr), NULL);
    Jsi_DSFree(&sStr); Jsi_DSFree(&vStr); Jsi_DSFree(&jStr);
    return JSI_OK;
}

Project: jsish

static void dbOutputHtmlString(QueryOpts *p, const char *z, Jsi_DString *dStr)
{
    while( *z ) {
        switch (*z) {
        case '<':
            Jsi_DSAppend(dStr, "&lt;", NULL);
            break;
        case '>':
            Jsi_DSAppend(dStr, "&gt;", NULL);
            break;
        case '&':
            Jsi_DSAppend(dStr, "&amp;", NULL);
            break;
        case '\"':
            Jsi_DSAppend(dStr, "&quot;", NULL);
            break;
        case '\'':
            Jsi_DSAppend(dStr, "&#39;", NULL);
            break;
        default:
            Jsi_DSAppendLen(dStr, z, 1);
            break;
        }
        z++;
    }
}

Project: jsish

static Jsi_RC SqliteCompleteCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                             Jsi_Value **ret, Jsi_Func *funcPtr)
{
    Jsi_Db *jdb;
    if (!(jdb = dbGetDbHandle(interp, _this, funcPtr))) return JSI_ERROR;
    Jsi_Value *s = Jsi_ValueArrayIndex(interp, args, 0);
    const char *str =  Jsi_ValueString(interp, s, NULL);
    int isComplete = 0;
    if (str)
        isComplete = sqlite3_complete( str );
    Jsi_ValueMakeBool(interp, ret, isComplete);
    return JSI_OK;
}

Project: jsish

int Jsi_Readlink(Jsi_Interp *interp, Jsi_Value* path, char *ret, int len) {
#ifdef __WIN32
    return -1;
#else
    void *data;
    Jsi_Filesystem *fsPtr = Jsi_FilesystemForPath(interp, path, &data);
    if (fsPtr == NULL || !fsPtr->readlinkProc) return -1;
    return fsPtr->readlinkProc(interp, path, ret, len);
#endif
}

Project: jsish

Jsi_RC jsi_JsPreprocessLine(Jsi_Interp* interp, char *buf, size_t bsiz, uint ilen, int jOpts[4], int lineNo) {
    if (buf[0]==';' && buf[1] && buf[2]) {
        // Wrap ";XXX;" in a puts("XXX ==> ", XXX)
        if (!jOpts[0]) {
            if (!Jsi_Strcmp(buf, "=!EXPECTSTART!=\n") || !Jsi_Strcmp(buf, "=!INPUTSTART!=\n") ) {
                return JSI_OK;
            }
        } else {
            if (!Jsi_Strcmp(buf, "=!EXPECTEND!=\n") || !Jsi_Strcmp(buf, "=!INPUTEND!=\n")) {
                jOpts[0] = 0;
                return JSI_OK;
            }
        }
        if (buf[ilen-1]=='\n' && buf[ilen-2]==';' && (2*ilen+12)<bsiz) {
            if (Jsi_Strchr(buf, '`')) {
                return Jsi_LogError("back-tick is illegal in unitTest on line %d: %s", lineNo, buf);
            }
            char ubuf[bsiz], *ucp = ubuf;
            buf[ilen-=2] = 0;
            Jsi_Strcpy(ubuf, buf+1);
            while (*ucp && isspace(*ucp)) ucp++;
            if (ilen>2 && ucp[0]=='\'' && ubuf[ilen-2]=='\'')
                snprintf(buf, bsiz, "puts(`%s`);\n", ucp); //output a 'Comment'
                
            else if (interp->debugOpts.testFmtCallback) {
                Jsi_DString kStr={};
                Jsi_Value *vrc = Jsi_ValueNew1(interp);
                Jsi_DSPrintf(&kStr, "[\"%s\", %d ]", ucp, lineNo);
                Jsi_RC rcs = Jsi_FunctionInvokeJSON(interp->parent, interp->debugOpts.testFmtCallback, Jsi_DSValue(&kStr), &vrc);
                if (rcs == JSI_OK) {
                    const char *cps = Jsi_ValueString(interp, vrc, NULL);
                    if (!cps)
                        rcs = JSI_ERROR;
                    else
                        snprintf(buf, bsiz, "%s", cps);
                }
                Jsi_DecrRefCount(interp, vrc);
                Jsi_DSFree(&kStr);
                if (rcs != JSI_OK)
                    return Jsi_LogError("failure in debugOpts.testFmtCallback");
            } else if (ilen>3 && ubuf[0]=='/' && ubuf[0]=='/') {
                char *ecp = ubuf+2;
                while (*ecp && isspace(*ecp)) ecp++;
                snprintf(buf, bsiz, "printf(`%%s ==>`, \"%s\"); try { %s; puts('\\nFAIL!\\n'); } "
                    "catch(err) { puts('\\nPASS!: err =',err); }\n", ecp, ecp);
            } else {
                snprintf(buf, bsiz, "printf(`%%s ==> `,`%s`),puts(%s);\n", ucp, ucp);
            }
        }
    }
    return JSI_OK;
}

Project: jsish

static Jsi_OpCodes *code_equal() { JSI_NEW_CODES(0,OP_EQUAL, 0); }

Project: jsish

jsi_wsHeader(jsi_wsPss *pss, const char *name, int *lenPtr)
{
    int i, nlen = Jsi_Strlen(name);
    const char *ret = NULL, *cp = Jsi_DSValue(&pss->dHdrs);
    for (i=0; i<pss->hdrNum; i+=2) {
        int sz = pss->hdrSz[i];
        int mat = (!Jsi_Strncasecmp(cp, name, nlen) && cp[nlen]=='=');
        cp += 1 + sz;
        if (mat) {
            ret = cp;
            if (lenPtr)
                *lenPtr = pss->hdrSz[i+1];
            break;
        }
        cp += (1 + pss->hdrSz[i+1]);
    }
    return ret;
}

Project: jsish

const char* jsi_FuncGetCode(Jsi_Interp *interp, Jsi_Func *func, int *lenPtr) {
    if (interp->subOpts.noFuncString || !func->bodyStr)
        return NULL;
    const char *cp, *cp2;
    if (func->startPos == -1) {
        cp = func->bodyStr;
        int cplin = func->bodyline.last_line-1;
        while (*cp && cplin>0) {
            if (*cp=='\n' && --cplin<=0)
                break;
            cp++;
        }
        while (*cp && isspace(*cp))
            cp++;
        func->startPos = (*cp?(cp - func->bodyStr):-2);
    }
    if (func->startPos >= 0) {
        int len = func->endPos - func->startPos;
        cp = func->bodyStr + func->startPos;
        while (len>0 && (isspace(cp[len-1]) || cp[len-1]==';')) len--;
        if (*cp != 'f' && Jsi_Strncmp(cp, "function", 8) && (cp2=Jsi_Strstr(cp, "function"))) {
            len -= (cp2-cp);
            cp = cp2;
        }
        *lenPtr = len;
        return cp;
    }
    return NULL;
}

Project: jsish

static Jsi_RC mdbIsNumArray(Jsi_Interp *interp, Jsi_Value *value)
{
    if (!Jsi_ValueIsArray(interp, value)) 
        return Jsi_LogError("expected array of numbers");
    int i, argc = Jsi_ValueGetLength(interp, value);
    for (i=0; i<argc; i++) {
        Jsi_Value *v = Jsi_ValueArrayIndex(interp, value, i);
        if (!Jsi_ValueIsNumber(interp, v)) 
            return Jsi_LogError("expected array of numbers");
    }
    return JSI_OK;
}

Project: jsish

void Jsi_IterGetKeys(Jsi_Interp *interp, Jsi_Value *target, Jsi_IterObj *iterobj, int depth)
{
    if (!target) return;
    if (target->vt != JSI_VT_OBJECT) {
        if (interp->strict)
            Jsi_LogWarn("operand is not a object");
        return;
    }
    Jsi_Obj *to = target->d.obj;
    Jsi_CmdSpec *cs = NULL;
    if (to->ot == JSI_OT_USEROBJ) {
        Jsi_UserObj *uobj = to->d.uobj;
        cs = uobj->reg->spec;
    } else if (to->ot == JSI_OT_FUNCTION) {
        Jsi_FuncObj *fobj = to->d.fobj;
        if (fobj->func->type == FC_BUILDIN)
            cs = fobj->func->cmdSpec;
    }
    if (cs) {
        while (cs->name) {
            IterObjInsertKey(iterobj, cs->name);
            cs++;
        }
        return;
    }
    iterobj->depth = depth;
    Jsi_TreeWalk(target->d.obj->tree, IterGetKeysCallback, iterobj, 0);
    if (target->d.obj->__proto__ && target != target->d.obj->__proto__)
        Jsi_IterGetKeys(interp, target->d.obj->__proto__, iterobj, depth+1);
    iterobj->depth = depth;
}

Project: jsish

static void TreeTest(Jsi_Interp* interp) {
    Jsi_Tree *st, *wt, *mt;
    Jsi_TreeEntry *hPtr, *hPtr2;
    bool isNew, i;
    Jsi_TreeSearch srch;
    struct tdata {
        int n;
        int m;
    } t1, t2;
    char nbuf[JSI_MAX_NUMBER_STRING];
    
    wt = Jsi_TreeNew(interp, JSI_KEYS_ONEWORD, NULL);
    mt = Jsi_TreeNew(interp, sizeof(struct tdata), NULL);

    Jsi_TreeSet(wt, wt,(void*)0x88);
    Jsi_TreeSet(wt, mt,(void*)0x99);
    printf("WT: %p\n", Jsi_TreeGet(wt, mt));
    printf("WT2: %p\n", Jsi_TreeGet(wt, wt));
    Jsi_TreeDelete(wt);

    t1.n = 0; t1.m = 1;
    t2.n = 1; t2.m = 2;
    Jsi_TreeSet(mt, &t1,(void*)0x88);
    Jsi_TreeSet(mt, &t2,(void*)0x99);
    Jsi_TreeSet(mt, &t2,(void*)0x98);
    printf("CT: %p\n", Jsi_TreeGet(mt, &t1));
    printf("CT2: %p\n", Jsi_TreeGet(mt, &t2));
    Jsi_TreeDelete(mt);

    st = Jsi_TreeNew(interp, JSI_KEYS_STRING, NULL);
    hPtr = Jsi_TreeEntryNew(st, "bob", &isNew);
    Jsi_TreeValueSet(hPtr, (void*)99);
    Jsi_TreeSet(st, "zoe",(void*)77);
    hPtr2 = Jsi_TreeSet(st, "ted",(void*)55);
    Jsi_TreeSet(st, "philip",(void*)66);
    Jsi_TreeSet(st, "alice",(void*)77);
    puts("SRCH");
    for (hPtr=Jsi_TreeSearchFirst(st,&srch,  JSI_TREE_ORDER_IN, NULL); hPtr; hPtr=Jsi_TreeSearchNext(&srch))
        mycall(st, hPtr, NULL);
    Jsi_TreeSearchDone(&srch);
    puts("IN");
    Jsi_TreeWalk(st, mycall, NULL, JSI_TREE_ORDER_IN);
    puts("PRE");
    Jsi_TreeWalk(st, mycall, NULL, JSI_TREE_ORDER_PRE);
    puts("POST");
    Jsi_TreeWalk(st, mycall, NULL, JSI_TREE_ORDER_POST);
    puts("LEVEL");
    Jsi_TreeWalk(st, mycall, NULL, JSI_TREE_ORDER_LEVEL);
    Jsi_TreeEntryDelete(hPtr2);
    puts("INDEL");
    Jsi_TreeWalk(st, mycall, NULL, 0);

    for (i=0; i<1000; i++) {
        snprintf(nbuf, sizeof(nbuf), "name%d", i);
        Jsi_TreeSet(st, nbuf,(void*)i);
    }
    Jsi_TreeWalk(st, mycall, NULL, 0);
    for (i=0; i<1000; i++) {
        Jsi_TreeEntryDelete(st->root);
    }
    puts("OK");
    Jsi_TreeWalk(st, mycall, NULL, 0);
    Jsi_TreeDelete(st);

}

Project: jsish

static void code_reserved_replace(Jsi_OpCodes *ops, int step_len, int break_only,
                           const char *desire_label, int topop)
{
    int i;
    for (i = 0; i < ops->code_len; ++i) {
        if (ops->codes[i].op != OP_RESERVED) continue;
        jsi_ReservedInfo *ri = (jsi_ReservedInfo *)ops->codes[i].data;

        if (ri->label) {
            if (!desire_label || Jsi_Strcmp(ri->label, desire_label) != 0) {
                ri->topop += topop;
                continue;
            }
        }
        
        if (ri->type == RES_CONTINUE) {
            if (break_only) {
                ri->topop += topop;
                continue;
            } else {
                int topop = ri->topop;
                Jsi_Free(ri);       /* kill reserved Warn, replace with other opcode */
 /*               if (ops->codes[i].data && ops->codes[i].alloc) //TODO: memory leak?
                    Jsi_Free(ops->codes[i].data);*/
                if (topop) {
                    ops->codes[i].data = jpinfo_new(ops->code_len - i, topop);
                    ops->codes[i].op = OP_JMPPOP;
                    ops->codes[i].alloc = 1;
                } else {
                    ops->codes[i].data = (void *)(uintptr_t)(ops->code_len - i);
                    ops->codes[i].op = OP_JMP;
                    ops->codes[i].alloc = 0;
                }
            }
        } else if (ri->type == RES_BREAK) {
            int topop = ri->topop;
            Jsi_Free(ri);
/*           if (ops->codes[i].data && ops->codes[i].alloc)
                Jsi_Free(ops->codes[i].data); */
            if (topop) {
                ops->codes[i].data = jpinfo_new(step_len + ops->code_len - i, topop);
                ops->codes[i].op = OP_JMPPOP;
                ops->codes[i].alloc = 1;
            } else {
                ops->codes[i].data = (void *)(uintptr_t)(step_len + ops->code_len - i);
                ops->codes[i].op = OP_JMP;
                ops->codes[i].alloc = 0;
            }
        }
    }
}

Project: jsish

void jsi_DebugValue(Jsi_Value* v, const char *reason, uint cidx, Jsi_Interp *interp)
{
    if (jsi_memDebugBreakIdx && jsi_memDebugBreakIdx == v->VD.Idx)
        jsi_memDebugBreak();
    return;
}

Project: jsish

static void mdbFlushStmtCache( MySqlObj *jdb ) {
    Jsi_ListClear(jdb->stmtCache);
    jdb->numStmts = 0;
}

Project: jsish

Jsi_OptionCustom* Jsi_OptionCustomBuiltin(Jsi_OptionCustom* cust) {
    if ((uintptr_t)cust < (uintptr_t)(sizeof(custOpts)/sizeof(custOpts[0])))
        return custOpts[(uintptr_t)cust];
    return cust;
}

Project: jsish

static Jsi_RC jsi_InterpDebugHook(struct Jsi_Interp* interp, const char *curFile,
    int curLine, int curLevel, const char *curFunc, const char *opCode, jsi_OpCode *op, const char *emsg)
{
    // TODO: when code is run in debugger, parser.y should attribute op for case stmt to skip str compares, etc.
    int isfun=0;
    if (interp->isInCallback || curLine<=0)
        return JSI_OK;
    if (op && op->nodebug)
        return JSI_OK;
    int isbp = 0, bpId = 0, cont = interp->debugOpts.doContinue,
        stop = (interp->debugOpts.noFilter || interp->debugOpts.forceBreak);
    if (!curFunc)
        curFunc = "";

    if (interp->parent && interp->parent->sigmask) {
        interp->parent->sigmask = 0;
        opCode = "SIGINT";

    } else if (Jsi_Strcmp(opCode, "DEBUG") || !interp->parent) {

        // Avoid overhead of multiple ops on same line of code.
        int sameLine = (interp->debugOpts.lastLine == curLine && interp->debugOpts.lastLevel == curLevel
            && interp->debugOpts.lastFile == curFile);

        if (sameLine && stop==0 && (interp->debugOpts.bpLast==0
            || (interp->debugOpts.bpOpCnt+10) >= interp->opCnt)) //TODO: need better way to detect bp dups.
            goto done;

        if (!interp->debugOpts.debugCallback || !interp->parent) {
            fprintf(stderr, "FILE %s:%d (%d) %s %s\n", curFile, curLine, curLevel, curFunc, opCode);
            return JSI_OK;
        }

        // Check for breakpoints.
        if (interp->breakpointHash) {
            Jsi_HashEntry *hPtr;
            Jsi_HashSearch search;
            for (hPtr = Jsi_HashSearchFirst(interp->breakpointHash, &search);
                hPtr != NULL && stop == 0; hPtr = Jsi_HashSearchNext(&search)) {
                jsi_BreakPoint* bptr = (jsi_BreakPoint*)Jsi_HashValueGet(hPtr);
                if (bptr == NULL || bptr->enabled == 0) continue;
                if (bptr->func)
                    stop = (!Jsi_Strcmp(bptr->func, curFunc));
                else
                    stop = (bptr->line == curLine && !Jsi_Strcmp(bptr->file, curFile));
                if (stop) {
                    isbp = 1;
                    bpId = bptr->id;
                    bptr->hits++;
                    if (bptr->temp)
                        bptr->enabled = 0;
                }
            }
        }

        if (stop == 0) { // No breakpoint.
            if (cont  // Cmd is "continue"
                // Handle "next" by skipping calls into functions.
                || (interp->debugOpts.minLevel>0 && curLevel>interp->debugOpts.minLevel)
                || (isfun=(Jsi_Strcmp(opCode, "PUSHVAR")==0 && op[1].op == OP_PUSHFUN)))
            {
                if (isfun) {
                    interp->debugOpts.lastLine = curLine;
                    interp->debugOpts.lastLevel = curLevel;
                    interp->debugOpts.lastFile = curFile;
                }
done:
                return JSI_OK;
            }
        }
    }
    interp->debugOpts.bpLast = isbp;
    interp->debugOpts.bpOpCnt = interp->opCnt;
    interp->debugOpts.lastLine = curLine;
    interp->debugOpts.lastLevel = curLevel;
    interp->debugOpts.lastFile = curFile;
    interp->debugOpts.forceBreak = 0;

    Jsi_DString dStr;
    Jsi_DSInit(&dStr);
    if (emsg && Jsi_Strchr(emsg,'\"'))
        emsg = 0;
    Jsi_DSPrintf(&dStr, "[\"%s\", %d, %d, \"%s\", \"%s\", %d, \"%s\"]", curFile?curFile:"", curLine, curLevel, curFunc, opCode, bpId, emsg?emsg:"");
    interp->isInCallback = 1;
    Jsi_RC rc = JSI_ERROR;
    if (interp->debugOpts.debugCallback)
        rc = Jsi_FunctionInvokeJSON(interp->parent, interp->debugOpts.debugCallback, Jsi_DSValue(&dStr), &interp->retValue);
    interp->isInCallback = 0;
    if (interp->parent->exited == 0 && rc != JSI_OK)
        Jsi_LogError("debugger failure");
    return rc;
}

Project: jsish

static void caselist_free(jsi_CaseList *c)
{
    jsi_CaseList *a = c;
    while (a) {
        a = c->next;
        if (c->es) Jsi_Free(c->es);
        Jsi_Free(c);
        c = a;
    }
}

Project: jsish

static void set_color(Jsi_TreeEntry* n, int color) {
    if (color == _JSI_TREE_BLACK && n == NULL) return;
    n->f.bits.color = color;
}

Project: jsish

uint Jsi_ListSize(Jsi_List *list) {
    SIGASSERT(list, LIST);
    return list->numEntries;
}

Project: jsish

Jsi_RC Jsi_FSRegister(Jsi_Filesystem *fsPtr, void *data) {
    FSList *fsl = (FSList *)Jsi_Calloc(1, sizeof(*fsl));
    fsl->fsPtr = fsPtr;
    fsl->data = data;
    fsl->next = jsiFSList;
    jsiFSList = fsl;
    return JSI_OK;
}

Project: jsish

Jsi_Value* Jsi_ValueNew(Jsi_Interp *interp) {
    return ValueNew(interp);
}

Project: jsish

static Jsi_RC jsi_wsfreeFile(Jsi_Interp *interp, Jsi_HashEntry* hPtr, void *ptr) {
    jsi_wsFile*h = (jsi_wsFile*)ptr;
    if (!h)
        return JSI_OK;
    if (h->fileVal)
        Jsi_DecrRefCount(interp, h->fileVal);
    Jsi_Free(h);
    return JSI_OK;
}

Project: jsish

static Jsi_RC WebSocketStatusCmd(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");
#ifndef OMIT_LWS_WITH_SERVER_STATUS
    char cbuf[JSI_BUFSIZ*2];
    lws_json_dump_context(cmdPtr->context, cbuf, sizeof(cbuf), 0);
    return Jsi_JSONParse(interp, cbuf, ret, 0);
#else
    return Jsi_LogError("unsupported");
#endif
}

Project: jsish

static Jsi_OpCodes *code_bor() { JSI_NEW_CODES(0,OP_BOR, 0); }

Project: jsish

static Jsi_Wide jsi_strtoul(const char *str, char **endptr)
{
#ifdef JSI__LONG_LONG
    int sign;
    int base;
    int i = JsiNumberBase(str, &base, &sign);

    if (base != 10) {
        Jsi_Wide value = strtoul(str + i, endptr, base);
        if (endptr == NULL || *endptr != str + i) {
            return value * sign;
        }
    }

    /* Can just do a regular base-10 conversion */
    return strtoul(str, endptr, 10);
#else
    return (unsigned long)jsi_strtol(str, endptr);
#endif
}

Project: jsish

static Jsi_RC freeGenObjTbl(Jsi_Interp *interp, Jsi_HashEntry *hPtr, void *ptr) {
    Jsi_Obj *obj = (Jsi_Obj *)ptr;
    SIGASSERT(obj,OBJ);
    if (!obj) return JSI_OK;
    Jsi_ObjDecrRefCount(interp, obj);
    return JSI_OK;
}

Project: jsish

static Jsi_RC InterpValueCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    InterpObj *udf = (InterpObj *)Jsi_UserObjGetData(interp, _this, funcPtr);
    Jsi_Interp *sinterp = interp;
    if (udf) {
        if (!udf->subinterp)
        return Jsi_LogError("Sub-interp gone");
        sinterp = udf->subinterp;
        if (interp->threadId != udf->subinterp->threadId)
            return Jsi_LogError("value not supported with threads");
    }
    Jsi_Value *nw = Jsi_ValueArrayIndex(interp, args, 1);
    jsi_Frame *f = sinterp->framePtr;
    Jsi_Number nlev = sinterp->framePtr->level;
    if (nw && Jsi_GetNumberFromValue(interp, nw, &nlev))
        return JSI_ERROR;
    int lev = (int)nlev;
    if (lev <= 0)
        lev = f->level+lev;
    if (lev <= 0 || lev > f->level)
        return Jsi_LogError("level %d not between 1 and %d", (int)nlev, f->level);
    while (f->level != lev  && f->parent)
        f = f->parent;

    const char* arg = Jsi_ValueArrayIndexToStr(interp, args, 0, NULL);
    Jsi_Value *val = NULL;
    if (arg) {
        if (f == sinterp->framePtr)
            val = Jsi_NameLookup(sinterp, arg);
        else {
            jsi_Frame *of = sinterp->framePtr;
            sinterp->framePtr = f;
            val = Jsi_NameLookup(sinterp, arg);
            sinterp->framePtr = of;
        }
    }
    if (!val)
        return Jsi_LogError("unknown var: %s", arg);
    if (sinterp == interp) {
        Jsi_ValueCopy(interp, *ret, val);
        return JSI_OK;
    }
    Jsi_CleanValue(sinterp, interp, val, ret);
    return JSI_OK;
}

Project: jsish

static int jsi_FSRenameProc(Jsi_Interp *interp, Jsi_Value *src, Jsi_Value *dest) {
    const char *zSrc = Jsi_ValueToString(interp, src, NULL);
    const char *zDest = Jsi_ValueToString(interp, dest, NULL);
    Jsi_DString dStr = {}, eStr = {};
    if (*zSrc == '~')
        zSrc = jsi_TildePath(interp, zSrc, &dStr);
    if (*zDest == '~')
        zDest = jsi_TildePath(interp, zDest, &eStr);
    int rc = rename(zSrc, zDest);
    Jsi_DSFree(&dStr);
    Jsi_DSFree(&eStr);
    return rc;
}

Project: jsish

int Jsi_NumberIsInfinity(Jsi_Number a) {
#if JSI__MUSL==1 || defined(__FreeBSD__) || defined(__WIN32)
    if (!isinf(a))
        return 0;
    return (a<0 ? -1 : 1);
#else
    return isinf(a);
#endif
}

Project: jsish

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

Project: jsish

void* Jsi_InterpThread(Jsi_Interp *interp) { return interp->threadId; }

Project: jsish

static Jsi_RC FilesysStatCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    UdfGet(udf, _this, funcPtr);
#if JSI__FILESYS==1
    return jsi_FileStatCmd(interp, udf->fname, _this, ret, funcPtr, 0);
#else
    return JSI_ERROR;
#endif
}

Project: jsish

void Jsi_ValueMove(Jsi_Interp *interp, Jsi_Value *to, Jsi_Value *from )  {
    return jsi_ValueCopyMove(interp, to, from, 0);
}

Project: jsish

static Jsi_RC IterGetKeysCallback(Jsi_Tree* tree, Jsi_TreeEntry *hPtr, void *data)
{
    Jsi_IterObj *io = (Jsi_IterObj *)data;
    if (!hPtr->f.bits.dontenum) {
        IterObjInsert(io, hPtr);
    }
    return JSI_OK;
}

Project: jsish

Jsi_TreeEntry *Jsi_TreeEntryFind (Jsi_Tree *treePtr, const void *key)
{
    Jsi_TreeEntry* hPtr = treePtr->root;
    int rc;
    if (treePtr->flags.destroyed)
        return NULL;
    if (treePtr->opts.lockTreeProc && (*treePtr->opts.lockTreeProc)(treePtr, 1) != JSI_OK)
        return NULL;
    while (hPtr != NULL) {
        rc = treePtr->opts.compareTreeProc(treePtr, Jsi_TreeKeyGet(hPtr), key);
        if (rc == 0) {
            break;
        }
        hPtr = (rc < 0 ? hPtr->left : hPtr->right);
    }
    if (treePtr->opts.lockTreeProc)
        (*treePtr->opts.lockTreeProc)(treePtr, 0);
    return hPtr;
}

Project: jsish

static Jsi_OpCodes *code_greater() { JSI_NEW_CODES(0,OP_GREATER, 0); }

Project: jsish

void Jsi_TreeClear (Jsi_Tree *treePtr)
{
    SIGASSERTV(treePtr, TREE);
    if (treePtr->opts.lockTreeProc && (*treePtr->opts.lockTreeProc)(treePtr, 1) != JSI_OK)
        return;
    destroy_node(treePtr->opts.interp, treePtr->root);
    treePtr->root = NULL;
    if (treePtr->opts.lockTreeProc)
        (*treePtr->opts.lockTreeProc)(treePtr, 0);
}

Project: jsish

Jsi_ScopeStrs *jsi_ScopeStrsNew()
{
    Jsi_ScopeStrs *ret = (Jsi_ScopeStrs *)Jsi_Calloc(1, sizeof(*ret));
    return ret;
}

Project: jsish

static int dbCommitHandler(void *cd) {
    int rc = 0;
    Jsi_Db *jdb = (Jsi_Db*)cd;
    Jsi_Interp *interp = jdb->interp;
    Jsi_Value *vpargs, *items[2] = {}, *ret = Jsi_ValueNew1(interp);
    
    items[0] = Jsi_ValueNewObj(interp, jdb->fobj);
    vpargs = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, items, 1, 0));
    Jsi_IncrRefCount(interp, vpargs);
    if( JSI_OK!=Jsi_FunctionInvoke(interp, jdb->onCommit, NULL, &ret, NULL) ) {
        jdb->errCnt++;
        rc = 1;
    } else
        rc = dbGetIntBool(interp, ret);
    Jsi_DecrRefCount(interp, vpargs);
    Jsi_DecrRefCount(interp, ret);
    return rc;
}

Project: jsish

long long mdbLastInsertRowid(MySqlObj* jdb)
{
    return mysql_insert_id(jdb->db);
}

Project: jsish

static int dbOptSelect(Jsi_Db *jdb, const char *cmd, OptionBind *obPtr, Jsi_CDataDb *dbopts)
{
    void *rec = dbopts[0].data, **recPtrPtr = NULL;
    Jsi_Interp *interp = jdb->interp;
    Jsi_StructSpec *specPtr, *specs = dbopts[0].sf;
    DbEvalContext sEval = {};
    int ccnt = 0;
    Jsi_Wide flags = 0;
    const char *cPtr = Jsi_Strstr(cmd, " %s");
    if (!cPtr) cPtr = Jsi_Strstr(cmd, "\t%s");
    Jsi_DString *eStr;
#ifdef JSI_DB_DSTRING_SIZE
    JSI_DSTRING_VAR(dStr, JSI_DB_DSTRING_SIZE);
#else
    Jsi_DString sStr, *dStr = &sStr;
    Jsi_DSInit(dStr);
#endif
    dbEvalInit(interp, &sEval, jdb, NULL, dStr, 0, 0);
    if (dbopts->noCache)
        sEval.nocache = 1;
    Jsi_DSAppendLen(dStr, cmd, cPtr?(cPtr-cmd):-1);
    if (cPtr) {
        Jsi_DSAppend(dStr, " ", NULL);
        for (specPtr = specs; specPtr->id>=JSI_OPTION_BOOL && specPtr->id < JSI_OPTION_END; specPtr++) {
            if (specPtr == obPtr->dirtyPtr || (specPtr->flags&JSI_OPT_DB_IGNORE))
                continue;
            if (ccnt)
                Jsi_DSAppendLen(dStr, ",", 1);
            Jsi_DSAppend(dStr, "[", specPtr->name, "]", NULL);
            ccnt++; 
        }
        Jsi_DSAppend(dStr, cPtr+3, NULL);
    }
    sEval.zSql = Jsi_DSValue(dStr);
    sEval.nocache = jdb->optPtr->nocache;
    int rc = JSI_ERROR, erc = -1, structSize = 0;
    int cnt = 0, dataMax = (dbopts->isPtr2?0:1);
    int multi = (dbopts->isPtr2!=0);
    int dnum = dbopts[0].arrSize;
    if (dnum<=0 && !dbopts->isPtr2) {
        dataMax = dnum = 1;
    }
    if (dnum>1) {
        multi = 1;
        dataMax = dbopts[0].arrSize;
    }
    if (dbopts->isPtr2) {
        recPtrPtr = (void**)rec; /* This is really a void***, but this gets recast below. */
        rec = *recPtrPtr;
    }
    structSize = specs[obPtr->optLen].size;

    cnt = 0;
    int ncnt = 0, bindMax = -1, dataIdx = -1;
    while(1) {
        dataIdx++;
        if (dataIdx>=dataMax) {
            if (!dbopts->isPtr2)
                break;
            else {
            /* Handle fully dynamic allocation of memory. */
#ifndef JSI_DB_MAXDYN_SIZE
#define JSI_DB_MAXDYN_SIZE 100000000
#endif
#ifndef JSI_DB_DYN_INCR
#define JSI_DB_DYN_INCR 16
#endif
                int ddMax = (dbopts->maxSize>0?dbopts->maxSize:JSI_DB_MAXDYN_SIZE);
                if (dataMax>=ddMax)
                    break;
                int olddm = dataMax;
                dataMax += JSI_DB_DYN_INCR;
                if (dataMax>ddMax)
                    dataMax = ddMax;
                if (!olddm)
                    rec = Jsi_Calloc(dataMax+1, sizeof(void*));
                else {
                    rec = Jsi_Realloc(rec, (dataMax+1)*sizeof(void*));
                    memset((char*)rec+olddm*sizeof(void*), 0, (dataMax-olddm+1)*sizeof(void*));
                }
                *recPtrPtr = rec;
            }
        }

        rc = dbEvalStepOption(&sEval, obPtr, &ncnt, dataIdx, bindMax, dbopts, &erc);
        if (rc == JSI_ERROR)
            break;
        if (rc != JSI_OK)
            break;
        cnt += ncnt;
        sqlite3_stmt *pStmt = sEval.pPreStmt->pStmt;
        int idx;
        int nCol;
        char **apColName;
        const char *str;
        int *apColType;
        void *prec = rec;
        bindMax = 0;

        if (dbopts->isPtr2 || dbopts->isPtrs) {
            prec = ((void**)rec)[dataIdx];
            if (!prec)
                ((void**)rec)[dataIdx] = prec = Jsi_Calloc(1, structSize);
        } else
                prec = (char*)rec + (dataIdx * structSize);
        dbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
        for (idx=0; idx<nCol; idx++) {
            specPtr = dbLookupSpecFromName(specs, apColName[idx]);
            if (!specPtr) {
                Jsi_LogError("unknown column name: %s", apColName[idx]);
                goto bail;
            }          
            if (specPtr->id<JSI_OPTION_BOOL || specPtr->id>=JSI_OPTION_END) {
                Jsi_LogError("unknown option type \"%d\" for \"%s\"", specPtr->id, specPtr->name);
                goto bail;
            }
            char *ptr = (char*)prec + specPtr->offset;

            switch (specPtr->id) {
                case JSI_OPTION_BOOL:
                    *(int*)ptr = sqlite3_column_int(pStmt, idx);
                    break;
                case JSI_OPTION_INT: *(int*)ptr = (int)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_UINT: *(uint*)ptr = (uint)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_INTPTR_T: *(intptr_t*)ptr = (intptr_t)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_UINTPTR_T: *(uintptr_t*)ptr = (uintptr_t)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_SIZE_T: *(size_t*)ptr = (size_t)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_SSIZE_T: *(ssize_t*)ptr = (ssize_t)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_SHORT: *(short*)ptr = (int)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_USHORT: *(ushort*)ptr = (uint)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_LONG: *(long*)ptr = (int)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_ULONG: *(ulong*)ptr = (uint)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_INT8: *(int8_t*)ptr = (int)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_UINT8: *(uint8_t*)ptr = (uint)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_INT16: *(int16_t*)ptr = (int)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_UINT16: *(uint16_t*)ptr = (uint)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_INT32: *(int32_t*)ptr = (int)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_UINT32: *(uint32_t*)ptr = (uint)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_INT64: *(int64_t*)ptr = (int)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_TIME_W:
                case JSI_OPTION_UINT64: *(uint64_t*)ptr = (uint)sqlite3_column_int64(pStmt, idx); break;
                case JSI_OPTION_TIME_T:
                    *(time_t*)ptr = (time_t)sqlite3_column_int64(pStmt, idx);
                    break;
                case JSI_OPTION_NUMBER:
                    *(Jsi_Number*)ptr = (Jsi_Number)sqlite3_column_double(pStmt, idx);
                    break;
                case JSI_OPTION_TIME_D:
                case JSI_OPTION_FLOAT:
                    *(float*)ptr = (float)sqlite3_column_double(pStmt, idx);
                    break;
                case JSI_OPTION_DOUBLE:
                    *(double*)ptr = (double)sqlite3_column_double(pStmt, idx);
                    break;
                case JSI_OPTION_LDOUBLE:
                    *(ldouble*)ptr = (ldouble)sqlite3_column_double(pStmt, idx);
                    break;
                case JSI_OPTION_DSTRING:
                    eStr = (Jsi_DString*)ptr;
                    str = (char*)sqlite3_column_text(pStmt, idx );
                    if (!str)
                        str = jdb->optPtr->nullvalue;
                    Jsi_DSSet(eStr, str?str:"");
                    break;
                case JSI_OPTION_STRBUF:
                    str = (char*)sqlite3_column_text(pStmt, idx );
                    if (!str)
                        str = jdb->optPtr->nullvalue;
                    strncpy((char*)ptr, str?str:"", specPtr->size);
                    ((char*)ptr)[specPtr->size-1] = 0;
                    break;
                case JSI_OPTION_CUSTOM: {
                    Jsi_OptionCustom* cust = Jsi_OptionCustomBuiltin(specPtr->custom);
                    if (cust && cust->parseProc) {
                        str = (char*)sqlite3_column_text(pStmt, idx );
                        if ((*cust->parseProc)(interp, (Jsi_OptionSpec*)specPtr, NULL, str, prec, flags) != JSI_OK) {
                            goto bail;
                        }
                    } else {
                        Jsi_LogError("missing or invalid custom for \"%s\"", specPtr->name);
                        goto bail;
                    }
                    break;
                }
                case JSI_OPTION_STRKEY:
                    str = (char*)sqlite3_column_text(pStmt, idx );
                    if (!str)
                        str = jdb->optPtr->nullvalue;
                    *(char**)ptr = (str?(char*)Jsi_DbKeyAdd(jdb, str):NULL);
                    break;
#ifndef JSI_LITE_ONLY
                case JSI_OPTION_STRING: {
                    Jsi_Value *vPtr = *((Jsi_Value **)ptr);
                    if (!(specPtr->flags&JSI_OPT_NO_DUPVALUE)) {
                        if (vPtr) Jsi_DecrRefCount(interp, vPtr);
                        *((Jsi_Value **)ptr) = NULL;
                    }
                    str = (char*)sqlite3_column_text(pStmt, idx );
                    if (!str)
                        str = jdb->optPtr->nullvalue;
                    if (str) {
                        vPtr = Jsi_ValueNewStringDup(interp, str);
                        *((Jsi_Value **)ptr) = vPtr;
                    }
                    break;
                }
#else
                case JSI_OPTION_STRING:        
#endif
                case JSI_OPTION_VALUE: /* The rest are unsupported. */
                case JSI_OPTION_VAR:
                case JSI_OPTION_OBJ:
                case JSI_OPTION_ARRAY:
                case JSI_OPTION_REGEXP:
                case JSI_OPTION_FUNC:
                
#ifdef __cplusplus
                case JSI_OPTION_USEROBJ:
                case JSI_OPTION_END:
#else
                default:
#endif
                    JSI_DBQUERY_PRINTF( "unsupported type: %d:%s\n", specPtr->id,
                        jsi_DbOptionTypeStr(specPtr->id, 0));
                    break;
            }
        }
        if (dbopts[0].callback)
            dbopts[0].callback(interp, dbopts, prec);
        cnt++;
        if (!multi)
            break;
    }
    dbEvalFinalize(&sEval);
    if( rc==JSI_BREAK ) {
        rc = JSI_OK;
    }
    return (rc==JSI_OK?cnt:erc);

bail:
    dbEvalFinalize(&sEval);
    return erc;
}

Project: jsish

Jsi_RC Jsi_TreeConf(Jsi_Tree *treePtr, Jsi_MapOpts *opts, bool set)
{
    if (set)
        treePtr->opts = *opts;
    else
        *opts = treePtr->opts;
    return JSI_OK;
}

Project: jsish

static Jsi_OpCodes *code_chthis(jsi_Pstate *p, jsi_Pline *line, int n) { JSI_NEW_CODESLN(0,OP_CHTHIS, n); }

Project: jsish

const char* Jsi_KeyLookup(Jsi_Interp *interp, const char *str)
{
    Jsi_MapEntry *hPtr;
    hPtr = Jsi_MapEntryFind(interp->strKeyTbl, str);
    if (!hPtr) {
        return NULL;
    }
    return (const char*)Jsi_MapKeyGet(hPtr, 0);
}

Project: jsish

static Jsi_OpCodes *code_greaterequ() { JSI_NEW_CODES(0,OP_GREATEREQU, 0); }

Project: jsish

void *Jsi_TreeGet(Jsi_Tree *treePtr, void *key, int flags)
{
    Jsi_TreeEntry *hPtr = Jsi_TreeEntryFind(treePtr, key);
    if (!hPtr)
        return NULL;
    return Jsi_TreeValueGet(hPtr);
}

Project: jsish

static int jsi_FSLstatProc(Jsi_Interp *interp, Jsi_Value* path, Jsi_StatBuf *buf) {
#ifdef __WIN32
    return jsi_FSStatProc(interp, path, buf);
#else
    const char *pathPtr = Jsi_ValueToString(interp, path, NULL);
    Jsi_DString dStr = {};
    if (*pathPtr == '~')
        pathPtr = jsi_TildePath(interp, pathPtr, &dStr);
    int rc = lstat(pathPtr, buf);
    Jsi_DSFree(&dStr);
    return rc;
#endif
}

Project: jsish

Jsi_RC Jsi_GetBool(Jsi_Interp* interp, const char *string, bool *n)
{
    int len = Jsi_Strlen(string);
    if (len && (Jsi_Strncasecmp(string, "true", len)==0 && len<=4)) {
        *n = 1;
        return JSI_OK;
    }
    if (len && (Jsi_Strncasecmp(string, "false", len)==0 && len<=5)) {
        *n = 0;
        return JSI_OK;
    }
    return JSI_ERROR;
}

Project: jsish

void jsi_AllValueOp(Jsi_Interp *interp, Jsi_Value* val, int op) {
    if (op==1) {
        //printf("ADD: %p : %p : %d\n", interp, val, val->VD.Idx);
        assert(interp->allValues!=val);
        val->next = interp->allValues;
        if (interp->allValues)
            interp->allValues->prev = val;
        interp->allValues = val;
        return;
    }
    if (op==0) {
        //printf("DEL: %p : %p\n", interp, val);
        if (!val || !interp->allValues) return;
        if (val == interp->allValues)
            interp->allValues = val->next;
        if (val->next)
            val->next->prev = val->prev;
        if (val->prev)  
            val->prev->next = val->next; 
        return;
    }
    if (op == -1) {
        while (interp->allValues) {
            printf("NEED CLEANUP: %p\n", interp->allValues);
            Jsi_ValueFree(interp, interp->allValues);
        }
        return;
    }
#if JSI__MEMDEBUG
    assert(0);
    abort();
#endif
}

Project: jsish

static void mdbEvalSetColumnJSON(MyDbEvalContext *p, int iCol, Jsi_DString *dStr) {
    Jsi_Interp *interp = p->jdb->interp;
    char nbuf[JSI_MAX_NUMBER_STRING];
    MysqlPrep *prep = p->prep;
    SqlFieldResults *field = prep->fieldResult+iCol;
    if (field->isnull) {
        Jsi_DSAppend(dStr, "null", NULL);
        return;
    }
    const char *zBlob = "";
    int bytes = 0;

    switch(field->jsiTypeMap) {
        case JSI_OPTION_BOOL: {
            snprintf(nbuf, sizeof(nbuf), "%s", field->buffer.vchar?"true":"false");
            Jsi_DSAppend(dStr, nbuf, NULL);
            return;
        }
        case JSI_OPTION_INT64: {
            snprintf(nbuf, sizeof(nbuf), "%lld", field->buffer.vlonglong);
            Jsi_DSAppend(dStr, nbuf, NULL);
            return;
        }
        case JSI_OPTION_DOUBLE: {
            Jsi_NumberToString(interp, field->buffer.vdouble, nbuf, sizeof(nbuf));
            Jsi_DSAppend(dStr, nbuf, NULL);
            return;
        }
        //case JSI_OPTION_TIME_T:
        case JSI_OPTION_TIME_D:
        case JSI_OPTION_TIME_W: {
            Jsi_Number jtime = mdbMyTimeToJS(&field->buffer.timestamp);
            Jsi_NumberToString(interp, jtime, nbuf, sizeof(nbuf));
            Jsi_DSAppend(dStr, nbuf, NULL);
            return;
        }
        case JSI_OPTION_STRING:
            zBlob = field->buffer.vstring;
        default:
        {
            if( !zBlob ) {
                Jsi_DSAppend(dStr, "null", NULL);
                return;
            }
            Jsi_JSONQuote(interp, zBlob, bytes, dStr);
            return;
        }
    }
}

Project: jsish

Jsi_RC Jsi_ValueToBool(Jsi_Interp *interp, Jsi_Value *v)
{
    Jsi_RC rc = JSI_OK;
    bool a = 0;
    switch(v->vt) {
        case JSI_VT_BOOL:
            a = v->d.val;
            break;
        case JSI_VT_NULL:
            break;
        case JSI_VT_UNDEF:
            break;
        case JSI_VT_NUMBER:
            a = (v->d.num ? 1: 0);
            break;
        case JSI_VT_STRING:     /* TODO: NaN, and accept true/false string? */
            a = atoi(v->d.s.str);
            a = (a ? 1 : 0);
            break;
        case JSI_VT_OBJECT: {
            Jsi_Obj *obj = v->d.obj;
            switch(obj->ot) {
                case JSI_OT_BOOL:
                    a = (obj->d.val ? 1.0: 0);
                    break;
                case JSI_OT_NUMBER:
                    a = obj->d.num;
                    a = (a ? 1 : 0);
                    break;
                case JSI_OT_STRING:
                    a = atoi(obj->d.s.str);
                    a = (a ? 1 : 0);
                    break;
                default:
                    break;
            }
            break;
        }
        default:
            Jsi_LogBug("Convert a unknown type: 0x%x to number", v->vt);
            return JSI_ERROR;
    }
    Jsi_ValueReset(interp,&v);
    Jsi_ValueMakeBool(interp, &v, a);
    return rc;
}

Project: jsish

static const char *jsi_TildePath(Jsi_Interp *interp, const char* path, Jsi_DString *dStr) {
    if (*path != '~')
        return path;
    const char *homedir = jsi_GetHomeDir(interp);
    if (!homedir)
        return path;
    Jsi_DSAppend(dStr, homedir, path[1] == '/' ? "" : "/", path+1, NULL);
    return Jsi_DSValue(dStr);
}

Project: jsish

int jsi_InitTree(Jsi_Interp *interp, int release)
{
    if (release) return JSI_OK;
    TreeTest(interp);
    return JSI_OK;
}

Project: jsish

void jsi_DebugObj(Jsi_Obj* o, const char *reason, uint cidx, Jsi_Interp *interp)
{
    if (jsi_memDebugBreakIdx && jsi_memDebugBreakIdx == o->VD.Idx)
        jsi_memDebugBreak();
    return;
}

Project: jsish

static char *dbLocalGetline(Jsi_Interp *interp, char *zPrompt, Jsi_Channel in) {
    char *zLine;
    int nLine;
    int n;
    int eol;

    nLine = 100;
    zLine = (char*)Jsi_Malloc( nLine );
    if( zLine==0 ) return 0;
    n = 0;
    eol = 0;
    while( !eol ) {
        if( n+100>nLine ) {
            nLine = nLine*2 + 100;
            zLine = (char*)Jsi_Realloc(zLine, nLine);
            if( zLine==0 ) return 0;
        }
        if( Jsi_Gets(interp, in, &zLine[n], nLine - n)==0 ) {
            if( n==0 ) {
                Jsi_Free(zLine);
                return 0;
            }
            zLine[n] = 0;
            eol = 1;
            break;
        }
        while( zLine[n] ) {
            n++;
        }
        if( n>0 && zLine[n-1]=='\n' ) {
            n--;
            zLine[n] = 0;
            eol = 1;
        }
    }
    zLine = (char*)Jsi_Realloc( zLine, n+1 );
    return zLine;
}

Project: jsish

Jsi_RC Jsi_GetWide(Jsi_Interp* interp, const char *string, Jsi_Wide *widePtr, int base)
{
    char *endptr;

    if (base) {
        *widePtr = strtoull(string, &endptr, base);
    }
    else {
        *widePtr = jsi_strtoull(string, &endptr);
    }

    return JsiCheckConversion(string, endptr);
}

Project: jsish

static Jsi_RC SqliteOnecolumnCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                          Jsi_Value **ret, Jsi_Func *funcPtr)
{
    Jsi_RC rc;
    Jsi_Db *jdb;
    if (!(jdb = dbGetDbHandle(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);
    DbEvalContext sEval = {};
    const char *zSql = Jsi_ValueGetDString(interp, vSql, &dStr, 0);

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


    if( JSI_OK==(rc = dbEvalStep(&sEval)) ) {
        sqlite3_stmt *pStmt = sEval.pPreStmt->pStmt;
        int nCol = sqlite3_column_count(pStmt);
        if (nCol>0)
            dbEvalSetColumnValue(&sEval, 0, ret);
        cnt++;
    }
    dbEvalFinalize(&sEval);
    if( rc==JSI_BREAK ) {
        rc = JSI_OK;
    }
    Jsi_DSFree(&dStr);
    return rc;
}

Project: jsish

int Jsi_Lstat(Jsi_Interp *interp, Jsi_Value* path, Jsi_StatBuf *buf) {
    void *data;
    Jsi_Filesystem *fsPtr = Jsi_FilesystemForPath(interp, path, &data);
    if (fsPtr == NULL || !fsPtr->lstatProc) return -1;
    return fsPtr->lstatProc(interp, path, buf);
}

Project: jsish

static Jsi_RC SysGetEnvCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    extern char **environ;
    char *cp;
    int i;
    if (interp->isSafe)
        return Jsi_LogError("no getenv in safe mode");
    Jsi_Value *v = Jsi_ValueArrayIndex(interp, args, 0);
    if (v != NULL) {
        const char *fnam = Jsi_ValueString(interp, v, NULL);
        if (!fnam) 
            return Jsi_LogError("arg1: expected string 'name'");
        cp = getenv(fnam);
        if (cp != NULL) {
            Jsi_ValueMakeStringDup(interp, ret, cp);
        }
        return JSI_OK;
    }
   /* Single object containing result members. */
    Jsi_Value *vres;
    Jsi_Obj  *ores = Jsi_ObjNew(interp);
    Jsi_Value *nnv;
    char *val, nam[JSI_BUFSIZ/2];
    //Jsi_ObjIncrRefCount(interp, ores);
    vres = Jsi_ValueMakeObject(interp, NULL, ores);
    //Jsi_IncrRefCount(interp, vres);
    
    for (i=0; ; i++) {
        int n;
        cp = environ[i];
        if (cp == 0 || ((val = Jsi_Strchr(cp, '='))==NULL))
            break;
        n = val-cp+1;
        if (n>=(int)sizeof(nam))
            n = sizeof(nam)-1;
        Jsi_Strncpy(nam, cp, n);
        val = val+1;
        nnv = Jsi_ValueMakeStringDup(interp, NULL, val);
        Jsi_ObjInsert(interp, ores, nam, nnv, 0);
    }
    Jsi_ValueReplace(interp, ret, vres);
    return JSI_OK;
}

Project: jsish

static Jsi_OpCodes *code_push_string(jsi_Pstate *p, jsi_Pline *line, const char *str) {
    if (*str == 'c' && !Jsi_Strcmp(str,"callee"))
        p->interp->hasCallee = 1;
    JSI_NEW_CODESLN(0,OP_PUSHSTR, str);
}

Project: jsish

static Jsi_RC DebugRemoveCmd_(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr, int op)
{
    Jsi_Value *val = Jsi_ValueArrayIndex(interp, args, 0);
    if (interp->breakpointHash)
    {
        int num;
        char nbuf[JSI_MAX_NUMBER_STRING];
        if (Jsi_GetIntFromValue(interp, val, &num) != JSI_OK) 
            return Jsi_LogError("bad number");
        
        snprintf(nbuf, sizeof(nbuf), "%d", num);
        Jsi_HashEntry *hPtr = Jsi_HashEntryFind(interp->breakpointHash, nbuf);
        jsi_BreakPoint* bptr;
        if (hPtr && (bptr = (jsi_BreakPoint*)Jsi_HashValueGet(hPtr))) {
            switch (op) {
                case 1: bptr->enabled = 0; break;
                case 2: bptr->enabled = 1; break;
                default:
                    Jsi_HashEntryDelete(hPtr);
            }
            return JSI_OK;
        }
    }
    return Jsi_LogError("unknown breakpoint");
}

Project: jsish

Jsi_RC Jsi_Interactive(Jsi_Interp* interp, int flags) 
{
    Jsi_RC rc = JSI_OK;
    int done = 0, len, quote = (flags & 0xff), istty = 1, chkHelp=0, hasHelp=0;
    const char *prompt = interp->subOpts.prompt;
    char *buf;
    if (jsi_interactiveInterp) 
        return Jsi_LogError("multiple interactive not supported");
#if JSI__SIGNAL
  signal(SIGINT, jsi_InteractiveSignal); 
#endif
    interp->typeCheck.parse = interp->typeCheck.run = interp->typeCheck.all = 1;
    interp->strict = 1;
    interp->isInteractive = 1;
    jsi_interactiveInterp = interp;
    interp->subOpts.istty = 1;
    interp->subOpts.logAllowDups = 1;
    Jsi_DString dStr;
    Jsi_DSInit(&dStr);
#ifndef __WIN32
    istty = isatty(fileno(stdin));
#else
    istty = _isatty(_fileno(stdin));
#endif
#ifdef JSI_HAS_READLINE
    Jsi_DString dHist = {}, sHist = {};
    char *hist = NULL;
#ifdef USE_GNU_READLINE
    rl_attempted_completion_function = jsiRlGetMatches;
#else
    linenoiseSetCompletionCallback(jsiLNGetMatches);
    linenoiseSetHintsCallback(jsiLNhints);
#endif
    if(interp->subOpts.noReadline == 0 && !interp->parent && !(interp->isSafe && interp->safeMode==jsi_safe_Lockdown))
    {
        const char *hfile = (interp->historyFile ? interp->historyFile : "~/.jsish_history");
        hist = Jsi_NormalPath(interp, hfile, &dHist);
        if (hist)
            jsi_sh_read_history(hist);
    }
#endif
    interp->level++;
    if (!interp->iskips)
        puts("Jsish interactive: see 'help [cmd]' or 'history'.  \\ cancels > input."
#if JSI__SIGNAL
        "  ctrl-c aborts running script."
#endif
        );
    while (done==0 && interp->exited==0) {
        buf = get_inputline(interp, istty, (prompt?prompt:"$ "));
        if (buf) {
            if (buf[0] == '\\' && !buf[1]) {
                 Jsi_DSSetLength(&dStr, 0);
                 prompt = interp->subOpts.prompt;
                 fprintf(stderr, "abandoned input");
            } else
                Jsi_DSAppend(&dStr, buf, NULL);
            free(buf);
        } else {
            done = 1;
        }
        len = Jsi_DSLength(&dStr);
        if (done && len == 0)
            break;
        if (!len) continue;
        Jsi_DSAppendLen(&dStr, " ", 1); // Allow for added space.
        buf = Jsi_DSValue(&dStr);
        if (done == 0 && (!jsi_StrIsBalanced(buf))) {
            prompt = interp->subOpts.prompt2;
            continue;
        }
        prompt = interp->subOpts.prompt;
        while ((len = Jsi_Strlen(buf))>0 && (isspace(buf[len-1])))
            buf[len-1] = 0;
        if (buf[0] == 0) {
            Jsi_DSSetLength(&dStr, 0);
            continue;
        }
        bool wantHelp = 0;
        if (interp->onEval == NULL) {
            /* Convenience: add semicolon to "var" statements (required by parser). */
#ifdef JSI_HAS_READLINE
            if (!Jsi_Strncmp(buf, "history", 7) && buf[7] == 0) {
                fputs(Jsi_DSValue(&sHist), stdout);
                Jsi_DSSetLength(&dStr, 0);
                continue;
            }
#endif
            if (!Jsi_Strncmp(buf, "help", 4) && (buf[4] == 0 || isspace(buf[4]))) {
                if (!chkHelp++)
                    hasHelp = (Jsi_PkgRequire(interp, "Help", 0)>=0);
                if (hasHelp) {
                    wantHelp = 1;
                    char tbuf[BUFSIZ];
                    snprintf(tbuf, sizeof(tbuf), "return runModule('Help', '%s'.trim().split(null));", buf+4);
                    rc = Jsi_EvalString(interp, tbuf, JSI_RETURN);
                }
            }
            if (!wantHelp) {
                if (!Jsi_Strncmp(buf,"var ", 4) && Jsi_Strchr(buf, '\n')==NULL && Jsi_Strchr(buf, ';')==NULL)
                    Jsi_Strcpy(buf+Jsi_Strlen(buf), ";"); // Added space above so strcat ok.
                rc = Jsi_EvalString(interp, buf, JSI_EVAL_RETURN);
                prompt = interp->subOpts.prompt;
#ifdef JSI_HAS_READLINE
                if (rc == JSI_OK)
                    Jsi_DSAppend(&sHist, buf, "\n", NULL);
#endif
            }
        }
        else
        {
            Jsi_Value *func = interp->onEval;
            if (func && Jsi_ValueIsFunction(interp, func)) {
                Jsi_Value *items[1] = {};
                items[0] = Jsi_ValueNewStringDup(interp, buf);
                Jsi_Value *args = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, items, 1, 0));
                Jsi_IncrRefCount(interp, args);
                rc = Jsi_FunctionInvoke(interp, func, args, &interp->retValue, interp->csc);
                Jsi_DecrRefCount(interp, args);
                if (rc != JSI_OK)
                    fprintf(stderr, "bad eval");
            }
        }
        if (interp->exited)
            break;
        jsi_interactiveInterp->interrupted = 0;
        if (rc == JSI_OK) {
             if (interp->retValue->vt != JSI_VT_UNDEF || interp->subOpts.outUndef) {
                Jsi_DString eStr = {};
                fputs(Jsi_ValueGetDString(interp, interp->retValue, &eStr, hasHelp?0:quote), stdout);
                Jsi_DSFree(&eStr);
                fputs("\n", stdout);
             }
        } else if (!interp->exited && !wantHelp) {
            fputs("ERROR\n", stderr);
        }
        Jsi_DSSetLength(&dStr, 0);
        len = 0;
    }
    interp->level--;
#ifdef JSI_HAS_READLINE
    if (hist && !interp->isSafe) {
        jsi_sh_stifle_history(100);
        jsi_sh_write_history(hist);
    }
    Jsi_DSFree(&dHist);
    Jsi_DSFree(&sHist);
#endif
    Jsi_DSFree(&dStr);
    if (interp->retValue) {
        Jsi_DecrRefCount(interp, interp->retValue);
        interp->retValue = NULL;
    }
    if (interp->exited && interp->level <= 0)
    {
        rc = JSI_EXIT;
        Jsi_InterpDelete(interp);
    }
    jsi_interactiveInterp = NULL;
    return rc;
}

Project: jsish

extern Jsi_otype Jsi_ObjTypeGet(Jsi_Obj *obj)
{
    return obj->ot;
}

Project: jsish

void jsi_FreeOpcodes(Jsi_OpCodes *ops) {
    int i;
    if (!ops) return;
    for (i=0; i<ops->code_len; i++) {
        jsi_OpCode *op = ops->codes+i;
        if (op->data && op->alloc)
            Jsi_Free(op->data);
        _JSI_MEMCLEAR(op);
    }
    jsiOpCodesCnt[1]++;
    jsiOpCodesCnt[2]--;
#ifdef JSI_MEM_DEBUG
    if (ops->hPtr)
        Jsi_HashEntryDelete(ops->hPtr);
#endif
    Jsi_Free(ops->codes);
    Jsi_Free(ops);
}

Project: jsish

static Jsi_OpCodes *code_local(jsi_Pstate *p, jsi_Pline *line, const char *varname) { JSI_NEW_CODESLN(0,OP_LOCAL, varname); }

Project: jsish

jsi_wsFileUploadCB(void *data, const char *name, const char *filename,
                   char *buf, int len, enum lws_spa_fileupload_states state)
{
    jsi_wsPss *pss = (typeof(pss))data;
    jsi_wsCmdObj *cmdPtr = pss->cmdPtr;
    Jsi_Value* callPtr = (pss->onUpload?pss->onUpload:cmdPtr->onUpload);
    Jsi_Interp *interp = cmdPtr->interp;
    const char *str;
    int slen, n = 0;
    if (cmdPtr->deleted) return -1;

    Jsi_Obj *oarg1;
    Jsi_Value *vpargs, *vargs[10];
    if (state == LWS_UFS_OPEN)
        pss->file_length = 0;
    //id:number, filename:string, data:string, startpos:number, complete:boolean
    vargs[n++] = Jsi_ValueNewObj(interp, cmdPtr->fobj);
    vargs[n++] = Jsi_ValueNewNumber(interp, (Jsi_Number)(pss->wid));
    vargs[n++] = Jsi_ValueNewBlobString(interp, filename);
    vargs[n++] = Jsi_ValueNewBlob(interp, (uchar*)buf, (uint)len);
    vargs[n++] = Jsi_ValueNewNumber(interp, (Jsi_Number)pss->file_length);
    vargs[n++] = Jsi_ValueNewBoolean(interp, (state==LWS_UFS_FINAL_CONTENT));
    vpargs = Jsi_ValueMakeObject(interp, NULL, oarg1 = Jsi_ObjNewArray(interp, vargs, n, 0));
    Jsi_IncrRefCount(interp, vpargs);
    pss->file_length += len;

    Jsi_Value *ret = Jsi_ValueNew1(interp);
    Jsi_ValueMakeUndef(interp, &ret);
    Jsi_RC rc = Jsi_FunctionInvoke(interp, callPtr, vpargs, &ret, NULL);

    if ((state==LWS_UFS_FINAL_CONTENT || rc != JSI_OK) && (str=Jsi_ValueString(interp, ret, &slen))) {
        Jsi_DSAppendLen(&pss->resultStr, str, slen);
        pss->resultCode = rc;
    }

    Jsi_DecrRefCount(interp, vpargs);
    Jsi_DecrRefCount(interp, ret);
    if (rc != JSI_OK) {
        Jsi_LogError("websock bad rcv eval");
        return -1;
    }
    return 0;
}

Project: jsish

static Jsi_RC MySqlLastRowidCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                                    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    Jsi_Wide rowid;
    MySqlObj *jdb;
    if (!(jdb = _mysql_getDbHandle(interp, _this, funcPtr))) return JSI_ERROR;
    rowid = mdbLastInsertRowid(jdb);
    Jsi_ValueMakeNumber(interp, ret, (Jsi_Number)rowid);
    return JSI_OK;
}

Project: jsish

void jsi_ValueObjSet(Jsi_Interp *interp, Jsi_Value *target, const char *key, Jsi_Value *value, int flags, int isstrkey)
{
    Jsi_TreeEntry *hPtr;
    if (target->vt != JSI_VT_OBJECT) {
        if (interp->strict)
            Jsi_LogWarn("Target is not object: %d", target->vt);
        return;
    }
    hPtr = Jsi_ObjInsert(interp, target->d.obj, key, value, (isstrkey?JSI_OM_ISSTRKEY:0));
    if (!hPtr)
        return;
    hPtr->f.flags |= (flags&JSI_TREE_USERFLAG_MASK);
}

Project: jsish

Jsi_RC Jsi_EvalFile(Jsi_Interp* interp, Jsi_Value *fname, int flags)
{
    int isnull;
    if ((isnull=Jsi_ValueIsNull(interp, fname)) || Jsi_ValueIsUndef(interp, fname)) 
        return Jsi_LogError("invalid file eval %s", (isnull?"null":"undefined"));
    int lev = interp->framePtr->level;
    return jsi_evalStrFile(interp, fname, NULL, flags, lev);
}

Project: jsish

Jsi_Value* Jsi_ValueNewNumber(Jsi_Interp *interp, Jsi_Number n) {
    Jsi_Value *v = Jsi_ValueNew(interp);
    v->vt = JSI_VT_NUMBER;
    v->d.num = n;
    return v;
}

Project: jsish

static void jsi_wsgetUriArgValue(Jsi_Interp *interp, struct lws *wsi, Jsi_Value **vPtr, Jsi_Value **oPtr)
{
    int n = 0;
    char buf[JSI_BUFSIZ*8];
    while (lws_hdr_copy_fragment(wsi, buf, sizeof(buf), WSI_TOKEN_HTTP_URI_ARGS, n++) > 0) {
        if (!*vPtr) {
            *vPtr = Jsi_ValueNewArray(interp, NULL, 0);
            Jsi_IncrRefCount(interp, *vPtr);
        }
        Jsi_ValueArraySet(interp, *vPtr, Jsi_ValueNewStringDup(interp, buf), n-1);
        if (!*oPtr) {
            *oPtr = Jsi_ValueNewObj(interp, NULL);
            Jsi_IncrRefCount(interp, *oPtr);
        }
        char *cp = Jsi_Strchr(buf, '=');
        if (cp) {
            *cp = 0;
            Jsi_ValueInsertFixed(interp, *oPtr, buf, Jsi_ValueNewStringDup(interp, cp+1));
        } else {
            Jsi_ValueInsertFixed(interp, *oPtr, buf, Jsi_ValueNewNull(interp));
        }
    }
}

Project: jsish

static void mdbEvalSetColumn(MyDbEvalContext *p, int iCol, Jsi_DString *dStr) {
    //Jsi_Interp *interp = p->jdb->interp;
    char nbuf[JSI_MAX_NUMBER_STRING];
    MysqlPrep *prep = p->prep;
    SqlFieldResults *field = prep->fieldResult+iCol;
    Jsi_Interp *interp = p->jdb->interp;
    if (field->isnull)
        return;
        
    switch(field->jsiTypeMap) {
        case JSI_OPTION_STRING: {
            int bytes = field->len;
            const char *zBlob = field->buffer.vstring;
    
            if( !zBlob ) {
                const char *nv = p->jdb->optPtr->nullvalue;
                Jsi_DSAppend(dStr, nv?nv:"null", NULL);
                return;
            }
            Jsi_DSAppendLen(dStr, zBlob, bytes);
            return;
        }
        case JSI_OPTION_BOOL: {
            snprintf(nbuf, sizeof(nbuf), "%s", field->buffer.vchar?"true":"false");
            Jsi_DSAppend(dStr, nbuf, NULL);
            return;
        }
        case JSI_OPTION_INT64: {
            snprintf(nbuf, sizeof(nbuf), "%lld", field->buffer.vlonglong);
            Jsi_DSAppend(dStr, nbuf, NULL);
            return;
        }
        //case JSI_OPTION_TIME_T:
        case JSI_OPTION_TIME_D:
        case JSI_OPTION_TIME_W: {
            Jsi_Number jtime = mdbMyTimeToJS(&field->buffer.timestamp);
            Jsi_NumberToString(interp, jtime, nbuf, sizeof(nbuf));
            Jsi_DSAppend(dStr, nbuf, NULL);
            return;
        }
        case JSI_OPTION_DOUBLE: {
            Jsi_NumberToString(interp, field->buffer.vdouble, nbuf, sizeof(nbuf));
            Jsi_DSAppend(dStr, nbuf, NULL);
            return;
        }
        default:
            Jsi_LogWarn("unknown type: %d", field->jsiTypeMap);
    
    }
}

Project: jsish

Jsi_RC Jsi_RegExpMatches(Jsi_Interp *interp, Jsi_Value *pattern, const char *str, int slen, Jsi_Value *ret)
{
    return jsi_RegExpMatches(interp, pattern, str, slen, ret, NULL, 0);
}

Project: jsish

static Jsi_RC StringSubstrCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    int sLen, bLen;
    const char *v;
    ChkString(_this, funcPtr, v, &sLen, &bLen);

    Jsi_Value *start = Jsi_ValueArrayIndex(interp, args, skip);
    Jsi_Value *len = Jsi_ValueArrayIndex(interp, args, skip+1);
    
    Jsi_Number nstart, nlen;
    if (!start || Jsi_GetNumberFromValue(interp,start, &nstart) != JSI_OK) {
        jsi_ValueMakeBlobDup(interp, ret, (uchar*)v, bLen);
        return JSI_OK;
    }
    int istart = (int)nstart, olen = -1;
    Jsi_DString dStr;
    char *ostr;
    Jsi_DSInit(&dStr);
    if (!len || Jsi_GetNumberFromValue(interp,len, &nlen) != JSI_OK) {
        if (sLen == bLen) {
            ostr = jsi_SubstrDup(v, bLen, istart, -1, &olen);
            Jsi_ValueMakeBlob(interp, ret, (uchar*)ostr, olen);
        } else {
            Jsi_UtfSubstr(v, istart, -1, &dStr);
            Jsi_ValueFromDS(interp, &dStr, ret);
        }
        return JSI_OK;
    }
    int ilen = (int)nlen;
    if (ilen <= 0) {
        Jsi_ValueMakeStringDup(interp, ret, "");
    } else {
        if (sLen == bLen) {
            ostr = jsi_SubstrDup(v, bLen, istart, ilen, &olen);
            Jsi_ValueMakeBlob(interp, ret, (uchar*)ostr, olen);
        } else {
            Jsi_UtfSubstr(v, istart, ilen, &dStr);
            Jsi_ValueFromDS(interp, &dStr, ret);
        }
    }
    return JSI_OK;
}

Project: jsish

static Jsi_Db *dbGetDbHandle(Jsi_Interp *interp, Jsi_Value *_this, Jsi_Func *funcPtr)
{
    Jsi_Db *jdb = (Jsi_Db*)Jsi_UserObjGetData(interp, _this, funcPtr);
    if (!jdb) {
        Jsi_LogError("Sqlite call to a non-sqlite object");
        return NULL;
    }
    if (!jdb->db)
    {
        Jsi_LogError("Sqlite db closed");
        return NULL;
    }
    return jdb;
}

Project: jsish

static int tree_preorder(Jsi_Tree *treePtr, Jsi_TreeEntry *hPtr, Jsi_TreeWalkProc *callback, void *data) {
    uint epoch = treePtr->epoch;
    if (hPtr == NULL) return JSI_OK;
    if (callback(treePtr, hPtr, data) != JSI_OK || epoch != treePtr->epoch)
        return JSI_ERROR;
    if (hPtr->right != NULL) {
        if (tree_preorder(treePtr, hPtr->right, callback, data) != JSI_OK || epoch != treePtr->epoch)
            return JSI_ERROR;
    }
    if (hPtr->left != NULL) {
        if (tree_preorder(treePtr, hPtr->left, callback, data) != JSI_OK || epoch != treePtr->epoch)
            return JSI_ERROR;
    }
    return JSI_OK;
}

Project: jsish

Jsi_Value* Jsi_ValueNewStringKey(Jsi_Interp *interp, const char *s) {
    Jsi_Value *v = Jsi_ValueNew(interp);
    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

void Jsi_EventuallyFree(Jsi_Interp* interp, void *data, Jsi_DeleteProc* proc) {
    Jsi_HashEntry *hPtr = Jsi_HashEntryFind(interp->preserveTbl, data);
    if (!hPtr) {
        (*proc)(interp, data);
        return;
    }
    PreserveData *ptr = (PreserveData*)Jsi_HashValueGet(hPtr);
    assert(ptr && ptr->interp == interp);
    JSI_NOWARN(ptr);
    Jsi_HashEntryDelete(hPtr);
}

Project: jsish

static int dbSqlCollate(
    void *pCtx,
    int nA,
    const void *zA,
    int nB,
    const void *zB
) {
    SqlCollate *p = (SqlCollate *)pCtx;
    Jsi_Interp *interp = p->interp;

    int rc;
    //Jsi_Db *jdb = (Jsi_Db*)cd;
    Jsi_Value *vpargs, *items[3], *ret;

    items[0] = Jsi_ValueNewObj(interp, p->jdb->fobj);
    items[1] = Jsi_ValueMakeStringDup(interp, NULL, (char*)zA);
    items[2] = Jsi_ValueMakeStringDup(interp, NULL, (char*)zB);
    vpargs = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, items, 3, 0));
    ret = Jsi_ValueNew1(interp);
    rc = Jsi_FunctionInvoke(interp, p->zScript, vpargs, &ret, NULL);
    if( JSI_OK!=rc ) {
        //jdb->errCnt++;
        rc = 0;
    } else
        rc = dbGetIntBool(interp, ret);
    Jsi_DecrRefCount(interp, vpargs);
    Jsi_DecrRefCount(interp, ret);
    return rc;
}

Project: jsish

Jsi_RC jsi_ParseTypeCheckStr(Jsi_Interp *interp, const char *str) {
    uint *iptr = (uint*)&interp->typeCheck;
    const char *wcp = str, *wcn = wcp;
    while (wcn && wcp) {
        int isnot = 0;
        if (*wcp == '!') { isnot = 1; wcp++; }
        wcn = Jsi_Strchr(wcp, ',');
        int ti, wlen = (wcn?(wcn-wcp):(int)Jsi_Strlen(wcp));
#define _JSIPARSETYPES(nam, field) \
        if (wlen == (sizeof(#nam)-1) && !Jsi_Strncmp(#nam, wcp, (sizeof(#nam)-1))) { \
            interp->field = (1-isnot); \
            wcp = (wcn?wcn+1:NULL); \
            continue; \
        }
        _JSIPARSETYPES(Debug, logOpts.Debug)
        _JSIPARSETYPES(Trace, logOpts.Trace)
        _JSIPARSETYPES(Test,  logOpts.Test)
        _JSIPARSETYPES(Info, logOpts.Info)
        _JSIPARSETYPES(Warn, logOpts.Warn)
        _JSIPARSETYPES(Error,  logOpts.Error)
        _JSIPARSETYPES(full,  logOpts.full)
        _JSIPARSETYPES(before,  logOpts.before)
        _JSIPARSETYPES(time,  logOpts.time)
        _JSIPARSETYPES(date,  logOpts.date)
        _JSIPARSETYPES(asserts, asserts)
        _JSIPARSETYPES(assert, asserts)
        _JSIPARSETYPES(noproto, subOpts.noproto)

        const char **tstrs = jsi_TypeChkStrs;
        for (ti=0; tstrs[ti]; ti++) {
            wlen = Jsi_Strlen(tstrs[ti]);
            if (!Jsi_Strncmp(tstrs[ti], wcp, wlen) && (!tstrs[ti][wlen] || tstrs[ti][wlen] == ',')) break;
        }
        if (tstrs[ti]) {
            if (isnot)
                *iptr &= ~(1<<ti);
            else {
                *iptr |= (1<<ti);
                if (!Jsi_Strcmp(tstrs[ti], "all"))
                    interp->typeCheck.parse = interp->typeCheck.run = 1;
                if (!Jsi_Strcmp(tstrs[ti], "strict")) {
                    interp->typeCheck.parse = interp->typeCheck.run = interp->typeCheck.all = 1;
                    if (interp->framePtr->level<=0 || interp->isMain)
                        interp->strict = 1;
                }
            }
        } else {
            Jsi_DString wStr = {};
            int i;
            tstrs = jsi_TypeChkStrs;
            for (i=0; tstrs[i]; i++) Jsi_DSAppend(&wStr, i?", ":"", tstrs[i], NULL);
            Jsi_LogWarn("unknown typeCheck warn option(s) \"%s\" not in: Debug, Trace, Test, Info, Warn, Error, assert, %s, noproto, full, before, time, date", str, Jsi_DSValue(&wStr));
            Jsi_DSFree(&wStr);
        }
        wcp = (wcn?wcn+1:NULL);
    }
    return JSI_OK;
}