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

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

Project: jsish

static Jsi_RC jsiValueGetString(Jsi_Interp *interp, Jsi_Value* v, Jsi_DString *dStr, objwalker *owPtr)
{
    char buf[JSI_MAX_NUMBER_STRING], *str;
    Jsi_DString eStr;
    Jsi_DSInit(&eStr);
    if (interp->maxDepth>0 && owPtr->depth > interp->maxDepth)
        return Jsi_LogError("recursive ToString");
    int quote = owPtr->quote;
    int isjson = owPtr->quote&JSI_OUTPUT_JSON;
    Jsi_Number num;
    switch(v->vt) {
        case JSI_VT_UNDEF:
            Jsi_DSAppend(dStr, "undefined", NULL);
            return JSI_OK;
        case JSI_VT_NULL:
            Jsi_DSAppend(dStr, "null", NULL);
            return JSI_OK;
        case JSI_VT_VARIABLE:
            Jsi_DSAppend(dStr, "variable", NULL);
            return JSI_OK;
        case JSI_VT_BOOL:
            Jsi_DSAppend(dStr, (v->d.val ? "true":"false"), NULL);
            return JSI_OK;
        case JSI_VT_NUMBER:
            num = v->d.num;
outnum:
            if (isjson && !Jsi_NumberIsNormal(num)) {
                Jsi_DSAppend(dStr, "null", NULL);
            } else if (Jsi_NumberIsInteger(num)) {
                Jsi_NumberItoA10((Jsi_Wide)num, buf, sizeof(buf));
                Jsi_DSAppend(dStr, buf, NULL);
            } else if (Jsi_NumberIsWide(num)) {
                snprintf(buf, sizeof(buf), "%" PRId64, (Jsi_Wide)num);
                Jsi_DSAppend(dStr, buf, NULL);
            } else if (Jsi_NumberIsNormal(num) || Jsi_NumberIsSubnormal(num)) {
                Jsi_NumberDtoA(interp, num, buf, sizeof(buf), 0);
                Jsi_DSAppend(dStr, buf, NULL);
            } else if (Jsi_NumberIsNaN(num)) {
                Jsi_DSAppend(dStr, "NaN", NULL);
            } else {
                int s = Jsi_NumberIsInfinity(num);
                if (s > 0) Jsi_DSAppend(dStr, "+Infinity", NULL);
                else if (s < 0) Jsi_DSAppend(dStr, "-Infinity", NULL);
                else Jsi_LogBug("Ieee function problem: %d", fpclassify(num));
            }
            return JSI_OK;
        case JSI_VT_STRING:
            str = v->d.s.str;
outstr:
            if (!quote) {
                Jsi_DSAppend(dStr, str, NULL);
                return JSI_OK;
            }
            Jsi_DSAppend(dStr,"\"", NULL);
            while (*str) {
                if ((*str == '\'' && (!isjson)) || *str == '\\'|| *str == '\"'|| (*str == '\n'
                    && (!(owPtr->quote&JSI_OUTPUT_NEWLINES)))
                    || *str == '\r' || *str == '\t' || *str == '\f' || *str == '\b'  ) {
                    char pcp[2];
                    *pcp = *str;
                    pcp[1] = 0;
                    Jsi_DSAppendLen(dStr,"\\", 1);
                    switch (*str) {
                        case '\r': *pcp = 'r'; break;
                        case '\n': *pcp = 'n'; break;
                        case '\t': *pcp = 't'; break;
                        case '\f': *pcp = 'f'; break;
                        case '\b': *pcp = 'b'; break;
                    }
                    Jsi_DSAppendLen(dStr,pcp, 1);
                } else if (isprint(*str) || !isjson)
                    Jsi_DSAppendLen(dStr,str, 1);
                else {
                    char ubuf[10];
                    int l = Jsi_UtfEncode(str, ubuf);
                    Jsi_DSAppend(dStr,ubuf, NULL);
                    str += l-1;
                }
                str++;
            }
            Jsi_DSAppend(dStr,"\"", NULL);
            Jsi_DSFree(&eStr);
            return JSI_OK;
        case JSI_VT_OBJECT: {
            Jsi_Obj *o = v->d.obj;
            switch(o->ot) {
                case JSI_OT_BOOL:
                    Jsi_DSAppend(dStr, (o->d.val ? "true":"false"), NULL);
                    return JSI_OK;
                case JSI_OT_NUMBER:
                    num = o->d.num;
                    goto outnum;
                    return JSI_OK;
                case JSI_OT_STRING:
                    str = o->d.s.str;
                    goto outstr;
                case JSI_OT_FUNCTION:
                    Jsi_FuncObjToString(interp, o->d.fobj->func, &eStr, 3 | ((owPtr->depth==0 && owPtr->quote)?8:0));
                    str = Jsi_DSValue(&eStr);
                    goto outstr;
                case JSI_OT_REGEXP:
                    str = o->d.robj->pattern;
                    goto outstr;
                case JSI_OT_USEROBJ:
                    jsi_UserObjToName(interp, o->d.uobj, &eStr);
                    str = Jsi_DSValue(&eStr);
                    goto outstr;
                case JSI_OT_ITER:
                    Jsi_DSAppend(dStr, (isjson?"null":"*ITER*"), NULL);
                    return JSI_OK;
                default:
                    break;
            }
                        
            if (o->isarrlist)
            {
                Jsi_Value *nv;
                int i, len = o->arrCnt;
                
                if (!o->arr)
                    len = Jsi_ValueGetLength(interp, v);
                Jsi_DSAppend(dStr,"[",len?" ":"", NULL);
                for (i = 0; i < len; ++i) {
                    nv = Jsi_ValueArrayIndex(interp, v, i);
                    if (i) Jsi_DSAppend(dStr,", ", NULL);
                    owPtr->depth++;
                    if (nv) {
                        if (jsiValueGetString(interp, nv, dStr, owPtr) != JSI_OK) {
                            owPtr->depth--;
                            return JSI_ERROR;
                        }
                    }
                    else Jsi_DSAppend(dStr, "undefined", NULL);
                    owPtr->depth--;
                }
                Jsi_DSAppend(dStr,len?" ":"","]", NULL);
            } else {
                int len = Jsi_TreeSize(o->tree);
                Jsi_DSAppend(dStr,"{",len?" ":"", NULL);
                owPtr->depth++;
                Jsi_TreeWalk(o->tree, _object_get_callback, owPtr, 0);
                owPtr->depth--;
                Jsi_DSAppend(dStr,len?" ":"","}", NULL);
            }
            return JSI_OK;
        }
#ifndef __cplusplus
        default:
            Jsi_LogBug("Unexpected value type: %d", v->vt);
#endif
    }
    return JSI_OK;
}

Project: jsish

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

Project: jsish

jsi_HashArrayFind( Jsi_Hash *tablePtr, const void *key)
{
    jsi_Hash hval = jsi_HashArray(key, tablePtr->keyType);
    size_t hindex = hval & tablePtr->mask;
    Jsi_HashEntry *hPtr = tablePtr->buckets[hindex];

    for (; hPtr != NULL; hPtr = hPtr->nextPtr)
        if (hPtr->hval == hval && !memcmp(hPtr->key.string, key, tablePtr->keyType))
            return hPtr;

    return NULL;
}

Project: jsish

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

Project: jsish

char* Jsi_NumberToString(Jsi_Interp *interp, Jsi_Number d, char *buf, int bsiz)
{
     if (Jsi_NumberIsInteger(d)) {
        Jsi_NumberItoA10((Jsi_Wide)d, buf, bsiz);
    } else if (Jsi_NumberIsNormal(d)) {
        Jsi_NumberDtoA(interp, d, buf, bsiz, 0);
    } else if (Jsi_NumberIsNaN(d)) {
        Jsi_Strcpy(buf, "NaN");
    } else {
        int s = Jsi_NumberIsInfinity(d);
        if (s > 0) Jsi_Strcpy(buf,  "Infinity");
        else if (s < 0) Jsi_Strcpy(buf, "-Infinity");
        else {
            buf[0] = 0;
        }
    }
    return buf;
}

Project: jsish

static Jsi_RC SqliteConfCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                         Jsi_Value **ret, Jsi_Func *funcPtr)
{
    Jsi_Db *jdb, ojdb;
    if (!(jdb = dbGetDbHandle(interp, _this, funcPtr))) return JSI_ERROR;
    Jsi_Value *opts = Jsi_ValueArrayIndex(interp, args, 0);
    if (jdb->noConfig && opts && !Jsi_ValueIsString(interp, opts))
        return Jsi_LogError("Socket conf() is disabled for set");
    ojdb = *jdb;
    
    jdb->lastInsertId = sqlite3_last_insert_rowid(jdb->db);
    jdb->changeCnt = sqlite3_changes(jdb->db);
    jdb->changeCntAll = sqlite3_total_changes(jdb->db);
    jdb->errorCode = sqlite3_errcode(jdb->db);
    
    Jsi_RC rc = Jsi_OptionsConf(interp, SqlOptions, jdb, opts, ret, 0);
    
    if (jdb->stmtCacheMax<0 || jdb->stmtCacheMax>MAX_PREPARED_STMTS) {
        JSI_DBQUERY_PRINTF( "option stmtCacheMax value %d is not in range 0..%d", jdb->stmtCacheMax, MAX_PREPARED_STMTS);
        jdb->stmtCacheMax = ojdb.stmtCacheMax;
        rc = JSI_ERROR;
    }
    dbSetupCallbacks(jdb, &ojdb);
    dbPrepStmtLimit(jdb);
    return rc;
}

Project: jsish

void Jsi_MutexDelete(Jsi_Interp *interp, Jsi_Mutex *mtx) { MutexDone(mtx); Jsi_Free(mtx);}

Project: jsish

static Jsi_RC WebSocketSendCmd(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 in a non-websock object");
    WSSIGASSERT(cmdPtr, OBJ);
    jsi_wsPss *pss;
    Jsi_HashEntry *hPtr;
    Jsi_HashSearch cursor;
    Jsi_Value *arg = Jsi_ValueArrayIndex(interp, args, 1);
    char *str = Jsi_ValueString(interp, arg, NULL);
    int id = -1, argc = Jsi_ValueGetLength(interp, args);
    Jsi_DString eStr = {};
    if (argc!=2)
        return Jsi_LogError("wrong args");
    Jsi_Number dnum;
    Jsi_Value *darg = Jsi_ValueArrayIndex(interp, args, 0);
    if (Jsi_ValueGetNumber(interp, darg, &dnum) != JSI_OK)
        return Jsi_LogError("invalid id");
    id = (int)dnum;

    if (!str)
        str = (char*)Jsi_ValueGetDString(interp, arg, &eStr, JSI_OUTPUT_JSON);

    if (cmdPtr->echo)
        Jsi_LogInfo("WS-SEND: %s\n", str);

    for (hPtr = Jsi_HashSearchFirst(cmdPtr->pssTable, &cursor);
        hPtr != NULL; hPtr = Jsi_HashSearchNext(&cursor)) {
        pss = (jsi_wsPss*)Jsi_HashValueGet(hPtr);
        WSSIGASSERT(pss, PWS);
        if ((id<0 || pss->wid == id) && pss->state != PWS_DEAD) {
            if (!pss->stack)
                pss->stack = Jsi_StackNew();
            char *msg = (char*)Jsi_Malloc(LWS_PRE + Jsi_Strlen(str) + 1);
            Jsi_Strcpy(msg + LWS_PRE, str);
            Jsi_StackPush(pss->stack, msg);
            pss->stats.msgQLen++;
            if (!cmdPtr->echo && pss->echo)
                Jsi_LogInfo("WS-SEND: %s\n", str);
        }
    }

    Jsi_DSFree(&eStr);
    return JSI_OK;
}

Project: jsish

static void fileObjErase(FileObj *fo)
{
    if (fo->filename) {
        Jsi_Close(fo->interp, fo->chan);
        Jsi_Free(fo->filename);
        Jsi_DecrRefCount(fo->interp, fo->fname);
        Jsi_Free(fo->mode);
    }
    fo->filename = NULL;
}

Project: jsish

static Jsi_OpCodes *code_reserved(jsi_Pstate *p, jsi_Pline *line, int type, const char *id)
{
    jsi_ReservedInfo *ri = (jsi_ReservedInfo*)Jsi_Calloc(1, sizeof(*ri));
    ri->type = type;
    ri->label = id;
    ri->topop = 0;
    JSI_NEW_CODESLN(1,OP_RESERVED, ri);
}

Project: jsish

Jsi_RC jsi_StaticArgTypeCheck(Jsi_Interp *interp, int atyp, const char *p1, const char *p2, int index, Jsi_Func *func, jsi_Pline *line) {
    Assert(index>0);
    Jsi_RC rc;
    if (interp->typeCheck.parse==0 && interp->typeCheck.all==0)
        return JSI_OK;
    int ai = index-1+func->callflags.bits.addargs;
    if (func->argnames == NULL || ai>=func->argnames->count || ai<0)
        return JSI_OK;
    int typ = func->argnames->args[ai].type;
    if (typ <= 0)
        return JSI_OK;
    if (index == 0 && func && func->type == FC_BUILDIN && 
        interp->typeCheck.all==0) // Normally do not check return types for builtins.
        return JSI_OK; 
    if ((typ&JSI_TT_ANY)) return JSI_OK;
    if (index == 0 && atyp == JSI_VT_UNDEF) {
        if (!(typ&JSI_TT_VOID)) 
            goto done;
        return JSI_OK;
    }
/*    if (index == 0 && (typ&JSI_TT_VOID)) {
        if (atyp != JSI_VT_UNDEF && !(typ&JSI_TT_UNDEFINED))
            goto done;
        return JSI_OK;
    }*/
    if (atyp == JSI_VT_UNDEF)
        return JSI_OK;
    rc = JSI_OK;
    if (typ&JSI_TT_UNDEFINED && atyp == JSI_TT_UNDEFINED) return rc;
    if (typ&JSI_TT_NUMBER && atyp==JSI_TT_NUMBER) return rc;
    if (typ&JSI_TT_STRING && atyp==JSI_TT_STRING) return rc;
    if (typ&JSI_TT_BOOLEAN && atyp==JSI_TT_BOOLEAN)  return rc;
    if (typ&JSI_TT_ARRAY && atyp==JSI_TT_ARRAY)   return rc;
    if (typ&JSI_TT_FUNCTION && atyp==JSI_TT_FUNCTION) return rc;
    if (typ&JSI_TT_REGEXP && atyp==JSI_TT_REGEXP) return rc;
    if (typ&JSI_TT_USEROBJ && atyp==JSI_TT_USEROBJ) return rc;
    if (typ&JSI_TT_ITEROBJ && atyp==JSI_TT_ITEROBJ) return rc;
    if (typ&JSI_TT_OBJECT && atyp==JSI_TT_OBJECT) return rc;
    if (typ&JSI_TT_NULL && atyp==JSI_TT_NULL) return rc;
done:
    {
        Jsi_DString dStr = {};
        const char *exp = jsi_typeName(interp, typ, &dStr);
        const char *vtyp = jsi_TypeName(interp, (Jsi_ttype)atyp);
        char idxBuf[JSI_MAX_NUMBER_STRING*2];
        idxBuf[0] = 0;
        if (index>0)
            snprintf(idxBuf, sizeof(idxBuf), " arg %d", index);
        if (line)
            interp->parseLine = line;
        if (interp->typeCheck.error)
            rc = JSI_ERROR;
        jsi_TypeMismatch(interp);
        Jsi_DString fStr = {};
        rc = Jsi_LogType("type mismatch %s%s '%s': expected \"%s\" but got \"%s\"%s",
            p1, idxBuf, p2, exp, vtyp, jsiFuncInfo(interp, &fStr, func, NULL));
        Jsi_DSFree(&fStr);
        Jsi_DSFree(&dStr);
    }
    return rc;
}

Project: jsish

int Jsi_SetChannelOption(Jsi_Interp *interp, Jsi_Channel chan, const char *optionName,
    const char *newValue) {return JSI_OK;}

Project: jsish

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

Project: jsish

const char *jsiFuncInfo(Jsi_Interp *interp, Jsi_DString *dStr, Jsi_Func* func, Jsi_Value *arg) {
    if (!func) return "";
    if (func->name)
        Jsi_DSPrintf(dStr, ", in call to '%s'", func->name);
    else
        Jsi_DSPrintf(dStr, ", in call to function");
    if (func->script) {
        const char *cp = Jsi_Strrchr(func->script, '/');
        if (cp)
            cp++;
        else
            cp = func->script;
        Jsi_DSPrintf(dStr, " declared at %s:%d.%d", cp, func->bodyline.first_line, func->bodyline.first_column);
    }
        if (arg) {
        Jsi_DSAppend(dStr, " <", NULL);
        Jsi_ValueGetDString(interp, arg, dStr, 0);
        Jsi_DSAppend(dStr, ">.", NULL);
    }
    return Jsi_DSValue(dStr);
}

Project: jsish

static Jsi_RC jsi_wsrecv_flush(jsi_wsCmdObj *cmdPtr, jsi_wsPss *pss)
{
    int nlen = Jsi_DSLength(&pss->recvBuf);
    if (nlen<=0)
        return JSI_OK;
    cmdPtr->recvBufCnt--;
    const char *inPtr = Jsi_DSFreeDup(&pss->recvBuf);
    Jsi_RC rc = jsi_wsrecv_callback(cmdPtr->interp, cmdPtr, pss, inPtr, nlen, 0);
    if (rc != JSI_OK) {
        pss->stats.recvErrCnt++;
        pss->stats.recvErrLast = time(NULL);
    }
    return rc;
}

Project: jsish

Jsi_RC Jsi_EvalCmdJSON(Jsi_Interp *interp, const char *cmd, const char *jsonArgs, Jsi_DString *dStr, int flags)
{
    if (Jsi_MutexLock(interp, interp->Mutex) != JSI_OK)
        return JSI_ERROR;
    Jsi_Value *nrPtr = Jsi_ValueNew1(interp);
    Jsi_RC rc = Jsi_CommandInvokeJSON(interp, cmd, jsonArgs, &nrPtr);
    Jsi_DSInit(dStr);
    Jsi_ValueGetDString(interp, nrPtr, dStr, flags /*JSI_OUTPUT_JSON*/);
    Jsi_DecrRefCount(interp, nrPtr);
    Jsi_MutexUnlock(interp, interp->Mutex);
    return rc;
}

Project: jsish

Jsi_OptionSpecsCached(Jsi_Interp *interp, const Jsi_OptionSpec *staticSpecs)
{
#ifdef NO_CACHED_SPECS
    return (Jsi_OptionSpec*)staticSpecs;
#else
    /* If we aren't master interp, need to cache due to init and modified flags if Jsi_OptionsChanged is called. */
    if (interp->mainInterp == NULL) {
        interp->mainInterp = interp;
    }
    if (interp == interp->mainInterp) {
        return staticSpecs;
    }
    return jsi_GetCachedOptionSpecs(interp, staticSpecs);
#endif
}

Project: jsish

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

Project: jsish

Jsi_RC Jsi_GetDouble(Jsi_Interp* interp, const char *string, Jsi_Number *n)
{
    char *endptr;

    /* Callers can check for underflow via ERANGE */
    errno = 0;

    *n = strtod(string, &endptr);

    return JsiCheckConversion(string, endptr);
}

Project: jsish

static void jsi_DumpCustomSpec(Jsi_Interp *interp, Jsi_Obj *nobj, Jsi_OptionSpec* spec)
{
    Jsi_OptionCustom* cspec = Jsi_OptionCustomBuiltin(spec->custom);
    void *data = spec->data;
    if (cspec->help) {
        if (Jsi_Strchr(cspec->help, '\n'))
            Jsi_LogError("%s .help contains newline: %s", cspec->name, cspec->help);
        Jsi_ObjInsert(interp, nobj, "help", Jsi_ValueNewStringKey(interp, cspec->help),0);
    }
    if (cspec->info)
        Jsi_ObjInsert(interp, nobj, "info", Jsi_ValueNewStringKey(interp, cspec->info),0);
    Jsi_ObjInsert(interp, nobj, "name", Jsi_ValueNewStringKey(interp, cspec->name),0);

    if (data && (spec->custom == Jsi_Opt_SwitchEnum || spec->custom == Jsi_Opt_SwitchBitset)) {
        Jsi_Obj *sobj = Jsi_ObjNewType(interp, JSI_OT_ARRAY);
        Jsi_Value *svalue = Jsi_ValueMakeObject(interp, NULL, sobj);
        const char **lst = jsi_OptGetEnumList(spec);
        int i = 0;
        while (lst[i]) {
            Jsi_ObjArrayAdd(interp, sobj, Jsi_ValueNewStringKey(interp, lst[i]));
            i++;
        }
        Jsi_ObjInsert(interp, nobj, (spec->custom == Jsi_Opt_SwitchBitset?"bitSet":"enumList"), svalue, 0);
    } else if (spec->custom == Jsi_Opt_SwitchSuboption) {
        Jsi_OptionSpec* subSpec = (Jsi_OptionSpec*)data;
        Jsi_Obj *sobj = Jsi_ObjNewType(interp, JSI_OT_ARRAY);
        Jsi_Value *svalue = Jsi_ValueMakeObject(interp, NULL, sobj);
        jsi_DumpOptionSpecs(interp, sobj, subSpec);
        Jsi_ObjInsert(interp, nobj, "subSpec", svalue, 0);
    }
}

Project: jsish

const char *jsi_GetHomeDir(Jsi_Interp *interp) {
    const char *str = NULL;
    if (interp->homeDir)
        return interp->homeDir;
#ifdef __WIN32
    str = getenv("USERPROFILE"); /* TODO: windows home dir. */
#else
        
    if ((str = getenv("HOME")) == NULL) {
        struct passwd pwd, *pw;
        char buf[JSI_BUFSIZ*3];
        if (getpwuid_r(getuid(), &pwd, buf, sizeof(buf), &pw) == 0 && pw->pw_dir)        
            str = pw->pw_dir;
    }
#endif
    if (!str) {
        Jsi_LogBug("no home dir");
        str = "/";
    }
#ifdef JSI_LITE_ONLY
    return str;
#else
    return (interp->homeDir = Jsi_KeyAdd(interp, str));
#endif
}

Project: jsish

static Jsi_ScopeStrs *jsi_ScopeStrsDup(jsi_Pstate *ps, Jsi_ScopeStrs *ss)
{
    Jsi_ScopeStrs *n = jsi_ScopeStrsNew();
    int i;
    if (!ss) return n;
    *n = *ss;
    if (!ss->args) return n;
    n->args = (jsi_ArgValue*)Jsi_Calloc(n->count, sizeof(ss->args[0]));
    n->_size = n->count;
    memcpy(n->args, ss->args, (n->count *  sizeof(ss->args[0])));
    for (i = 0; i < ss->count; ++i) {
        if (ss->args[i].defValue)
            Jsi_IncrRefCount(ps->interp, ss->args[i].defValue);
    }
    return n;
}

Project: jsish

static Jsi_RC interpObjFree(Jsi_Interp *interp, void *data)
{
    InterpObj *fo = (InterpObj *)data;
    SIGASSERT(fo,INTERPOBJ);
    if (fo->deleting) return JSI_OK;
    fo->deleting = 1;
    interpObjErase(fo);
    Jsi_Free(fo);
    return JSI_OK;
}

Project: jsish

Jsi_RC Jsi_GetInt(Jsi_Interp* interp, const char *string, int *n, int base)
{
    char *endptr;
    if (base) {
        *n = strtoul(string, &endptr, base);
    }
    else {
        *n = (int)jsi_strtoul(string, &endptr);
    }
    return JsiCheckConversion(string, endptr);
}

Project: jsish

static Jsi_RC jsi_ValueToParentFunc(Jsi_Interp *interp, Jsi_OptionSpec* spec, Jsi_Value *inValue, const char *inStr, void *record, Jsi_Wide flags)
{
    if (inStr)
        return JSI_ERROR;
    Jsi_Value *val;
    Jsi_Value **v = (Jsi_Value **)(((char*)record) + spec->offset);
    const char *s, *subspec = (const char *)spec->data;
    Jsi_Interp *pinterp = interp->parent;
    if (!pinterp)
        return Jsi_LogError("no parent interp");
    if (!subspec) 
        return Jsi_LogError("custom parentfunc spec did not set data: %s", spec->name);
    if (!inValue || Jsi_ValueIsNull(interp, inValue)) {
        if (*v) Jsi_DecrRefCount(pinterp, *v);
        *v = NULL;
        return JSI_OK;
    }
    if (!(s=Jsi_ValueString(interp, inValue, NULL))) {
        return Jsi_LogError("expected string or null");
    }
    val = Jsi_NameLookup(pinterp, s);
    if (!val)
        return Jsi_LogError("value not found in parent: %s", s);
    if (!Jsi_ValueIsFunction(pinterp, val))
        return Jsi_LogError("expected a func value");
    if (spec->data && (interp->strict || pinterp->strict))
        if (!jsi_FuncIsNoop(pinterp, val)
            && !jsi_FuncArgCheck(pinterp, val->d.obj->d.fobj->func, (char*)spec->data)) 
            return Jsi_LogError("failed setting func pointer for %s", spec->name);

    *v = val;
    Jsi_IncrRefCount(pinterp, val);
    return JSI_OK;
}

Project: jsish

static Jsi_RC jsi_ValueToSubopt(Jsi_Interp *interp, Jsi_OptionSpec* spec, Jsi_Value *inValue, const char *inStr, void *record, Jsi_Wide flags)
{
    if (inStr)
        return JSI_ERROR;
    char *s = ((char*)record) + spec->offset;
    Jsi_OptionSpec *subspec = (Jsi_OptionSpec *)spec->data;
  
    if (spec == subspec) 
        return Jsi_LogError("subspec was recursive");
    if (!subspec) 
        return Jsi_LogError("custom suboption spec did not set data: %s", spec->name);
    if (inValue && Jsi_ValueIsNull(interp, inValue) == 0 &&
        (Jsi_ValueIsObjType(interp, inValue, JSI_OT_OBJECT)==0 || inValue->d.obj->isarrlist)) 
        return Jsi_LogError("expected object");
    return (Jsi_OptionsProcess(interp, subspec, s, inValue, flags)<0 ? JSI_ERROR : JSI_OK);
}

Project: jsish

Jsi_Value* Jsi_ValueMakeNumber(Jsi_Interp *interp, Jsi_Value **vPtr, Jsi_Number n) {
    Jsi_Value *v = (vPtr?*vPtr:NULL);
    if (!v)
        v = Jsi_ValueNew(interp);
    else
        Jsi_ValueReset(interp, vPtr);
    v->vt = JSI_VT_NUMBER;
    v->d.num = n;
    return v;
}

Project: jsish

static void DbClose(sqlite3 *db) {
        sqlite3_close(db);
}

Project: jsish

static Jsi_RC InterpEvalCmd_(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr, int flags)
{
    int isFile = flags&2;
    int isUplevel = flags&1;
    int lev = 0;
    bool async = 0;
    Jsi_RC rc = JSI_OK;
    int isthrd;
    Jsi_Interp *sinterp = interp;
    Jsi_ValueMakeUndef(interp, ret);
    InterpObj *udf = (InterpObj *)Jsi_UserObjGetData(interp, _this, funcPtr);
    if (udf)
        sinterp = udf->subinterp;
    if (Jsi_InterpGone(interp) || Jsi_InterpGone(sinterp))
        return Jsi_LogError("Sub-interp gone");
    isthrd = (interp->threadId != sinterp->threadId);
    jsi_Frame *f = sinterp->framePtr;
    Jsi_Value *nw = Jsi_ValueArrayIndex(interp, args, 1);
    if (!isUplevel) {
        if (nw && Jsi_GetBoolFromValue(interp, nw, &async))
            return JSI_ERROR;
    } else {
        if (isthrd)
            return Jsi_LogError("can not use uplevel() with threaded interp");
        Jsi_Number nlev = sinterp->framePtr->level;
        if (nw && Jsi_GetNumberFromValue(interp, nw, &nlev)!=JSI_OK)
            return Jsi_LogError("expected number");
        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);
    }

    char *cp = Jsi_ValueArrayIndexToStr(interp, args, 0, NULL);
    if (cp==NULL || *cp == 0)
        return JSI_OK;
    if (async && isthrd) {
        /* Post to thread event in sub-interps queue. TODO: could just use event like below... */
        if (Jsi_MutexLock(interp, sinterp->QMutex) != JSI_OK)
            return JSI_ERROR;
        Jsi_DSAppend(&sinterp->interpEvalQ, Jsi_Strlen(Jsi_DSValue(&sinterp->interpEvalQ))?";":"", cp, NULL);
        Jsi_MutexUnlock(interp, sinterp->QMutex);
        return JSI_OK;
    }
    if (interp->subOpts.mutexUnlock) Jsi_MutexUnlock(interp, interp->Mutex);
    if (!isthrd) {
        int ostrict = sinterp->strict;
        sinterp->strict = 0;
        sinterp->level++;
        if (interp->framePtr->tryDepth)
            sinterp->framePtr->tryDepth++;
        if (isFile) {
            int sflags = 0;
            if (!sinterp->includeCnt) {
                sflags = JSI_EVAL_ARGV0|JSI_EVAL_AUTOINDEX;
                sinterp->isMain = 1;
            }
            if (sinterp->debugOpts.debugCallback && !sinterp->includeCnt)  // TODO: safe debugging can't use "source"
                // TODO: we do this in debugger, even though it is illegal for interps to share objects.
                sinterp->autoFiles = Jsi_ValueDup(sinterp, interp->autoFiles);
            sinterp->includeCnt++;
            rc = Jsi_EvalFile(sinterp, Jsi_ValueArrayIndex(interp, args, 0), sflags);
        } else if (isUplevel == 0 || lev <= 1)
            rc = (Jsi_EvalString(sinterp, cp, 0) == 0 ? JSI_OK : JSI_ERROR);
        else {
            rc = (jsi_evalStrFile(sinterp, NULL, cp, 0, lev) == 0 ? JSI_OK : JSI_ERROR);
        }
        sinterp->strict = ostrict;
        if (interp->framePtr->tryDepth) {
            sinterp->framePtr->tryDepth--;
            if (rc != JSI_OK && interp != sinterp) {
                Jsi_Strcpy(interp->errMsgBuf, sinterp->errMsgBuf);
                interp->errLine = sinterp->errLine;
                interp->errFile = sinterp->errFile;
                sinterp->errMsgBuf[0] = 0;
            }
        }
        sinterp->level--;
    } else {
        if (Jsi_MutexLock(interp, sinterp->QMutex) != JSI_OK)
            return JSI_ERROR;
        InterpStrEvent *se, *s = (InterpStrEvent *)Jsi_Calloc(1, sizeof(*s));
        SIGINIT(s,INTERPSTREVENT);
        s->isExec = 1;
        s->tryDepth = interp->framePtr->tryDepth;
        Jsi_DSInit(&s->data);
        Jsi_DSAppend(&s->data, cp, NULL);
        Jsi_DSInit(&s->func);
        //s->mutex = Jsi_MutexNew(interp, -1, JSI_MUTEX_RECURSIVE);
        //Jsi_MutexLock(s->mutex);
        se = sinterp->interpStrEvents;
        if (!se)
            sinterp->interpStrEvents = s;
        else {
            while (se->next)
                se = se->next;
            se->next = s;
        }

        Jsi_MutexUnlock(interp, sinterp->QMutex);
        while (s->isExec)      /* Wait until done. TODO: timeout??? */
            Jsi_Sleep(interp, 1);
        rc = (s->rc == 0 ? JSI_OK : JSI_ERROR);
        if (rc != JSI_OK)
            Jsi_LogError("eval failed: %s", Jsi_DSValue(&s->data));
        Jsi_DSFree(&s->func);
        Jsi_DSFree(&s->data);
        Jsi_Free(s);
    }

    if (interp->subOpts.mutexUnlock && Jsi_MutexLock(interp, interp->Mutex) != JSI_OK) {
        return JSI_ERROR;
    }

    if (Jsi_InterpGone(sinterp))
    {
        /* TODO: perhaps exit() be able to delete. */
        //Jsi_InterpDelete(sinterp);
        return JSI_OK;
    }
    /*if (rc != JSI_OK && !async)
        return rc;*/
    if (sinterp->retValue->vt != JSI_VT_UNDEF) {
        if (sinterp == interp)
            Jsi_ValueCopy(interp, *ret, sinterp->retValue);
        else
            Jsi_CleanValue(sinterp, interp, sinterp->retValue, ret);
    }
    return rc;
}

Project: jsish

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

Project: jsish

void Jsi_NumberDtoA(Jsi_Interp *interp, Jsi_Number value, char* buf, int bsiz, int prec)
{
    int dp = interp->subOpts.dblPrec-1, dm = __DBL_DECIMAL_DIG__;
    if (prec==0)
        prec = (dp<=0?dm+dp:dp);
    else if (prec<0)
            prec = dm+prec;
    if (prec<=0)
        prec = dm-1;
    if (Jsi_NumberIsNaN(value))
        Jsi_Strcpy(buf,"NaN");
    else
        snprintf(buf, bsiz, "%.*" JSI_NUMGFMT, prec, value);
}

Project: jsish

int Jsi_AddAutoFiles(Jsi_Interp *interp, const char *dir) {
    Jsi_DString dStr = {};
    Jsi_StatBuf stat;
    int i, cnt = 0;
    for (i=0; i<2; i++) {
        Jsi_DSAppend(&dStr, dir, (i==0?"/lib":""),"/autoload.jsi", NULL);
        Jsi_Value *v = Jsi_ValueNewStringKey(interp, Jsi_DSValue(&dStr));
        if (Jsi_Stat(interp, v, &stat) != 0)
            Jsi_ValueFree(interp, v);
        else {
            if (!interp->autoFiles) {
                interp->autoFiles = Jsi_ValueNewArray(interp, 0, 0);
                Jsi_IncrRefCount(interp, interp->autoFiles);
            }
            Jsi_ObjArrayAdd(interp, interp->autoFiles->d.obj, v);
            cnt++;
            interp->autoLoaded = 0;
        }
        Jsi_DSSetLength(&dStr, 0);
    }
    Jsi_DSFree(&dStr);
    return cnt;
}

Project: jsish

static Jsi_RC SqliteCollateCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                            Jsi_Value **ret, Jsi_Func *funcPtr)
{
    Jsi_Db *jdb;
    Jsi_Value *func;

    SqlCollate *pCollate;
    char *zName;
    if (!(jdb = dbGetDbHandle(interp, _this, funcPtr))) return JSI_ERROR;

    zName = Jsi_ValueArrayIndexToStr(interp, args, 0, NULL);
    func = Jsi_ValueArrayIndex(interp, args, 1);
    pCollate = (SqlCollate*)Jsi_Calloc(1, sizeof(*pCollate));
    if( pCollate==0 ) return JSI_ERROR;
    pCollate->jdb = jdb;
    pCollate->interp = interp;
    pCollate->pNext = jdb->pCollate;
    pCollate->zScript = func; /*(char*)&pCollate[1];*/
    jdb->pCollate = pCollate;

    if( sqlite3_create_collation(jdb->db, zName, SQLITE_UTF8, pCollate, dbSqlCollate) )
    
        return Jsi_LogError("%s", (char *)sqlite3_errmsg(jdb->db));
    return JSI_OK;
}

Project: jsish

Jsi_Value *Jsi_VarLookup(Jsi_Interp *interp, const char *varname)
{
    Jsi_Value *v;
    v = Jsi_ValueObjLookup(interp, interp->framePtr->incsc, (char*)varname, 0);
    if (!v)
        v = jsi_ScopeChainObjLookupUni(interp->framePtr->ingsc, (char*)varname);
    return v;
}

Project: jsish

Jsi_RC jsi_InitTree(Jsi_Interp *interp, int release)
{
    if (release) return JSI_OK;
    /* TODO: maintain hash table of trees created per interp? */
    return JSI_OK;
}

Project: jsish

static Jsi_Value * jsi_FSListVolumesProc(Jsi_Interp *interp) {return 0;}

Project: jsish

static Jsi_RC SqliteFilenameCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                             Jsi_Value **ret, Jsi_Func *funcPtr)
{
#if (SQLITE_VERSION_NUMBER>3007009)
    const char *zName = "main";
    int argc = Jsi_ValueGetLength(interp, args);
    Jsi_Db *jdb;

    if (!(jdb = dbGetDbHandle(interp, _this, funcPtr))) return JSI_ERROR;
    if (argc)
        zName = Jsi_ValueArrayIndexToStr(interp, args, 0, NULL);
    zName = sqlite3_db_filename(jdb->db, zName);
    if (zName)
        Jsi_ValueMakeStringDup(interp, ret, zName);
#endif
    return JSI_OK;
}

Project: jsish

static Jsi_RC dbTransPostCmd(
    Jsi_Db *jdb,                       /* Sqlite3Db for $db */
    Jsi_Interp *interp,                  /* Jsi interpreter */
    Jsi_RC result                           /* Result of evaluating SCRIPT */
) {
    static const char *azEnd[] = {
        "RELEASE _jsi_transaction",        /* rc==JSI_ERROR, nTransaction!=0 */
        "COMMIT",                          /* rc!=JSI_ERROR, nTransaction==0 */
        "ROLLBACK TO _jsi_transaction ; RELEASE _jsi_transaction",
        "ROLLBACK"                         /* rc==JSI_ERROR, nTransaction==0 */
    };
    Jsi_RC rc = result;
    const char *zEnd;

    jdb->nTransaction--;
    zEnd = azEnd[(rc==JSI_ERROR)*2 + (jdb->nTransaction==0)];

    jdb->disableAuth++;
    if( sqlite3_exec(jdb->db, zEnd, 0, 0, 0)) {
        /* This is a tricky scenario to handle. The most likely cause of an
        ** error is that the exec() above was an attempt to commit the
        ** top-level transaction that returned SQLITE_BUSY. Or, less likely,
        ** that an IO-error has occured. In either case, throw a Jsi exception
        ** and try to rollback the transaction.
        **
        ** But it could also be that the user executed one or more BEGIN,
        ** COMMIT, SAVEPOINT, RELEASE or ROLLBACK commands that are confusing
        ** this method's logic. Not clear how this would be best handled.
        */
        if( rc!=JSI_ERROR ) {
            Jsi_LogError("%s", sqlite3_errmsg(jdb->db));
            rc = JSI_ERROR;
        }
        sqlite3_exec(jdb->db, "ROLLBACK", 0, 0, 0);
    }
    jdb->disableAuth--;

    return rc;
}

Project: jsish

static Jsi_RC dbEvalInit(
    Jsi_Interp *interp,
    DbEvalContext *p,               /* Pointer to structure to initialize */
    Jsi_Db *jdb,                  /* Database handle */
    const char* zSql,                /* Value containing SQL script */
    Jsi_DString *dStr,
    Jsi_Obj *pArray,                /* Name of Jsi array to set (*) element of */
    Jsi_Obj *pValVar                  /* Name element in array for list. */
) {
    p->dSql = dStr;
    p->zSql = Jsi_DSAppend(p->dSql, zSql?zSql:"", NULL);
    p->jdb = jdb;
    return JSI_OK;
}

Project: jsish

Jsi_DbQuery(Jsi_Db *jdb, Jsi_CDataDb *dPtr, const char *query)
{
    if (!jsi_dbVfsPtr) {
        printf( "Sqlite unsupported\n");
        return -1;
    }
    return jsi_dbVfsPtr->dbcQuery(jdb, dPtr, query);
}

Project: jsish

static Jsi_OpCodes *code_jtrue_np(int off) { JSI_NEW_CODES(0,OP_JTRUE_NP, off); }

Project: jsish

const char* Jsi_KeyAdd(Jsi_Interp *interp, const char *str)
{
    Jsi_MapEntry *hPtr;
    bool isNew;
    hPtr = Jsi_MapEntryNew(interp->strKeyTbl, str, &isNew);
    assert(hPtr) ;
    return (const char*)Jsi_MapKeyGet(hPtr, 0);
}

Project: jsish

static int codes_insert(Jsi_OpCodes *c, jsi_Eopcode code, void *extra, int doalloc)
{
    if (c->code_size - c->code_len <= 0) {
        c->code_size += 100;
        c->codes = (jsi_OpCode *)Jsi_Realloc(c->codes, c->code_size * sizeof(jsi_OpCode));
    }
    c->codes[c->code_len].op = code;
    c->codes[c->code_len].data = extra;
    c->codes[c->code_len].alloc = doalloc;
    c->code_len ++;
    return 0;
}

Project: jsish

Jsi_DbQuery(Jsi_Db *jdb, Jsi_CDataDb *dbopts, const char *query)
{
    int rc = jsi_DbQuery(jdb, dbopts, query);
#ifdef JSI_DBQUERY_ERRORCMD
    if (rc<0)
        rc = JSI_DBQUERY_ERRORCMD(jdb, specs, data, arrSize, query, dopts, rc);
#endif
    return rc;
}

Project: jsish

Jsi_TreeEntry *Jsi_TreeEntryNew(Jsi_Tree *treePtr, const void *key, bool *isNew)
{
    Jsi_TreeEntry* hPtr;
    bool isn;
    if (treePtr->flags.destroyed)
        return NULL;
    if (treePtr->opts.lockTreeProc && (*treePtr->opts.lockTreeProc)(treePtr, 1) != JSI_OK)
        return NULL;
    treePtr->flags.inserting=1;
    if (treePtr->flags.internstr) {
        Assert(treePtr->keyType == JSI_KEYS_STRINGKEY);
        if (!treePtr->strHash)
            treePtr->strHash = Jsi_HashNew(treePtr->opts.interp, JSI_KEYS_STRING, NULL);
        key = Jsi_HashEntryNew(treePtr->strHash, key, NULL);
    }
    hPtr = treePtr->createProc(treePtr, key, &isn);
    if (isNew)
        *isNew = isn;
    if (isn == 0 || treePtr->flags.nonredblack == 1 || !hPtr) {
        treePtr->flags.inserting=0;
        goto done;
    }
    treePtr->epoch++;
    hPtr->f.bits.color = _JSI_TREE_RED;
    if (treePtr->root == NULL) {
        treePtr->root = hPtr;
    } else {
        Jsi_TreeEntry* n = treePtr->root;
        while (1) {
            int rc = treePtr->opts.compareTreeProc(treePtr, Jsi_TreeKeyGet(n) , key);
            if (rc == 0) {
                Assert(0);
            } else if (rc < 0) {
                if (n->left == NULL) {
                    n->left = hPtr;
                    break;
                } else {
                    n = n->left;
                }
            } else {
                if (n->right == NULL) {
                    n->right = hPtr;
                    break;
                } else {
                    n = n->right;
                }
            }
        }
        hPtr->parent = n;
    }
    insert_case1(hPtr);
    treePtr->flags.inserting = 0;
done:
    if (treePtr->opts.lockTreeProc)
        (*treePtr->opts.lockTreeProc)(treePtr, 0);
    return hPtr;
}

Project: jsish

static Jsi_RC jsi_wsFileAdd(Jsi_Interp *interp, jsi_wsCmdObj *cmdPtr, Jsi_Value *name) {
    const char *sname = Jsi_ValueString(interp, name, NULL);
    if (cmdPtr->onModify && sname) {
        bool isNew = 0;
        Jsi_HashEntry *hPtr = Jsi_HashEntryNew(cmdPtr->fileHash, sname, &isNew);
        if (hPtr) {
            jsi_wsFile* fPtr;
            if (!isNew)
                fPtr = Jsi_HashValueGet(hPtr);
            else {
                fPtr = (jsi_wsFile *)Jsi_Calloc(1, sizeof(*fPtr));
                fPtr->fileVal = name;
                fPtr->loadFirst = time(NULL);
                Jsi_IncrRefCount(interp, name);
                fPtr->flags = 0;
                Jsi_HashValueSet(hPtr, fPtr);
            }
            fPtr->loadLast = time(NULL);
        }
    }
    return JSI_OK;
}

Project: jsish

void jsi_AddEventHandler(Jsi_Interp *interp)
{
    Jsi_Event *ev;
    while (!interp->EventHdlId) { /* Find an empty event slot. */
        bool isNew;
        uintptr_t id = ++interp->eventIdx;
        Jsi_HashEntry *hPtr = Jsi_HashEntryNew(interp->eventTbl, (void*)id, &isNew);
        if (!isNew)
            continue;
        ev = (Jsi_Event*)Jsi_Calloc(1, sizeof(*ev));
        SIGINIT(ev,EVENT);
        ev->id = id;
        ev->handler = ThreadEvalCallback;
        ev->hPtr = hPtr;
        ev->evType = JSI_EVENT_ALWAYS;
        Jsi_HashValueSet(hPtr, ev);
        interp->EventHdlId = id;
    }
}

Project: jsish

Jsi_HashNew(Jsi_Interp *interp, unsigned int keyType, Jsi_HashDeleteProc freeProc)
{
    Jsi_Hash *tablePtr = (Jsi_Hash*)Jsi_Calloc(1,sizeof(*tablePtr));
    SIGINIT(tablePtr, HASH);
    tablePtr->opts.mapType = (Jsi_Map_Type)JSI_MAP_HASH;
    tablePtr->typ = JSI_MAP_HASH;
    tablePtr->opts.interp = interp;
    tablePtr->buckets = tablePtr->staticBuckets;
#if !(JSI_SMALL_HASH_TABLE && !(JSI_SMALL_HASH_TABLE & (JSI_SMALL_HASH_TABLE - 1)))
#error "small hash must be a power of two"
#endif
    tablePtr->numBuckets = JSI_SMALL_HASH_TABLE;
    tablePtr->rebuildSize = JSI_SMALL_HASH_TABLE * REBUILD_MULTIPLIER;
    tablePtr->downShift = DOWNSHIFT_START;
    tablePtr->opts.freeHashProc = freeProc;
    tablePtr->mask = (jsi_Hash)(tablePtr->numBuckets - 1);
    tablePtr->opts.keyType = (Jsi_Key_Type)keyType;
    tablePtr->keyType = (Jsi_Key_Type)keyType;

    switch (keyType) {
    case JSI_KEYS_STRING:   /* NUL terminated string keys. */
        tablePtr->findProc = HashStringFind;
        tablePtr->createProc = HashStringCreate;
        break;

    case JSI_KEYS_STRINGKEY: /* Lookup from another String hash, eg. Jsi_KeyAdd() */
    case JSI_KEYS_ONEWORD: /* A pointer. */
        tablePtr->findProc = HashOneWordFind;
        tablePtr->createProc = HashOneWordCreate;
        break;

    default:            /* Structs. */
        if (keyType < JSI_KEYS_STRUCT_MINSIZE) {
            Jsi_LogError("Jsi_HashNew: Key size can't be %d, must be >= %d", keyType, JSI_KEYS_STRUCT_MINSIZE);
            Jsi_Free(tablePtr);
            return NULL;
        }
        tablePtr->findProc = jsi_HashArrayFind;
        tablePtr->createProc = jsi_HashArrayCreate;
        break;
    }
    return tablePtr;
}

Project: jsish

Jsi_RC Jsi_initSqlite(Jsi_Interp *interp, int release)
{
    if (!release) Jsi_InterpSetData(interp, JSI_SQLITE_DB_VFS, &jsi_dbVfsPtr, NULL);
    return JSI_OK;
}

Project: jsish

static void jsi_ValueCopyMove(Jsi_Interp *interp, Jsi_Value *to, Jsi_Value *from, int isCopy )  {
    if (!from) {
        Jsi_ValueMakeUndef(interp, &to);
        return;
    }
    VALCHK(from);
    if( to == from) return;
    int ocnt = to->refCnt;
    Jsi_Value *ovt = NULL;
    assert(ocnt>0);
    int toVt = to->vt;
    if (toVt == JSI_VT_VARIABLE) {
        ovt = to->d.lval;
        Jsi_IncrRefCount(interp, ovt);
    }
    Jsi_ValueMakeUndef(interp, &to);
#ifdef JSI_MEM_DEBUG
    memcpy(to, from, sizeof(*to)-sizeof(to->VD));
    to->VD.label3 = from->VD.func;
#else
    *to = *from;
#endif
    if (isCopy) {
        if (to->refCnt) {
            switch (to->vt) {
                case JSI_VT_STRING:
                    if (!to->f.bits.isstrkey) {
                        to->d.s.str = Jsi_StrdupLen(to->d.s.str, to->d.s.len);
                    }
                    break;
                case JSI_VT_OBJECT:
                    Jsi_ObjIncrRefCount(interp,to->d.obj);
                    break;
                case JSI_VT_VARIABLE:
                    Jsi_IncrRefCount(interp,to->d.lval);
                    break;
                default:
                    break;
            }
        }
        to->refCnt = ocnt;
        if (ovt)
            Jsi_DecrRefCount(interp, ovt);
    } else {
        to->refCnt = ocnt;
        if (ovt)
            Jsi_DecrRefCount(interp, ovt);
        ocnt = from->refCnt;
#ifdef JSI_MEM_DEBUG
        memset(from, 0, sizeof(*to)-sizeof(to->VD));
#else
        memset(from, 0, sizeof(*to));
#endif
        SIGINIT(from, VALUE);
        from->refCnt = ocnt;
    }
}

Project: jsish

static int jsi_wsHttp(Jsi_Interp *interp, jsi_wsCmdObj *cmdPtr, struct lws *wsi, void *user,
    struct lws_context *context, const char* inPtr, Jsi_DString *tStr, jsi_wsPss *pss)
{
    const char *ext = NULL;
    unsigned char buffer[JSI_BUFSIZ];
    const char *mime = NULL;
    time_t now = time(NULL);
    char buf[JSI_BUFSIZ];
    int rc = 0;
    buf[0] = 0;
    uchar *p = buffer, *end = &buffer[sizeof(buffer)-1];
    int n;
    Jsi_Value* fname = NULL;
    bool isJsiWeb = 0, isSSI = 0;
    cmdPtr->stats.httpLast = now;
    
    /* if a legal POST URL, let it continue and accept data */
    if (lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI))
        return 0;
    if (!pss)
        pss = jsi_wsgetPss(cmdPtr, wsi, user, 1, 1);

    int uplen=(cmdPtr->urlPrefix?Jsi_Strlen(cmdPtr->urlPrefix):0);

    if (inPtr && cmdPtr->urlPrefix && !Jsi_Strncmp(inPtr, cmdPtr->urlPrefix, uplen))
        inPtr += uplen;

    if (cmdPtr->redirDisable) {// Try to defray redirect loops.
        if (difftime(now, cmdPtr->stats.redirLast)>=600)
            cmdPtr->redirDisable = 0;
        else
            cmdPtr->redirDisable--;
    }

    if ((cmdPtr->urlRedirect && (inPtr == 0 || *inPtr == 0 || !Jsi_Strcmp(inPtr, "/")) && !cmdPtr->redirDisable)
        && (inPtr = cmdPtr->urlRedirect) && inPtr[0]) {
        cmdPtr->stats.redirCnt++;
        // TODO: system time change can disrupt the following.
        if (cmdPtr->redirMax>0 && !cmdPtr->redirDisable && cmdPtr->redirMax>0 && cmdPtr->stats.redirLast
            && difftime(now, cmdPtr->stats.redirLast)<600 && ++cmdPtr->redirAllCnt>cmdPtr->redirMax)
            cmdPtr->redirDisable = 100;
        cmdPtr->stats.redirLast = now;
        rc = lws_http_redirect(wsi, 301, (uchar*)inPtr, Jsi_Strlen(inPtr), &p, end);
        return (rc == 100 ? 0 : 1);
    }
    if (!inPtr || !*inPtr)
        inPtr = "/";

    if (cmdPtr->useridPass || cmdPtr->onAuth) {
        int ok = 0;
        int alen;
        const char *auth = jsi_wsHeader(pss, "authorization", &alen);
        if (auth && !Jsi_Strncasecmp(auth, "basic ", 6) && !cmdPtr->deleted) {
            auth += 6;
            Jsi_DString eStr = {}, bStr = {};
            Jsi_DSAppendLen(&eStr, auth, alen - 6);
            Jsi_Base64(Jsi_DSValue(&eStr), -1, &bStr, 1);
            const char *bp = Jsi_DSValue(&bStr);
            if (bp && bp[0]) {
                if (!cmdPtr->onAuth)
                    ok = (!Jsi_Strcmp(cmdPtr->useridPass, bp));
                else {
                    /* Pass 4 args: ws, id, url and userid:pass . */
                    Jsi_Obj *oarg1;
                    Jsi_Value *vpargs, *vargs[10];
                    int n = 0;
                    vargs[n++] = Jsi_ValueNewObj(interp, cmdPtr->fobj);
                    vargs[n++] = Jsi_ValueNewNumber(interp, (Jsi_Number)(pss->wid));
                    vargs[n++] = Jsi_ValueNewStringDup(interp, inPtr);
                    vargs[n++] = Jsi_ValueNewStringDup(interp, bp);
                    vpargs = Jsi_ValueMakeObject(interp, NULL, oarg1 = Jsi_ObjNewArray(interp, vargs, n, 0));
                    Jsi_IncrRefCount(interp, vpargs);
                    Jsi_Value *ret = Jsi_ValueNew1(interp);
                    bool rb = 0;
                    rc = Jsi_FunctionInvoke(interp, cmdPtr->onAuth, vpargs, &ret, NULL);
                    if (rc == JSI_OK)
                        rb = !Jsi_ValueIsFalse(interp, ret);

                    Jsi_DecrRefCount(interp, vpargs);
                    Jsi_DecrRefCount(interp, ret);

                    if (rc != JSI_OK) {
                        Jsi_LogError("websock bad rcv eval");
                        return -1;
                    }
                    ok = rb;
                }
            }
            Jsi_DSFree(&eStr);
            Jsi_DSFree(&bStr);
        }
        if (!ok) {
            const char *realm = (cmdPtr->realm?cmdPtr->realm:"jsish");
            int n = snprintf(buf, sizeof(buf), "Basic realm=\"%s\"", realm);
            if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_WWW_AUTHENTICATE,
                    (unsigned char *)buf, n, &p, end))
                return -1;
            if (jsi_wsServeString(pss, wsi, "Password is required to access this page", 401, (char*)buffer, NULL)<0)
                return -1;
            return lws_http_transaction_completed(wsi);
        }
    }

    if (cmdPtr->onGet || pss->onGet) {
        Jsi_RC jrc;
        int rrv = 1;
        if (cmdPtr->getRegexp) {
            rrv = 0;
            jrc = Jsi_RegExpMatch(interp, cmdPtr->getRegexp, inPtr, &rrv, NULL);
            if (jrc != JSI_OK)
                return -1; // Error in regexp.
        }
        if (rrv) {
            jrc = jsi_wsGetCmd(interp, cmdPtr, pss, wsi, inPtr, pss->onGet?pss->onGet:cmdPtr->onGet, tStr);
            switch (jrc) {
                case JSI_ERROR: return -1;
                case JSI_OK: return 0;
                case JSI_SIGNAL:
                    return jsi_ws_http_redirect(wsi, 302, tStr, &p, end);
                case JSI_CONTINUE:
                    inPtr = Jsi_DSValue(tStr); break;
                case JSI_BREAK: break;
                default: break;
            }
        }
    }
    ext = Jsi_Strrchr(inPtr, '.');

    Jsi_Value *rdir = (pss->rootdir?pss->rootdir:cmdPtr->rootdir);
    cmdPtr->curRoot = (rdir?Jsi_ValueString(cmdPtr->interp, rdir, NULL):"./");
    Jsi_DString sStr;
    Jsi_DSInit(&sStr);
    jsi_wsPathAlias(interp, cmdPtr, &inPtr, &sStr);

    snprintf(buf, sizeof(buf), "%s/%s", cmdPtr->curRoot, inPtr);
    Jsi_DSFree(&sStr);
    if (cmdPtr->debug>1)
        fprintf(stderr, "FILE: %s in %s | %s\n", buf, cmdPtr->curRoot, Jsi_ValueString(interp, cmdPtr->rootdir, NULL));
    char extBuf[JSI_BUFSIZ], *cpde = Jsi_Strrchr(buf, '/');
    isJsiWeb = (cpde && cmdPtr->jsiFnPattern && Jsi_GlobMatch(cmdPtr->jsiFnPattern, cpde+1, 0));
    bool isgzip = 0;
    if (!ext || !ext[1])
        mime = "text/html";
    else {
        const char *eext = ext+1;
        uint elen = Jsi_Strlen(ext);
        if (elen>3 && elen<(sizeof(extBuf)-10) && !Jsi_Strcmp(ext+elen-3,".gz")) {
            Jsi_Strcpy(extBuf, ext);
            extBuf[elen-3] = 0;
            char *ext2 = Jsi_Strrchr(extBuf, '.');
            if (ext2) {
                isgzip = 1;
                ext = ext2;
            }
        }
        Jsi_HashEntry *hPtr;

        if (cmdPtr->mimeTypes) {
            /* Lookup mime type in mimeTypes object. */
            Jsi_Value *mVal = Jsi_ValueObjLookup(interp, cmdPtr->mimeTypes, ext+1, 1);
            if (mVal)
                mime = Jsi_ValueString(interp, mVal, NULL);
        }
        if (!mime) {
            static const char* mtypes[] = {
                "html", "text/html", "js", "application/x-javascript",
                "css", "text/css", "png", "image/png", "ico", "image/icon",
                "gif", "image/gif", "jpeg", "image/jpeg",
                "jpg", "image/jpeg", "svg", "image/svg+xml",
                "json", "application/json", "txt", "text/plain",
                "jsi", "application/x-javascript", "cssi", "text/css",
                "shtml", "text/html",  "scss", "text/css",
                "sjs", "application/x-javascript",
                0, 0
            };
            mime = "text/html";
            int i;
            for (i=0; mtypes[i]; i+=2)
                if (tolower(*eext) == mtypes[i][0] && !Jsi_Strncasecmp(eext, mtypes[i], -1)) {
                    mime = mtypes[i+1];
                    break;
                }
        }

        isSSI = jsi_wsIsSSIExt(interp, cmdPtr, pss, eext);

        if ((hPtr = Jsi_HashEntryFind(cmdPtr->handlers, ext)) && !cmdPtr->deleted) {
            /* Use interprete html eg. using jsi_wpp preprocessor */
            Jsi_DString jStr = {};
            Jsi_Value *vrc = NULL;
            int hrc = 0, strLen, evrc, isalloc=0;
            char *vStr, *hstr = NULL;
            jsi_wsHander *hdlPtr = (jsi_wsHander*)Jsi_HashValueGet(hPtr);
            Jsi_Value *hv = hdlPtr->val;

            if (Jsi_Strchr(buf, '\'') || Jsi_Strchr(buf, '\"')) {
                jsi_wsServeString(pss, wsi, "Can not handle quotes in url", 404, NULL, NULL);
                return -1;
            }
            cmdPtr->handlersPkg=1;

            // Attempt to load package and get function.
            if ((hdlPtr->flags&1) && cmdPtr->handlersPkg && Jsi_ValueIsString(interp, hv)
                && ((hstr = Jsi_ValueString(interp, hv, NULL)))) {
                vrc = Jsi_NameLookup(interp, hstr);
                if (!vrc) {
                    Jsi_Number pver = Jsi_PkgRequire(interp, hstr, 0);
                    if (pver >= 0)
                        vrc = Jsi_NameLookup(interp, hstr);
                }
                if (!vrc || !Jsi_ValueIsFunction(interp, vrc)) {
                    if (vrc)
                        Jsi_DecrRefCount(interp, vrc);
                    Jsi_LogError("Failed to autoload handle: %s", hstr);
                    jsi_wsServeString(pss, wsi, "Failed to autoload handler", 404, NULL, NULL);
                    return -1;
                }
                if (hdlPtr->val)
                    Jsi_DecrRefCount(interp, hdlPtr->val);
                hdlPtr->val = vrc;
                Jsi_IncrRefCount(interp, vrc);
                hv = vrc;
            }

            if ((hdlPtr->flags&2) && !hdlPtr->triedLoad && !hdlPtr->objVar && Jsi_ValueIsFunction(interp, hv)) {
                // Run command and from returned object get the parse function.
                hdlPtr->triedLoad = 1;
                Jsi_DSAppend(&jStr, "[null]", NULL);
                Jsi_DSAppend(&jStr, "]", NULL);
                vrc = Jsi_ValueNew1(interp);
                evrc = Jsi_FunctionInvokeJSON(interp, hv, Jsi_DSValue(&jStr), &vrc);
                if (Jsi_InterpGone(interp))
                    return -1;
                if (evrc != JSI_OK || !vrc || !Jsi_ValueIsObjType(interp, vrc, JSI_OT_OBJECT)) {
                    Jsi_LogError("Failed to load obj: %s", hstr);
                    jsi_wsServeString(pss, wsi, "Failed to load obj", 404, NULL, NULL);
                    return -1;
                }
                Jsi_Value *fvrc = Jsi_ValueObjLookup(interp, vrc, "parse", 0);
                if (!fvrc || !Jsi_ValueIsFunction(interp, fvrc)) {
                    Jsi_LogError("Failed to find parse: %s", hstr);
                    jsi_wsServeString(pss, wsi, "Failed to find parse", 404, NULL, NULL);
                    return -1;
                }
                hdlPtr->objVar = fvrc;
                Jsi_IncrRefCount(interp, fvrc);
                hv = vrc;

            }

            if (hdlPtr->objVar) {  // Call the obj.parse function.
                Jsi_DSAppend(&jStr, "[\"", buf, "\"]", NULL); // TODO: JSON encode.
                vrc = Jsi_ValueNew1(interp);
                evrc = Jsi_FunctionInvokeJSON(interp, hdlPtr->objVar, Jsi_DSValue(&jStr), &vrc);
                isalloc = 1;
            }
            else if (Jsi_ValueIsFunction(interp, hv)) {
                //printf("CNCNN: %s\n", Jsi_DSValue(&cmdPtr->cName));
                Jsi_DSAppend(&jStr, "[\"", buf, "\", {wsName:\"", Jsi_DSValue(&cmdPtr->cName), "\"", "}]", NULL); // TODO: JSON encode.
                vrc = Jsi_ValueNew1(interp);
                evrc = Jsi_FunctionInvokeJSON(interp, hv, Jsi_DSValue(&jStr), &vrc);
                isalloc = 1;
            } else {
                // One shot invoke of string command.
                hstr = Jsi_ValueString(interp, hv, NULL);
                Jsi_DSAppend(&jStr, hstr, "('", buf, "');", NULL);
                evrc = Jsi_EvalString(interp, Jsi_DSValue(&jStr), JSI_EVAL_RETURN);
                if (evrc == JSI_OK)
                    vrc = Jsi_InterpResult(interp);
            }
            // Take result from vrc and return it.
            if (evrc != JSI_OK) {
                Jsi_LogError("failure in websocket handler");
            } else if ((!vrc) ||
                (!(vStr = Jsi_ValueString(interp, vrc, &strLen)))) {
                Jsi_LogError("failed to get result");
            } else {
                hrc = jsi_wsServeString(pss, wsi, vStr, 0, NULL, mime);
            }
            Jsi_DSFree(&jStr);
            if (isalloc)
                Jsi_DecrRefCount(interp, vrc);
            if (hrc<=0)
                return -1;
            return 1;
        }
    }
    if (!buf[0]) {
        if (cmdPtr->debug)
            fprintf(stderr, "empty file: %s\n", inPtr);
        return -1;
    }
    fname = Jsi_ValueNewStringDup(interp, buf);
    Jsi_IncrRefCount(interp, fname);

    Jsi_DString hStr = {};
    Jsi_StatBuf jsb;
    bool native = Jsi_FSNative(interp, fname);
    if ((native && Jsi_InterpSafe(interp) && Jsi_InterpAccess(interp, fname, JSI_INTACCESS_READ) != JSI_OK) ||
        (Jsi_Stat(interp, fname, &jsb) || jsb.st_size<=0)) {
nofile:
        if (cmdPtr->onUnknown || pss->onUnknown) {
            Jsi_Value *uk = (pss->onUnknown?pss->onUnknown:cmdPtr->onUnknown);
            Jsi_RC jrc = jsi_wsGetCmd(interp, cmdPtr, pss, wsi, inPtr, uk, NULL);
            if (jrc == JSI_ERROR)
                goto bail;
            if (jrc == JSI_OK)
                goto done;
        }

        if (0 && Jsi_Strstr(buf, "favicon.ico"))
            rc = jsi_wsServeString(pss, wsi, "data:;base64,iVBORw0KGgo=", 200, NULL, "image/icon");
        else {
            const char *cp = Jsi_Strrchr(buf,'/');
            if (cp && cp[1]) {
                char statPath[PATH_MAX];
                snprintf(statPath, sizeof(statPath), "/zvfs/lib/web%s", cp);
                Jsi_DecrRefCount(interp, fname);
                fname = Jsi_ValueNewStringDup(interp, statPath);
                Jsi_IncrRefCount(interp, fname);
                if (!Jsi_Stat(interp, fname, &jsb) && jsb.st_size>0) {
                    native = 0;
                    goto serve;
                }
            }
            if (cmdPtr->noWarn==0 && !Jsi_Strstr(buf, "favicon.ico"))
                fprintf(stderr, "failed open file for read: %s\n", buf);
            rc = jsi_wsServeString(pss, wsi, "<b style='color:red'>ERROR: can not serve file!</b>", 404, NULL, NULL);
        }
        Jsi_DecrRefCount(interp, fname);
        goto done;
    }
    if (!ext || isSSI)
        goto serve;
    if (S_ISDIR(jsb.st_mode)) {
        if (cmdPtr->noWarn==0)
            fprintf(stderr, "can not serve directory: %s\n", buf);
        rc = jsi_wsServeString(pss, wsi, "<b style='color:red'>ERROR: can not serve directory!</b>", 404, NULL, NULL);
        Jsi_DecrRefCount(interp, fname);
        goto done;
    }

serve:
    n = 0;
    // TODO: add automatic cookie mgmt?
/*
    if (!strcmp((const char *)in, "/") &&
       !lws_hdr_total_length(wsi, WSI_TOKEN_HTTP_COOKIE)) {
        gettimeofday(&tv, NULL);
        n = sprintf(b64, "test=LWS_%u_%u_COOKIE;Max-Age=360000",
            (unsigned int)tv.tv_sec,
            (unsigned int)tv.tv_usec);

        if (lws_add_http_header_by_name(wsi,
            (unsigned char *)"set-cookie:",
            (unsigned char *)b64, n, &p,
            (unsigned char *)buffer + sizeof(buffer)))
            return 1;
    }*/
    static const char stsStr[] = "max-age=15768000 ; includeSubDomains";
    if (lws_is_ssl(wsi) && lws_add_http_header_by_name(wsi,
                    (uchar *) "Strict-Transport-Security:",
                    (uchar *) stsStr,
                    sizeof(stsStr)-1, &p, (uchar *)buffer + sizeof(buffer)))
        goto bail;
    n = p - buffer;
    if (n>0)
        Jsi_DSAppendLen(&hStr, (char*)buffer, n);
    p = buffer;

    if (isgzip) {
        if (lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_ENCODING,
                    (unsigned char *)"gzip", n, &p, end))
            goto bail;
    }
    if (cmdPtr->headers && !jsi_wsAddHeader(interp, cmdPtr, wsi, cmdPtr->headers, &hStr))
        goto bail;

    if (pss->headers && !jsi_wsAddHeader(interp, cmdPtr, wsi, pss->headers, &hStr))
        goto bail;

    n = Jsi_DSLength(&hStr);

    if (native && !isSSI && !isJsiWeb) {

        if (!jsi_wsAddStdHeader(interp, cmdPtr, wsi, &hStr)) {
            Jsi_DecrRefCount(interp, fname);
            goto bail;
        }
        int hrc = lws_serve_http_file(wsi, buf, mime, Jsi_DSValue(&hStr), Jsi_DSLength(&hStr));
        if (hrc >= 0 && cmdPtr->onModify)
            jsi_wsFileAdd(interp, cmdPtr, fname);
        Jsi_DecrRefCount(interp, fname);
        if (hrc<0) {
            if (cmdPtr->noWarn==0)
                fprintf(stderr, "can not serve file (%d): %s\n", hrc, buf);
            goto bail;
        } else if (hrc > 0 && lws_http_transaction_completed(wsi))
            goto bail;
    } else {
        // Need to read data for non-native files.
        Jsi_DString dStr = {}, fStr = {};
        if (isSSI)
            rc = jsi_wsEvalSSI(interp, cmdPtr, fname, &fStr, 1, pss);
        else {
            rc = jsi_wsFileRead(interp, fname, &fStr, cmdPtr, pss);
            if (isJsiWeb)
                Jsi_DSAppend(&fStr, "\nwindow.jsiWebSocket=true;", NULL);
        }
        if (rc != JSI_OK) {
            Jsi_DSFree(&fStr);
            goto nofile;
        }
        int hrc = jsi_wsServeHeader(pss, wsi, (int)Jsi_DSLength(&fStr), 200, Jsi_DSValue(&hStr), mime, &dStr);
        if (hrc>=0) {
            Jsi_DSAppendLen(&dStr, Jsi_DSValue(&fStr), Jsi_DSLength(&fStr));
            char *strVal = Jsi_DSValue(&dStr);
            int strLen = Jsi_DSLength(&dStr);
            hrc = jsi_wswrite(pss, wsi, (unsigned char*)strVal, strLen, LWS_WRITE_HTTP);
        }
        Jsi_DecrRefCount(interp, fname);
        Jsi_DSFree(&dStr);
        Jsi_DSFree(&fStr);
        if (hrc<0) {
            if (cmdPtr->noWarn==0)
                fprintf(stderr, "can not serve data (%d): %s\n", hrc, buf);
            goto bail;
        } else if (hrc > 0 && lws_http_transaction_completed(wsi))
            goto bail;

    }
done:
    Jsi_DSFree(&hStr);
    return rc;

bail:
    rc = 1;
    goto done;
}

Project: jsish

void jsi_ToHexStr(const uchar *indata, int dlen, char *out) {
    static char hex[] = "0123456789abcdef";
    int i, n=0;
    for (i=0; i<dlen; i++) {
        int c = indata[i];
        out[n++] = hex[(c>>4)&0xf];
        out[n++] = hex[c&0xf];
    }
    out[n] = 0;
}

Project: jsish

static Jsi_RC mdbEvalStep(MyDbEvalContext *p) {
    Jsi_RC rc = JSI_OK;
    if( p->prep==0)
        rc = mdbEvalPrep(p);
    if (rc == JSI_BREAK)
        return JSI_BREAK;
    if (rc == JSI_OK)
        rc = mdbEvalStepSub(p, 1, NULL);
    return rc;
}

Project: jsish

static Jsi_OpCodes *code_push_vstring(jsi_Pstate *p, jsi_Pline *line, Jsi_String *s) {
    JSI_NEW_CODESLN(0,OP_PUSHVSTR, s);
}

Project: jsish

Jsi_Value* Jsi_ValueNewString(Jsi_Interp *interp, const char *s, int len) {
    assert(s);
    Jsi_Value *v = Jsi_ValueNew(interp);
    Jsi_Obj *obj = Jsi_ObjNewType(interp, JSI_OT_STRING);
    Jsi_ValueMakeObject(interp, &v, obj);
    obj->d.s.str = (char*)s;
    obj->d.s.len = (len<0?Jsi_Strlen(s):(uint)len);
    return v;
}

Project: jsish

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

Project: jsish

static Jsi_RC JSONStringifyCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    bool strict = 1;
    Jsi_Value *jsv = Jsi_ValueArrayIndex(interp, args, 1);
    if (jsv && Jsi_ValueGetBoolean(interp, jsv, &strict) != JSI_OK) 
        return Jsi_LogError("Expected boolean");
    int quote = JSI_OUTPUT_JSON;
    if (strict) quote|=JSI_JSON_STRICT;
    Jsi_DString dStr = {};
    Jsi_Value *arg = Jsi_ValueArrayIndex(interp, args, 0);
    Jsi_ValueGetDString(interp, arg, &dStr, quote);
    Jsi_ValueFromDS(interp, &dStr, ret);
    return JSI_OK;
}

Project: jsish

static void dbEvalSetColumnJSON(DbEvalContext *p, int iCol, Jsi_DString *dStr) {
    Jsi_Interp *interp = p->jdb->interp;
    char nbuf[JSI_MAX_NUMBER_STRING];

    sqlite3_stmt *pStmt = p->pPreStmt->pStmt;

    switch( sqlite3_column_type(pStmt, iCol) ) {
    case SQLITE_BLOB: {
        int bytes = sqlite3_column_bytes(pStmt, iCol);
        const char *zBlob = (char*)sqlite3_column_blob(pStmt, iCol);
        if( !zBlob ) {
            Jsi_DSAppend(dStr, "null", NULL);
            return;
        }
        Jsi_JSONQuote(interp, zBlob, bytes, dStr);
        return;
    }
    case SQLITE_INTEGER: {
        sqlite_int64 v = sqlite3_column_int64(pStmt, iCol);
        if (v==0 || v==1) {
            const char *dectyp = sqlite3_column_decltype(pStmt, iCol);
            if (dectyp && !Jsi_Strncasecmp(dectyp,"bool", 4)) {
                Jsi_DSAppend(dStr, (v?"true":"false"), NULL);
                return;
            }
        }
#ifdef __WIN32
        snprintf(nbuf, sizeof(nbuf), "%" PRId64, (Jsi_Wide)v);
#else
        snprintf(nbuf, sizeof(nbuf), "%lld", v);
#endif
        Jsi_DSAppend(dStr, nbuf, NULL);
        return;
    }
    case SQLITE_FLOAT: {
        Jsi_NumberToString(interp, sqlite3_column_double(pStmt, iCol), nbuf, sizeof(nbuf));
        Jsi_DSAppend(dStr, nbuf, NULL);
        return;
    }
    case SQLITE_NULL: {
        Jsi_DSAppend(dStr, "null", NULL);
        return;
    }
    }
    const char *str = (char*)sqlite3_column_text(pStmt, iCol );
    if (!str)
        str = p->jdb->optPtr->nullvalue;
    Jsi_JSONQuote(interp, str?str:"", -1, dStr);
}

Project: jsish

void Jsi_MutexDelete(Jsi_Interp *interp, Jsi_Mutex *mtx) { }

Project: jsish

Jsi_OptionsConf(Jsi_Interp *interp, Jsi_OptionSpec *specs,  void *rec, Jsi_Value *val, Jsi_Value **ret, Jsi_Wide flags)
{
    flags |= JSI_OPTS_IS_UPDATE;
    if (!Jsi_OptionsValid(interp, specs))
        return Jsi_LogError("invalid options");
    
    if (!val)
        return Jsi_OptionsDump(interp, specs, rec, ret, flags);
    if (val->vt == JSI_VT_NULL)
        return Jsi_OptionsDump(interp, specs, rec, ret, flags|JSI_OPTS_VERBOSE);
    if (Jsi_ValueIsString(interp, val)) {
        const char *cp = Jsi_ValueString(interp, val, NULL);
        if (cp && *cp)
            return Jsi_OptionsGet(interp, specs, rec, cp, ret, flags);
        Jsi_Obj *sobj = Jsi_ObjNewType(interp, JSI_OT_ARRAY);
        Jsi_Value *svalue = Jsi_ValueMakeObject(interp, NULL, sobj);
        jsi_DumpOptionSpecs(interp, sobj, specs);
        Jsi_ValueReplace(interp, ret, svalue);
        return JSI_OK;
    }
    if (val->vt != JSI_VT_OBJECT) 
        return Jsi_LogError("expected string, object, or null");
    if (Jsi_OptionsProcess(interp, specs, rec, val, JSI_OPTS_IS_UPDATE|flags) < 0)
        return JSI_ERROR;
    return JSI_OK;
}

Project: jsish

void *Jsi_TreeKeyGet(Jsi_TreeEntry *hPtr)
{
    Jsi_Tree *t = hPtr->treePtr;
    return (t->keyType == JSI_KEYS_ONEWORD || t->keyType == JSI_KEYS_STRINGKEY ? hPtr->key.oneWordValue : hPtr->key.string);
}

Project: jsish

bool Jsi_MapUnset(Jsi_Map *mapPtr, const void *key){
    SIGASSERT(mapPtr, MAP);
    switch (mapPtr->typ) {
        case JSI_MAP_HASH: return Jsi_HashUnset(mapPtr->v.hash, (void*)key);
        case JSI_MAP_TREE: return Jsi_TreeUnset(mapPtr->v.tree, (void*)key);
        case JSI_MAP_LIST: {
            /*Jsi_ListEntry* lptr = (key == NULL? Jsi_ListGetFront(mapPtr->v.list) : Jsi_ListGetBack(mapPtr->v.list));
            if (lptr)
                return Jsi_ListUnset(lptr);*/
            break;
        }
        case JSI_MAP_NONE: break;
    }
    return false;
}

Project: jsish

jsi_wsGetHeaders(jsi_wsPss *pss, struct lws *wsi, Jsi_DString* dStr, int lens[], int hmax)
{
    int n = 0, i = 0, nlen;
    char buf[JSI_BUFSIZ];
    const char *cp;
    while ((cp = (char*)lws_token_to_string((enum lws_token_indexes)n))) {
        int len = lws_hdr_copy(wsi, buf, sizeof(buf), ( enum lws_token_indexes)n);
        n++;
        if (i>=(n*2+2)) break;
        if (len<=0) continue;
        buf[sizeof(buf)-1] = 0;
        if (!buf[0]) continue;
        nlen = Jsi_Strlen(cp);
        if (nlen>0 && cp[nlen-1]==' ') nlen--;
        if (nlen>0 && cp[nlen-1]==':') nlen--;
        Jsi_DSAppendLen(dStr, cp, nlen);
        Jsi_DSAppend(dStr, "=", buf, "\n", NULL);
        if (lens) {
            lens[i++] = nlen;
            lens[i++] = Jsi_Strlen(buf);
        }
    }
    //printf("HEE: %d = %s\n", pss->wid, Jsi_DSValue(dStr) );
    return i;
}

Project: jsish

static Jsi_RC StringReplaceCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    /* Now handles perl regex flag extensions.*/
    const char *source_str;
    int source_len, bLen;
    const char *replace_str = NULL;
    int replace_len;
    int regexec_flags = 0;
    Jsi_Value *seq, *strVal;
    Jsi_DString dStr = {};
    regex_t *regex;
    Jsi_Regex *re;
    const char *p;
    int maxArgs = 1;

    int offset = 0, n, j, isglob = 0, num_matches = 0;
    /* Is a generic  String.replace if _this->d.obj is a function */
    ChkString(_this, funcPtr, source_str, &source_len, &bLen);
    source_len = bLen;
    if (!skip)
        strVal = _this;
    else
        strVal = Jsi_ValueArrayIndex(interp, args, 0);
    seq = Jsi_ValueArrayIndex(interp, args, skip);
    Jsi_Value *repVal = Jsi_ValueArrayIndex(interp, args, 1+skip);
    if (!Jsi_ValueIsFunction(interp, repVal))
        replace_str = Jsi_ValueToString(interp, repVal, &replace_len);
    else
        maxArgs = repVal->d.obj->d.fobj->func->argnames->argCnt;
    Jsi_DSInit(&dStr);

    if (Jsi_ValueIsString(interp, seq)) {
        const char *ce, *cp = Jsi_ValueString(interp, seq, NULL);
        if (!(ce = Jsi_Strstr(source_str, cp)))
            Jsi_ValueMakeStringDup(interp, ret, source_str);
        else {
            int slen;
            slen = (ce-source_str);
            if (slen)
                Jsi_DSAppendLen(&dStr, source_str, slen);
            if (replace_str)
                Jsi_DSAppendLen(&dStr, replace_str, replace_len);
            else {
                Jsi_Value *inStr = Jsi_ValueNewStringDup(interp, source_str);
                Jsi_IncrRefCount(interp, inStr);
                Jsi_RC rc = Jsi_FunctionInvokeString(interp, repVal, 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_DSAppend(&dStr, ce+Jsi_Strlen(cp), NULL);
            Jsi_ValueFromDS(interp, &dStr, ret);
        }
        return JSI_OK;
    }
    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 = &re->reg;
    isglob = (re->eflags & JSI_REG_GLOB);
    
    regmatch_t pmatch[MAX_SUBREGEX] = {};
    /* If an offset has been specified, adjust for that now.
     * If it points past the end of the string, point to the terminating null
     */
    int eoffset=0;
    if (offset) {
        if (offset < 0) {
            offset += source_len + 1;
        }
        if (offset > source_len) {
            offset = source_len;
        }
        else if (offset < 0) {
            offset = 0;
        }
    }

    Jsi_DSAppendLen(&dStr, source_str, offset);
    n = source_len - offset;
    p = source_str + offset;
    Jsi_RC rc = JSI_OK;
    do {
        if (num_matches > 10000000) {
            Jsi_LogBug("regexp infinite loop");
            rc = JSI_ERROR;
            break;
        }
        int match = regexec(regex, p, MAX_SUBREGEX, pmatch, regexec_flags);

        if (match >= REG_BADPAT) {
            char buf[JSI_MAX_NUMBER_STRING];

            regerror(match, regex, buf, sizeof(buf));
            Jsi_LogError("error while matching pattern: %s", buf);
            Jsi_DSFree(&dStr);
            return JSI_ERROR;
        }
        if (match == REG_NOMATCH) {
            break;
        }
        num_matches++;
        Jsi_DSAppendLen(&dStr, p, pmatch[0].rm_so);

        if (replace_str &&  !Jsi_Strchr(replace_str, '$'))
            Jsi_DSAppend(&dStr, replace_str, NULL);
        else if (replace_str) {
            for (j = 0; j < replace_len; j++) {
                int idx;
                int c = replace_str[j];
     
                if (c == '$' && j < replace_len) {
                    c = replace_str[++j];
                    if ((c >= '0') && (c <= '9')) {
                        idx = c - '0';
                    } else if (c == '&') {
                        idx = 0;
                    } else if (c == '$') {
                        Jsi_DSAppendLen(&dStr, replace_str + j, 1);
                        continue;
                    }
                    else if (c == '\'') {
                        Jsi_DSAppendLen(&dStr, p + pmatch[0].rm_eo, pmatch[0].rm_eo-Jsi_Strlen(p));
                        continue;
                    }
                    else if (c == '`') {
                        Jsi_DSAppendLen(&dStr, p, pmatch[0].rm_so);
                        continue;
                    }
                    else {
                        Jsi_DSAppendLen(&dStr, replace_str + j - 1, 2);
                        continue;
                    }
                } else {
                     Jsi_DSAppendLen(&dStr, replace_str + j, 1);
                     continue;
                }
                if ((idx < MAX_SUBREGEX) && pmatch[idx].rm_so != -1 && pmatch[idx].rm_eo != -1) {
                    Jsi_DSAppendLen(&dStr, p + pmatch[idx].rm_so,
                        pmatch[idx].rm_eo - pmatch[idx].rm_so);
                }
            }
    
        } else {
            Jsi_DString sStr;
            Jsi_DSInit(&sStr); 
            if (pmatch[0].rm_so <= 0 && pmatch[0].rm_eo <= 0)
                break;
            int olen = -1;
            char *ostr = jsi_SubstrDup(p, -1, pmatch[0].rm_so, pmatch[0].rm_eo - pmatch[0].rm_so, &olen);
            Jsi_Value *inStr = Jsi_ValueMakeBlob(interp, NULL, (uchar*)ostr, olen);
            Jsi_DSFree(&sStr);
            Jsi_IncrRefCount(interp, inStr);
            if (maxArgs==1) {
                Jsi_RC rc = Jsi_FunctionInvokeString(interp, repVal, inStr, &dStr);
                if (Jsi_InterpGone(interp))
                    return JSI_ERROR;
                if (rc != JSI_OK) {
                    Jsi_DSFree(&dStr);
                    Jsi_DecrRefCount(interp, inStr);
                    return JSI_ERROR;
                }
            } else {
                Jsi_Value *vpargs, *items[MAX_SUBREGEX] = {}, *ret;
                int i;
                items[0] = inStr;
                for (i=1; i<=(int)re->reg.re_nsub && i<(MAX_SUBREGEX-3); i++) {
                    if (pmatch[i].rm_so<0)
                        items[i] = interp->NullValue;
                    else {
                        ostr = jsi_SubstrDup(p, -1, pmatch[i].rm_so, pmatch[i].rm_eo - pmatch[i].rm_so, &olen);
                        items[i] = Jsi_ValueMakeBlob(interp, NULL, (uchar*)ostr, olen);
                    }
                }
                items[i++] = Jsi_ValueMakeNumber(interp, NULL, eoffset+pmatch[0].rm_so);
                items[i++] = strVal;
                vpargs = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, items, i, 0));
                Jsi_IncrRefCount(interp, vpargs);
                ret = Jsi_ValueNew1(interp);
                rc = Jsi_FunctionInvoke(interp, repVal, vpargs, &ret, NULL);
                if (Jsi_InterpGone(interp))
                    return JSI_ERROR;
                Jsi_DecrRefCount(interp, vpargs);
                if (rc == JSI_OK)
                    Jsi_DSAppend(&dStr, Jsi_ValueToString(interp, ret, NULL), NULL);
                Jsi_DecrRefCount(interp, ret);
                if (rc != JSI_OK) {
                    Jsi_DSFree(&dStr);
                    Jsi_DecrRefCount(interp, inStr);
                    return JSI_ERROR;
                }
            }
            Jsi_DecrRefCount(interp, inStr);
        }
        eoffset += pmatch[0].rm_eo;
        p += pmatch[0].rm_eo;
        n -= pmatch[0].rm_eo;
        /* If -all is not specified, or there is no source left, we are done */
        if (!isglob || n == 0 || pmatch[0].rm_eo == 0) {
            break;
        }
        /* An anchored pattern without -line must be done */
        if ((re->eflags & JSI_REG_NEWLINE) == 0 && re->pattern[0] == '^') {
            break;
        }
        
        /* If the pattern is empty, need to step forwards */
        if (re->pattern[0] == '\0' && n) {
            /* Need to copy the char we are moving over */
            Jsi_DSAppendLen(&dStr, p, 1);
            p++;
            n--;
        }

        regexec_flags |= REG_NOTBOL;
    } while (n);

    /*
     * Copy the portion of the string after the last match to the
     * result variable.
     */
    Jsi_DSAppend(&dStr, p, NULL);
    Jsi_ValueFromDS(interp, &dStr, ret);
    return rc;

}

Project: jsish

static Jsi_RC jsi_PushVar(jsi_Pstate *ps, jsi_OpCode *ip, jsi_ScopeChain *scope, Jsi_Value *currentScope, int context_id) {
    Jsi_Interp *interp = ps->interp;
    jsi_FastVar *fvar = (typeof(fvar))ip->data;
    SIGASSERT(fvar,FASTVAR);
    Jsi_Value **dvPtr = &_jsi_STACKIDX(interp->framePtr->Sp), *dv = *dvPtr, *v = NULL;
    if (fvar->context_id == context_id && fvar->ps == ps) {
        v = fvar->lval;
    } else {
        char *varname = fvar->varname;
        v = Jsi_ValueObjLookup(interp, currentScope, varname, 1);
        if (v) {
            fvar->local = 1;
            if (v->vt == JSI_VT_UNDEF) {
                v->d.lookupFail = varname;
                v->f.bits.lookupfailed = 1;
            }
        } else {
            v = jsi_ScopeChainObjLookupUni(scope, varname);
            if (v) 
                fvar->local = 1;
            else {
                /* add to global scope.  TODO: do not define if a right_val??? */
                Jsi_Value *global_scope = scope->chains_cnt > 0 ? scope->chains[0]:currentScope;
                Jsi_Value key = VALINIT, *kPtr = &key; // Note: a string key so no reset needed.
                Jsi_ValueMakeStringKey(interp, &kPtr, varname);
                v = jsi_ValueObjKeyAssign(interp, global_scope, &key, NULL, JSI_OM_DONTENUM);
                if (v->vt == JSI_VT_UNDEF) {
                    v->d.lookupFail = varname;
                    v->f.bits.lookupfailed = 1;
                }
                jsi_ValueDebugLabel(v, "var", varname);
                bool isNew;
                Jsi_HashEntry *hPtr = Jsi_HashEntryNew(interp->varTbl, varname, &isNew);
                if (hPtr && isNew)
                    Jsi_HashValueSet(hPtr, 0);
            }
        }
        
        Jsi_IncrRefCount(interp, v);

    }
    if (dv != v && (dv->vt != JSI_VT_VARIABLE || dv->d.lval != v)) {
        Jsi_ValueReset(interp, dvPtr);
        dv->vt = JSI_VT_VARIABLE;
        SIGASSERT(v, VALUE);
        dv->d.lval = v;
        dv->f.bits.local = (fvar->local);
    }
    SIGASSERT(v, VALUE);
    jsiPush(interp,1);
    return JSI_OK;
}

Project: jsish

Jsi_Value *Jsi_ValueArrayIndex(Jsi_Interp *interp, Jsi_Value *args, int index)
{
    Jsi_Obj *obj = args->d.obj;
    Jsi_Value *v;
    assert(args->vt == JSI_VT_OBJECT);
    if (obj->isarrlist && obj->arr)
        return ((index < 0 || (uint)index >= obj->arrCnt) ? NULL : obj->arr[index]);
    char unibuf[JSI_MAX_NUMBER_STRING];
    Jsi_NumberItoA10(index, unibuf, sizeof(unibuf));
    v = Jsi_TreeObjGetValue(args->d.obj, unibuf, 0);
    return v;
}

Project: jsish

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

Project: jsish

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

Project: jsish

uint Jsi_HashSize(Jsi_Hash *hashPtr) { return hashPtr->numEntries; }

Project: jsish

static Jsi_RC dbBindOptionStmt(Jsi_Db *jdb, sqlite3_stmt *pStmt, OptionBind *obPtr,
                            int dataIdx, int bindMax, Jsi_CDataDb *dbopts)
{
    Jsi_Interp *interp = jdb->interp;
    int j, k, cnt = 0, idx, sidx = -1, rc = 0;
    Jsi_StructSpec *specPtr, *specs;
    void *rec;
    Jsi_DString *eStr;
    const char *bName;
    int lastBind = sqlite3_bind_parameter_count(pStmt);
    if (lastBind<=0)
        return JSI_OK;
    int structSize = 0;
    Jsi_Wide flags = 0;
    sqlite3_destructor_type statFlags = ((dbopts->noStatic)?SQLITE_TRANSIENT:SQLITE_STATIC);
    specPtr = dbopts[0].sf;
    structSize = specPtr[obPtr->optLen].size;
    
    for (j=1; j<=lastBind; j++) {
        bName = sqlite3_bind_parameter_name(pStmt, j);
        if (bName==NULL || bName[0]==0 || bName[1]==0)
            continue;
        idx = j;
        if (dbopts[0].prefix==0)
            k = 0;
        else {
            for (k=0; dbopts[k].sf; k++) {
                if (bName[0] == dbopts[k].prefix)
                    break;
            }
            if (bindMax>0 && k>=bindMax)
                continue;
            if (!dbopts[k].sf) {
                Jsi_LogError("bad bind: %s", bName);
                continue;
            }
        }
        specs = dbopts[k].sf;
        rec = dbopts[k].data;
        if (k==0) {
            if (dbopts->isMap) {
                Jsi_Map *map = *(typeof(map)*)rec;
                rec = Jsi_MapEntryFind(map, (void*)(intptr_t)dataIdx);
                if (!rec) return JSI_ERROR;
            } else if (dbopts->isPtrs)
                rec = ((void**)rec)[dataIdx];
            else
                rec = ((uchar*)rec)+ (dataIdx * structSize);
        }
        if (bName[0] == '?')
            sidx = atoi(bName+1);
        for (specPtr = specs, cnt=1; specPtr->id>=JSI_OPTION_BOOL && specPtr->id < JSI_OPTION_END; specPtr++, cnt++) {
            if (specPtr->flags&JSI_OPT_DB_IGNORE)
                continue;
            if (bName[0] == '?') {
                if (cnt == sidx)
                    break;
            } else {
                const char *sName = specPtr->name;
                if (bName[1] == sName[0] && !Jsi_Strcmp(bName+1, sName))
                    break;
            }
        }
        if (specPtr->id<JSI_OPTION_BOOL || specPtr->id>=JSI_OPTION_END) 
            return Jsi_LogError("unknown bind: %s", bName);

        char *ptr = (char *)rec + specPtr->offset;
        switch (specPtr->id) {
        case JSI_OPTION_BOOL:
            rc = sqlite3_bind_int(pStmt, idx, *(int*)ptr);
            break;
        case JSI_OPTION_INT: rc = sqlite3_bind_int64(pStmt, idx, *(int*)ptr); break;
        case JSI_OPTION_UINT:rc = sqlite3_bind_int64(pStmt, idx, *(uint*)ptr); break;
        case JSI_OPTION_INT8: rc = sqlite3_bind_int64(pStmt, idx, *(int8_t*)ptr); break;
        case JSI_OPTION_UINT8:rc = sqlite3_bind_int64(pStmt, idx, *(uint8_t*)ptr); break;
        case JSI_OPTION_INT16: rc = sqlite3_bind_int64(pStmt, idx, *(int16_t*)ptr); break;
        case JSI_OPTION_UINT16:rc = sqlite3_bind_int64(pStmt, idx, *(uint16_t*)ptr); break;
        case JSI_OPTION_INT32: rc = sqlite3_bind_int64(pStmt, idx, *(int32_t*)ptr); break;
        case JSI_OPTION_UINT32:rc = sqlite3_bind_int64(pStmt, idx, *(uint32_t*)ptr); break;
        case JSI_OPTION_TIME_W:
        case JSI_OPTION_INT64: rc = sqlite3_bind_int64(pStmt, idx, *(int64_t*)ptr); break;
        case JSI_OPTION_UINT64:rc = sqlite3_bind_int64(pStmt, idx, *(uint64_t*)ptr); break;
        case JSI_OPTION_USHORT:rc = sqlite3_bind_int64(pStmt, idx, *(ushort*)ptr); break;
        case JSI_OPTION_SHORT:rc = sqlite3_bind_int64(pStmt, idx, *(short*)ptr); break;
        case JSI_OPTION_LONG:rc = sqlite3_bind_int64(pStmt, idx, *(long*)ptr); break;
        case JSI_OPTION_ULONG:rc = sqlite3_bind_int64(pStmt, idx, *(ulong*)ptr); break;
        case JSI_OPTION_INTPTR_T:rc = sqlite3_bind_int64(pStmt, idx, *(intptr_t*)ptr); break;
        case JSI_OPTION_UINTPTR_T:rc = sqlite3_bind_int64(pStmt, idx, *(uintptr_t*)ptr); break;
        case JSI_OPTION_SIZE_T:rc = sqlite3_bind_int64(pStmt, idx, *(size_t*)ptr); break;
        case JSI_OPTION_SSIZE_T:rc = sqlite3_bind_int64(pStmt, idx, *(ssize_t*)ptr); break;
        case JSI_OPTION_LDOUBLE:rc = sqlite3_bind_int64(pStmt, idx, *(ldouble*)ptr); break;
        case JSI_OPTION_FLOAT:rc = sqlite3_bind_int64(pStmt, idx, *(float*)ptr); break;
        case JSI_OPTION_TIME_T:
            rc = sqlite3_bind_int64(pStmt, idx, (Jsi_Wide)*(time_t*)ptr);
            break;
        case JSI_OPTION_NUMBER:
            rc = sqlite3_bind_double(pStmt, idx, (double)*(Jsi_Number*)ptr);
            break;
        case JSI_OPTION_TIME_D:
        case JSI_OPTION_DOUBLE:
            rc = sqlite3_bind_double(pStmt, idx, (double)*(Jsi_Number*)ptr);
            break;
        case JSI_OPTION_CUSTOM: {
            Jsi_OptionCustom* cust = Jsi_OptionCustomBuiltin(specPtr->custom);
            if (cust && cust->formatProc) {
                Jsi_DString dStr;
                Jsi_DSInit(&dStr);
                if ((*cust->formatProc)(interp, (Jsi_OptionSpec*)specPtr, NULL, &dStr, rec, flags) != JSI_OK) {
                    Jsi_DSFree(&dStr);
                    return JSI_ERROR;
                }
                rc = sqlite3_bind_text(pStmt, idx, Jsi_DSValue(&dStr), -1, SQLITE_TRANSIENT );
                Jsi_DSFree(&dStr);
            } else 
                return Jsi_LogError("missing or invalid custom for \"%s\"", specPtr->name);
            break;
        }
        case JSI_OPTION_DSTRING:
            eStr = (Jsi_DString*)ptr;
            if (jdb->optPtr->nullvalue && !Jsi_Strcmp(jdb->optPtr->nullvalue, Jsi_DSValue(eStr)))
                rc = sqlite3_bind_text(pStmt, idx, NULL, -1, statFlags );
            else
                rc = sqlite3_bind_text(pStmt, idx, Jsi_DSValue(eStr), -1, statFlags );
            break;
        case JSI_OPTION_STRBUF:
            if (jdb->optPtr->nullvalue && ptr && !Jsi_Strcmp(jdb->optPtr->nullvalue, (char*)ptr))
                rc = sqlite3_bind_text(pStmt, idx, NULL, -1, statFlags );
            else
                rc = sqlite3_bind_text(pStmt, idx, (char*)ptr, -1, statFlags );
            break;
        case JSI_OPTION_STRKEY:
            rc = sqlite3_bind_text(pStmt, idx, *(char**)ptr, -1, SQLITE_STATIC );
            break;
#ifndef JSI_LITE_ONLY
        case JSI_OPTION_STRING:
            rc = sqlite3_bind_text(pStmt, idx, Jsi_ValueString(interp, *((Jsi_Value **)ptr), NULL), -1, statFlags );
            break;
#else
        case JSI_OPTION_STRING:
#endif
        case JSI_OPTION_VALUE: /* 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_END:
        case JSI_OPTION_USEROBJ:
#else
        default:
#endif
            Jsi_LogError("unsupported jdb option type \"%s\" for \"%s\"", jsi_DbOptionTypeStr(specPtr->id, 0), specPtr->name);
            return JSI_ERROR;

        }
        if (rc != SQLITE_OK)
            Jsi_LogError("bind failure: %s", sqlite3_errmsg(jdb->db));
    }
    cnt++;
    return JSI_OK;
}

Project: jsish

bool Jsi_ValueIsStringKey(Jsi_Interp* interp, Jsi_Value *key)
{
    if (key->vt == JSI_VT_STRING && key->f.bits.isstrkey)
        return 1;
    if (key->vt == JSI_VT_OBJECT && key->d.obj->ot == JSI_OT_STRING && key->d.obj->isstrkey)
        return 1;
    return 0;
}

Project: jsish

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

Project: jsish

static Jsi_OpCodes *code_div() { JSI_NEW_CODES(0,OP_DIV, 0); }

Project: jsish

Jsi_RC jsi_FunctionSubCall(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Value *tocall, int discard)
{
    Jsi_RC rc = JSI_OK;
    const char *oldCurFunc = interp->curFunction, *spnam = "";
    jsi_OpCode *ip = interp->curIp;
    int adds, as_constructor = (ip->op == OP_NEWFCALL);
    double timStart = 0;
    int docall;
    int calltrc = 0, profile = interp->profile, coverage = interp->coverage;
    jsi_PkgInfo *pkg;
    int tc;
    
    //char *lpv = interp->lastPushStr;
    if (tocall->vt == JSI_VT_UNDEF && tocall->f.bits.lookupfailed && tocall->d.lookupFail && !interp->noAutoLoad) {
        spnam = tocall->d.lookupFail;
        tocall->f.bits.lookupfailed = 0;
        tocall = jsi_LoadFunction(interp, spnam, tocall);
        interp->lastPushStr = (char*)spnam;
        interp->curIp = ip;
    }
    if (!Jsi_ValueIsFunction(interp, tocall)) {
       // if (tocall->f.bits.subscriptfailed && tocall->d.lookupFail)
       //     spnam = tocall->d.lookupFail;
        jsi_DumpFunctions(interp, spnam);
        rc = JSI_ERROR;
        goto empty_func;
    }

    if (tocall->d.obj->d.fobj==NULL || tocall->d.obj->d.fobj->func==NULL) {   /* empty function */
empty_func:
        //jsiPop(interp, stackargc);
        //jsiClearStack(interp,1);
        //Jsi_ValueMakeUndef(interp, &_jsi_TOP);
        interp->curFunction = oldCurFunc;
        Jsi_DecrRefCount(interp, _this);
        if (rc==JSI_OK)
            rc = JSI_CONTINUE;
        return rc;
        //goto done;
    }
    
    Jsi_FuncObj *fobj = tocall->d.obj->d.fobj;
    Jsi_Func *funcPtr = fobj->func;
    if (funcPtr->callback == jsi_NoOpCmd || tocall->d.obj->isNoOp) {
        jsi_NoOpCmd(interp, NULL, NULL, NULL, NULL);
        goto empty_func;
    }
    if (!interp->asserts && funcPtr->callback == jsi_AssertCmd)
        goto empty_func;
    const char *onam = funcPtr->name;
//        if (!onam) // Override blank name with last index.
//            funcPtr->name = lpv;
    if (funcPtr->name && funcPtr->name[0] && funcPtr->type == FC_NORMAL)
        interp->curFunction = funcPtr->name;
    adds = funcPtr->callflags.bits.addargs;
    Jsi_CmdSpec *cs  = funcPtr->cmdSpec;
    if (adds && (cs->flags&JSI_CMDSPEC_NONTHIS))
        adds = 0;

    Jsi_Func *pprevActive = interp->prevActiveFunc, *prevActive = interp->prevActiveFunc = interp->activeFunc;
    interp->activeFunc = funcPtr;

    rc = jsi_SharedArgs(interp, args, funcPtr, 1); /* make arg vars to share arguments */
    if (rc != JSI_OK)
        goto bail;
    funcPtr->callflags.bits.addargs = 0;
    jsi_InitLocalVar(interp, args, funcPtr);
    jsi_SetCallee(interp, args, tocall);
    
    pkg = funcPtr->pkg;
    tc = interp->traceCall;
    if (pkg) {
        tc |= pkg->popts.modConf.traceCall;
        profile |= pkg->popts.modConf.profile;
        coverage |= pkg->popts.modConf.coverage;
    }

    if (as_constructor) {                       /* new Constructor */
        Jsi_Obj *newobj = Jsi_ObjNewType(interp, JSI_OT_OBJECT);
        Jsi_Value *proto = NULL;
        if (!interp->subOpts.noproto)
            proto = Jsi_ValueObjLookup(interp, tocall, "prototype", 0);
        if (proto && proto->vt == JSI_VT_OBJECT) {
            newobj->__proto__ = proto;
            newobj->clearProto = 1;
            Jsi_IncrRefCount(interp, proto);
        }
        Jsi_ValueReset(interp, &_this);
        Jsi_ValueMakeObject(interp, &_this, newobj);            
        /* TODO: constructor specifics??? */
        calltrc = (tc&jsi_callTraceNew);
    }
    if (funcPtr->type == FC_NORMAL)
        calltrc = (tc&jsi_callTraceFuncs);
    else
        calltrc = (tc&jsi_callTraceCmds);
    if (calltrc && funcPtr->name)
        jsi_TraceFuncCall(interp, funcPtr, ip, _this, args, 0, tc);

    //Jsi_Value *spretPtr = *ret;
 
    interp->activeFunc = funcPtr;
    docall = (rc==JSI_OK);
    if (profile || coverage) {
        interp->profileCnt++;
        timStart = jsi_GetTimestamp();
    }
    if (funcPtr->type == FC_NORMAL) {
        if (docall) {
            rc = jsi_evalcode(interp->ps, funcPtr, funcPtr->opcodes, tocall->d.obj->d.fobj->scope, 
                args, _this, ret);
        }
        interp->funcCallCnt++;
    } else if (!funcPtr->callback) {
        Jsi_LogError("can not call:\"%s()\"", funcPtr->name);
    } else {
        int oldcf = funcPtr->callflags.i;
        funcPtr->callflags.bits.iscons = (as_constructor?JSI_CALL_CONSTRUCTOR:0);
        if (funcPtr->f.bits.hasattr)
        {
#define SPTR(s) (s?s:"")
            if ((funcPtr->f.bits.isobj) && _this->vt != JSI_VT_OBJECT) {
                rc = JSI_ERROR;
                docall = 0;
                Jsi_LogError("'this' is not object: \"%s()\"", funcPtr->name);
            } else if ((!(funcPtr->f.bits.iscons)) && as_constructor) {
                rc = JSI_ERROR;
                docall = 0;
                Jsi_LogError("can not call as constructor: \"%s()\"", funcPtr->name);
            } else {
                int aCnt = Jsi_ValueGetLength(interp, args);
                if (aCnt<(cs->minArgs+adds)) {
                    Jsi_LogError("missing args, expected \"%s(%s)\" ", cs->name, SPTR(cs->argStr));
                    rc = JSI_ERROR;
                    docall = 0;
                } else if (cs->maxArgs>=0 && (aCnt>cs->maxArgs+adds)) {
                    Jsi_LogError("extra args, expected \"%s(%s)\" ", cs->name, SPTR(cs->argStr));
                    rc = JSI_ERROR;
                    docall = 0;
                }
            }
        }
        if (docall) {
            funcPtr->fobj = fobj; // Backlink for bind.
            funcPtr->callflags.bits.isdiscard = discard;
            rc = funcPtr->callback(interp, args, 
                _this, ret, funcPtr);
            interp->cmdCallCnt++;
        }
        funcPtr->callflags.i = oldcf;
    }
    if (profile || coverage) {
        double timEnd = jsi_GetTimestamp(), timUsed = (timEnd - timStart);;
        assert(timUsed>=0);
        funcPtr->allTime += timUsed;
        if (interp->framePtr->evalFuncPtr)
            interp->framePtr->evalFuncPtr->subTime += timUsed;
        else
            interp->subTime += timUsed;
    }
    if (calltrc && (tc&jsi_callTraceReturn) && funcPtr->name)
        jsi_TraceFuncCall(interp, funcPtr, ip, _this, NULL, *ret, tc);
    if (!onam)
        funcPtr->name = NULL;
    if (docall) {
        funcPtr->callCnt++;
        if (rc == JSI_OK && !as_constructor && funcPtr->retType && (interp->typeCheck.all || interp->typeCheck.run))
            rc = jsi_ArgTypeCheck(interp, funcPtr->retType, *ret, "returned from", funcPtr->name, 0, funcPtr, 0);
    }
    interp->prevActiveFunc = pprevActive;
    interp->activeFunc = prevActive;

    if (as_constructor) {
        if (_this->vt == JSI_VT_OBJECT)
            _this->d.obj->constructor = tocall->d.obj;
        if ((*ret)->vt != JSI_VT_OBJECT) {
            Jsi_ValueReset(interp, ret);
            Jsi_ValueCopy(interp, *ret, _this);
        }
    }

bail:
    jsi_SharedArgs(interp, args, funcPtr, 0); /* make arg vars to shared arguments */
    Jsi_DecrRefCount(interp, _this);
    interp->curFunction = oldCurFunc;

    return rc;
}

Project: jsish

static int dbGetIntBool(Jsi_Interp *interp, Jsi_Value* v)
{
    if (Jsi_ValueIsNumber(interp, v)) {
        Jsi_Number d;
        Jsi_ValueGetNumber(interp, v, &d);
        return (int)d;
    }
    if (Jsi_ValueIsBoolean(interp, v)) {
        bool n;
        Jsi_ValueGetBoolean(interp, v, &n);
        return n;
    }
    return 0;
}

Project: jsish

static int JsiNumberBase(const char *str, int *base, int *sign)
{
    int i = 0;

    *base = 10;

    while (isspace(UCHAR(str[i]))) {
        i++;
    }

    if (str[i] == '-') {
        *sign = -1;
        i++;
    }
    else {
        if (str[i] == '+') {
            i++;
        }
        *sign = 1;
    }

    if (str[i] != '0') {
        /* base 10 */
        return 0;
    }

    /* We have 0<x>, so see if we can convert it */
    switch (str[i + 1]) {
        case 'x': case 'X': *base = 16; break;
        case 'o': case 'O': *base = 8; break;
        case 'b': case 'B': *base = 2; break;
        default: return 0;
    }
    i += 2;
    /* Ensure that (e.g.) 0x-5 fails to parse */
    if (str[i] != '-' && str[i] != '+' && !isspace(UCHAR(str[i]))) {
        /* Parse according to this base */
        return i;
    }
    /* Parse as base 10 */
    return 10;
}

Project: jsish

char jsi_toHexChar(char code) {
    static char hex[] = "0123456789abcdef";
    return hex[code & 15];
}

Project: jsish

int jsi_PstateSetFile(jsi_Pstate *ps, Jsi_Channel fp, int skipbang)
{
    jsi_Lexer *l = ps->lexer;
    jsi_PstateClear(ps);
    l->ltype = LT_FILE;
    l->d.fp = fp;
    Jsi_Rewind(ps->interp, fp);
    if (skipbang) {
        char buf[JSI_BUFSIZ];
        if (Jsi_Gets(ps->interp, fp, buf, sizeof(buf)) && (buf[0] != '#' || buf[1] != '!')) {
            Jsi_Rewind(ps->interp, fp);
        }
    }
            
    return JSI_OK;
}

Project: jsish

void jsi_PstateClear(jsi_Pstate *ps)
{
    jsi_Lexer* l = ps->lexer;
    if (l->ltype == LT_FILE)
    {
        if (l->d.fp)
            Jsi_Close(ps->interp, l->d.fp);
        l->d.fp = NULL;
    }
    if (l->ltype == LT_STRING)
    {
        l->d.str = NULL;
    }
    l->ltype = LT_NONE;
    l->last_token = 0;
    l->cur_line = 1;
    l->cur_char = 0;
    l->cur = 0;
    ps->err_count = 0;
}

Project: jsish

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

Project: jsish

Jsi_RC Jsi_FunctionInvokeJSON(Jsi_Interp *interp, Jsi_Value *func, const char *json, Jsi_Value **ret)
{
    if (!Jsi_ValueIsFunction(interp, func))
        return JSI_ERROR;
    Jsi_Value *aPtr = Jsi_ValueNew1(interp);
    Jsi_RC rc = Jsi_JSONParse(interp, json, &aPtr, 0);
    if (rc == JSI_OK)
        rc = Jsi_FunctionInvoke(interp, func, aPtr, ret, NULL);
    Jsi_DecrRefCount(interp, aPtr);
    return rc;
}

Project: jsish

static int jsi_wsServeString(jsi_wsPss *pss, struct lws *wsi,
    const char *buf, int code, const char *extra, const char *mime)
{
    int strLen = Jsi_Strlen(buf);
    Jsi_DString jStr = {};
    int rc = jsi_wsServeHeader(pss, wsi, strLen, code, extra, mime, &jStr);
    if (rc>=0) {
        Jsi_DSAppend(&jStr, buf, NULL);
        char *vStr = Jsi_DSValue(&jStr);
        int vLen = Jsi_DSLength(&jStr);
        rc = jsi_wswrite(pss, wsi, (unsigned char*)vStr, vLen, LWS_WRITE_HTTP);
    }
    Jsi_DSFree(&jStr);
    return (rc>=0?1:0);
}

Project: jsish

Jsi_Regex* Jsi_RegExpNew(Jsi_Interp *interp, const char *regtxt, int eflag)
{
    bool isNew;
    Jsi_HashEntry *hPtr;
    int flag = REG_EXTENDED;
    char c, *cm, *ce;
    const char *cp;
    Jsi_Regex *re;
    if (interp->subOpts.noRegex) {
        Jsi_LogError("regex disabled for interp");
        return NULL;
    }

    eflag |= JSI_REG_STATIC;
    if (!regtxt[0])
        return NULL;
    hPtr = Jsi_HashEntryFind(interp->regexpTbl, regtxt);
    if (hPtr) {
        re = (Jsi_Regex*)Jsi_HashValueGet(hPtr);
        if (JSI_REG_STATIC & eflag)
            re->eflags |= JSI_REG_STATIC;
        return re;
    }
    cp = regtxt+1;
    if (regtxt[0] != '/')
        return NULL;
    ce = (char*)Jsi_Strrchr(cp, '/');
    if (ce == cp || !ce)
        return NULL;
    cm = ce + 1;    
    while (*cm) {
        c = *cm++;
        if (c == 'i') flag |= REG_ICASE;
        else if (c == 'g') eflag |= JSI_REG_GLOB;
        else if (c == 'm') { /* PERL NON-STANDARD */
            eflag |= JSI_REG_NEWLINE;
            flag |= REG_NEWLINE;
        }
#ifdef RE_DOT_NEWLINE
        else if (c == 's') { /* PERL NON-STANDARD */
            eflag |= JSI_REG_DOT_NEWLINE;
            flag |= RE_DOT_NEWLINE;
        }
#endif
    }
    *ce = 0;
    regex_t reg;
    if (regcomp(&reg, cp, flag)) {
        *ce++ = '/';
        Jsi_LogError("Invalid regex string '%s'", cp);
        return NULL;
    }
    *ce++ = '/';
    re = (Jsi_Regex*)Jsi_Calloc(1, sizeof(Jsi_Regex));
    SIGINIT(re, REGEXP);
    assert (re);
    re->reg = reg;
    re->eflags = eflag;
    re->flags = flag;
    hPtr = Jsi_HashEntryNew(interp->regexpTbl, regtxt, &isNew);
    assert(hPtr);
    Jsi_HashValueSet(hPtr, re);
    re->pattern = (char*)Jsi_HashKeyGet(hPtr);
    return re;

}

Project: jsish

void  Jsi_ValueFromDS(Jsi_Interp *interp, Jsi_DString *dsPtr, Jsi_Value **ret)
{
    char *cp = NULL;
    int len = Jsi_DSLength(dsPtr);
    if (len && !(cp=(char*)dsPtr->strA)) 
        cp = Jsi_StrdupLen(dsPtr->Str, len);
    dsPtr->strA = NULL;
    dsPtr->Str[0] = 0;
    dsPtr->len = 0;
    dsPtr->spaceAvl = dsPtr->staticSize;
    if (!cp)
        Jsi_ValueMakeStringDup(interp, ret, "");
    else
        Jsi_ValueMakeBlob(interp, ret, (uchar*)cp, len);
}

Project: jsish

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

Project: jsish

static void insert_case1(Jsi_TreeEntry* n) {
    if (n->parent == NULL)
        set_color(n, _JSI_TREE_BLACK);
    else
        insert_case2(n);
}

Project: jsish

Jsi_Filesystem* Jsi_FilesystemForPath(Jsi_Interp *interp, Jsi_Value* path, void**clientDataPtr) {
    FSList *fsl = jsiFSList;
    if (!fsl) return NULL;
    clientDataPtr = NULL;
    const char *pathStr = Jsi_ValueToString(interp, path, NULL);
    if (!pathStr || pathStr[0] == '~')
        return &jsiFilesystem;
    if (pathStr[0] == '.' && pathStr[1] == 0)
        return (jsiIntData.cwdFsPtr ? jsiIntData.cwdFsPtr : &jsiFilesystem);
    Jsi_Value *tpath = NULL;
    if (Jsi_Strstr(pathStr, "..")) {
        Jsi_DString dStr;
        Jsi_DSInit(&dStr);
        pathStr = Jsi_ValueNormalPath(interp, path, &dStr);
        tpath = path = Jsi_ValueNewStringDup(interp, pathStr);
        Jsi_IncrRefCount(interp, tpath);
        Jsi_DSFree(&dStr);
    }
    while (1) {
        if (fsl->fsPtr->pathInFilesystemProc && fsl->fsPtr->pathInFilesystemProc(interp, path, clientDataPtr))
            break;
        if (!fsl->next)
            break;
        fsl = fsl->next;
    }
    if (tpath)
        Jsi_DecrRefCount(interp, tpath);
    return (fsl ? fsl->fsPtr : &jsiFilesystem);
}

Project: jsish

Jsi_TreeEntry *Jsi_TreeSearchFirst (Jsi_Tree *treePtr, Jsi_TreeSearch *searchPtr, int flags, const void *startKey)
{
    Jsi_TreeEntry *hPtr = NULL, *hPtr2 = NULL;
    if (!treePtr) return NULL;
    memset(searchPtr, 0, sizeof(*searchPtr));
    searchPtr->treePtr = treePtr;
    searchPtr->flags = flags;
    searchPtr->Ptrs = searchPtr->staticPtrs;
    searchPtr->max = sizeof(searchPtr->staticPtrs)/sizeof(searchPtr->staticPtrs[0]);
    searchPtr->epoch = treePtr->epoch;
    if (startKey || (flags & JSI_TREE_SEARCH_KEY))
        hPtr2 = Jsi_TreeEntryFind(treePtr, startKey);;
    searchPtr->current = treePtr->root;
    hPtr = searchAdd(searchPtr, treePtr->root);
    if (hPtr2 && hPtr && hPtr2 != hPtr)
        while (hPtr && hPtr2 != hPtr) // TODO: need a more efficient way to do this...
            hPtr = Jsi_TreeSearchNext(searchPtr);
    return hPtr;
}

Project: jsish

uint Jsi_TreeSize(Jsi_Tree *treePtr) { return treePtr->numEntries; }

Project: jsish

Jsi_Value *jsi_MakeFuncValueSpec(Jsi_Interp *interp, Jsi_CmdSpec *cmdSpec, void *privData)
{
    Jsi_Obj *o = Jsi_ObjNew(interp);
    Jsi_Func *f = jsi_FuncNew(interp);
    o->ot = JSI_OT_FUNCTION;
    f->type = FC_BUILDIN;
    f->cmdSpec = cmdSpec;
    f->callback = cmdSpec->proc;
    f->privData = privData;
    f->f.flags = (cmdSpec->flags & JSI_CMD_MASK);
    f->script = interp->curFile;
    o->d.fobj = jsi_FuncObjNew(interp, f);
    return Jsi_ValueMakeObject(interp, NULL, o);
}

Project: jsish

void Jsi_InterpOnDelete(Jsi_Interp *interp, Jsi_DeleteProc *freeProc, void *ptr)
{
    if (freeProc)
        Jsi_HashSet(interp->onDeleteTbl, ptr, (void*)freeProc);
    else {
        Jsi_HashEntry *hPtr = Jsi_HashEntryFind(interp->onDeleteTbl, ptr);
        if (hPtr)
            Jsi_HashEntryDelete(hPtr);
    }
}

Project: jsish

static int node_color(Jsi_TreeEntry* n) {
    return n == NULL ? _JSI_TREE_BLACK : n->f.bits.color;
}

Project: jsish

static Jsi_OpCodes *code_newfcall(jsi_Pstate *p, jsi_Pline *line, int argc, const char *name, Jsi_OpCodes *argCodes) {
    jsi_FuncCallCheck(p,line,argc,1, name, NULL, argCodes); JSI_NEW_CODESLN(0,OP_NEWFCALL, argc);
}

Project: jsish

int Jsi_Read(Jsi_Interp *interp, Jsi_Channel chan, char *bufPtr, int toRead) {
    if (chan->fsPtr==0 || !chan->fsPtr->readProc) return -1;
    return chan->fsPtr->readProc(chan, bufPtr, toRead);
}

Project: jsish

char* Jsi_ValueNormalPath(Jsi_Interp *interp, Jsi_Value *file, Jsi_DString *dStr) {
    return Jsi_NormalPath(interp, Jsi_ValueString(interp, file, NULL), dStr);
}

Project: jsish

static void dbDeleteCmd(Jsi_Db *jdb)
{
    Jsi_Interp *interp = jdb->interp;
    if (jdb->debug & TMODE_DELETE)
        JSI_DBQUERY_PRINTF( "DEBUG: delete\n");
    dbFlushStmtCache(jdb);
    if (jdb->stmtHash)
        Jsi_HashDelete(jdb->stmtHash);
    //closeIncrblobChannels(jdb);
    if (jdb->db) {
        DbClose(jdb->db);
    }
    while( jdb->pFunc ) {
        SqlFunc *pFunc = jdb->pFunc;
        jdb->pFunc = pFunc->pNext;
        Jsi_DSFree(&pFunc->dScript);
        Jsi_DecrRefCount(interp, pFunc->tocall);
        Jsi_Free((char*)pFunc);
    }
    while( jdb->pCollate ) {
        SqlCollate *pCollate = jdb->pCollate;
        jdb->pCollate = pCollate->pNext;
        Jsi_Free((char*)pCollate);
    }

    Jsi_OptionsFree(interp, SqlOptions, jdb, 0);
    if (jdb->stmtCache)
        Jsi_ListDelete(jdb->stmtCache);
}

Project: jsish

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

Project: jsish

Jsi_ListEntry* Jsi_ListSearchNext (Jsi_ListSearch *searchPtr)
{
    Jsi_ListEntry *lptr = searchPtr->nextEntryPtr;
    searchPtr->nextEntryPtr = (lptr?(searchPtr->flags & JSI_LIST_REVERSE ? Jsi_ListEntryPrev(lptr): Jsi_ListEntryNext(lptr)):NULL);
    return lptr;
}

Project: jsish

char * Jsi_Gets(Jsi_Interp *interp, Jsi_Channel chan, char *s, int size) {
    if (chan->fsPtr==0 || !chan->fsPtr->getsProc) return NULL;
    return chan->fsPtr->getsProc(chan, s, size);
}

Project: jsish

static Jsi_RC StringMapCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    /* Now handles perl regex flag extensions.*/
    const char *source_str;
    const char *replace_str = NULL;
    uint i, j;
    int slen, source_len, replace_len, bLen;

    const char *p;
    bool nocase = 0;
    /* Is a generic  String.replace if _this->d.obj is a function */
    ChkString(_this, funcPtr, source_str, &source_len, &bLen);
    int argc = Jsi_ValueGetLength(interp, args);
    Jsi_Value *repVal = Jsi_ValueArrayIndex(interp, args, skip);
    if (Jsi_ValueIsArray(interp, repVal)==0 || repVal->d.obj->arrCnt&1) 
        return Jsi_LogError("expected even length array");
    if (argc>(skip+1) && Jsi_ValueGetBoolean(interp, Jsi_ValueArrayIndex(interp, args, skip+1), &nocase) != JSI_OK) 
        return Jsi_LogError("expected boolean");
    Jsi_DString dStr;
    Jsi_DSInit(&dStr);
    Jsi_Obj *obj = repVal->d.obj;
    p = source_str;
    for (j=0; j<(uint)bLen; j++, p++) {
        for (i=0; i<obj->arrCnt; i+=2) {
            if (!obj->arr[i]) continue;
            if (!obj->arr[i+1]) continue;
            const char *cp = Jsi_ValueToString(interp, obj->arr[i], &slen);
            if (!cp || !slen) {
                Jsi_DSFree(&dStr);
                return Jsi_LogError("map src can not be empty");
            }
            int res = (nocase ? Jsi_Strncasecmp(cp, p, slen) : Jsi_Strncmp(cp, p, slen));
            if (!res) {
                replace_str = Jsi_ValueToString(interp, obj->arr[i+1], &replace_len);
                Jsi_DSAppendLen(&dStr, replace_str, replace_len);
                p += slen-1;
                j += slen-1;
                break;
            }
        }
        if (i>=obj->arrCnt)
            Jsi_DSAppendLen(&dStr, p, 1);
    }

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

Project: jsish

jsi_ws_http_redirect(struct lws *wsi, int code, Jsi_DString *tStr, 
                  unsigned char **p, unsigned char *end)
{
    char *loc = Jsi_DSValue(tStr);
    uchar *start = *p;
    char* cookie = Jsi_Strchr(loc, '|');
    if (cookie) { *cookie= 0; cookie++; }

    if (lws_add_http_header_status(wsi, code, p, end)
        || lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_LOCATION, (uchar *)loc, Jsi_Strlen(loc), p, end)
        || lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_TYPE,(uchar *)"text/html", 9, p,end)
        || lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_CONTENT_LENGTH, (uchar *)"0", 1, p, end))
        return -1;
    if (cookie && lws_add_http_header_by_token(wsi, WSI_TOKEN_HTTP_SET_COOKIE, (uchar *)cookie, Jsi_Strlen(cookie), p, end))
        return -1;
    if (lws_finalize_http_header(wsi, p, end))
        return -1;
    return lws_write(wsi, start, *p - start, LWS_WRITE_HTTP_HEADERS);
}

Project: jsish

static Jsi_RC SysVerConvertCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    Jsi_Value *val = Jsi_ValueArrayIndex(interp, args, 0);
    Jsi_Value *flag = Jsi_ValueArrayIndex(interp, args, 1);
    if (!val) goto bail;
    if (Jsi_ValueIsNumber(interp, val)) {
        char buf[JSI_MAX_NUMBER_STRING*2];
        Jsi_Number n;
        if (Jsi_GetNumberFromValue(interp, val, &n) != JSI_OK)
            goto bail;
        jsi_VersionNormalize(n, buf, sizeof(buf));
        int trunc = 0;
        if (flag && (Jsi_GetIntFromValue(interp, flag, &trunc) != JSI_OK
            || trunc<0 || trunc>2))
            return Jsi_LogError("arg2: bad trunc: expected int between 0 and 2");
        if (trunc) {
            int len = Jsi_Strlen(buf)-1;
            while (trunc>0 && len>1) {
                if (buf[len] == '0' && buf[len-1] == '.')
                    buf[len-1] = 0;
                len -= 2;
                trunc--;
            }
        }
        Jsi_ValueMakeStringDup(interp, ret, buf);
        return JSI_OK;
    }
    if (Jsi_ValueIsString(interp, val)) {
        Jsi_Number n;
        if (jsi_GetVerFromVal(interp, val, &n, 0) == JSI_OK) {
            Jsi_ValueMakeNumber(interp, ret, n);
            return JSI_OK;
        }
    }
bail:
    Jsi_ValueMakeNull(interp, ret);
    return JSI_OK;
}

Project: jsish

static void mdbEvalFinalize(MyDbEvalContext *p) {
    if( p->prep) {
        mdbReleaseStmt(p->jdb, p->prep, p->nocache);
        p->prep = 0;
    }
    Jsi_DSFree(p->dzSql);
}

Project: jsish

jsonGenObject(Jsi_Interp *interp, Jsi_JsonParser *p, const char *js, uint pos, uint *endPos)
{
    Jsi_Obj *obj = Jsi_ObjNewType(interp, JSI_OT_OBJECT);
    Jsi_Value *nv, *v = Jsi_ValueMakeObject(interp, NULL, obj);
    uint i, n, len;
    Jsi_JsonTok *tok;
    const char *t;
    tok = p->tokens+pos;
    for (i=pos+1, n=0; i<p->toknext && n<tok->size; i++, n++) {

        Jsi_DString dStr;
        t = Jsi_JsonGetTokstr(p, js, i, &len);
        i++; n++;
        if (n>=tok->size)
            nv = Jsi_ValueMakeUndef(interp, NULL);
        else
            nv = jsonGen1Value(interp, p, js, i, &i, 0);
        Jsi_DSInit(&dStr);
        Jsi_DSAppendLen(&dStr, t, len);
        Jsi_ObjInsert(interp, obj, Jsi_DSValue(&dStr), nv, 0);
        Jsi_DSFree(&dStr);
    }
    if (endPos)
        *endPos = i;
    return v;
}

Project: jsish

Jsi_RC Jsi_FunctionArguments(Jsi_Interp *interp, Jsi_Value *func, int *argcPtr)
{
    Jsi_FuncObj *funcPtr;
    Jsi_Func *f;
    if (!Jsi_ValueIsFunction(interp, func))
        return JSI_ERROR;
    funcPtr = func->d.obj->d.fobj;
    f = funcPtr->func;
    SIGASSERT(f, FUNC);
    *argcPtr = f->argnames->count;
    return JSI_OK;
}

Project: jsish

static int jsi_FSChmodProc(Jsi_Interp *interp, Jsi_Value* path, int mode) {
#ifdef __WIN32
    return -1;
#else
    const char *pathPtr = Jsi_ValueToString(interp, path, NULL);
    Jsi_DString dStr = {};
    if (*pathPtr == '~')
        pathPtr = jsi_TildePath(interp, pathPtr, &dStr);
    int rc = chmod(pathPtr, mode);
    Jsi_DSFree(&dStr);
    return rc;
#endif
}

Project: jsish

static Jsi_RC JSONParseCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    bool strict = 1;
    Jsi_Value *jsv = Jsi_ValueArrayIndex(interp, args, 1);
    if (jsv && Jsi_ValueGetBoolean(interp, jsv, &strict) != JSI_OK) 
        return Jsi_LogError("Expected boolean");
    jsv = Jsi_ValueArrayIndex(interp, args, 0); 
    const char *js = Jsi_ValueToString(interp, jsv, NULL);
    return Jsi_JSONParse(interp, js, ret, strict);
}

Project: jsish

static Jsi_RC jsi_ValueToEnum(Jsi_Interp *interp, Jsi_OptionSpec* spec, Jsi_Value *inValue, const char *inStr, void *record, Jsi_Wide flags)
{
    int n = 0;
    char *s = (char*)(((char*)record) + spec->offset);
    const char **list = (const char **)spec->data;
    Jsi_OptionSpec* es=NULL;
    int fflags = (flags|spec->flags);
    int cflags = (fflags&JSI_OPT_CUST_NOCASE?JSI_CMP_NOCASE:0);
    if (fflags&JSI_OPT_ENUM_EXACT)
        cflags |= JSI_CMP_EXACT;
    if (list && (fflags & JSI_OPT_ENUM_SPEC)) {
        es = (typeof(es))list;
        while (es->id != JSI_OPTION_END) es++;
        list = es->init.STRKEY;
        es = (typeof(es))spec->data;
    }
    if (!list) 
        return Jsi_LogError("custom enum spec did not set data: %s", spec->name);
    if (inStr) {
        if (JSI_OK != Jsi_GetIndex(interp, (char*)inStr, list, "enum", cflags, &n))
            return JSI_ERROR;
    } else
#ifndef JSI_LITE_ONLY
    if (JSI_OK != Jsi_ValueGetIndex(interp, inValue, list, "enum", cflags, &n))
        return JSI_ERROR;

#endif
    if (fflags&JSI_OPT_ENUM_UNSIGNED) {
        uint64_t u = (uint64_t)n;
        if (es)
            u = es[n].value;
        switch (spec->size) {
            case 1: *(uint8_t*)s = (uint8_t)u; break;
            case 2: *(uint16_t*)s = (uint16_t)u; break;
            case 4: *(uint32_t*)s = (uint32_t)u; break;
            case 8: *(uint64_t*)s = (uint64_t)u; break;
            default:
                return Jsi_LogError("enum size must be 1, 2, 4, or 8: %s", spec->name);
        }
    } else {
        int64_t m = n;
        if (es)
            m = es[n].value;
        switch (spec->size) {
            case 1: *(int8_t*)s = (int8_t)m; break;
            case 2: *(int16_t*)s = (int16_t)m; break;
            case 4: *(int32_t*)s = (int32_t)m; break;
            case 8: *(int64_t*)s = (int64_t)m; break;
            default: 
                return Jsi_LogError("enum size must be 1, 2, 4, or 8: %s", spec->name);
        }
    }
    
    return JSI_OK;
}

Project: jsish

static Jsi_RC dbEvalStepSub(DbEvalContext *p, int release, int *erc) {
    int rcs;
    Jsi_Db *jdb = p->jdb;
    Jsi_Interp *interp = jdb->interp;
    JSI_NOTUSED(interp);
    SqlPreparedStmt *pPreStmt = p->pPreStmt;
    SQLSIGASSERT(pPreStmt, STMT);
    sqlite3_stmt *pStmt = pPreStmt->pStmt;

    if (jdb->debug & TMODE_STEP)
        JSI_DBQUERY_PRINTF( "DEBUG: step: %s\n", pPreStmt->zSql);
    rcs = sqlite3_step(pStmt);
    if( rcs==SQLITE_BUSY ) {
        if (erc) *erc = -2;
        return JSI_ERROR;
    }
    if( rcs==SQLITE_ROW ) {
        return JSI_OK;
    }
    if( p->pArray ) {
        dbEvalRowInfo(p, 0, 0, 0);
    }
    rcs = sqlite3_reset(pStmt);

    jdb->stepCnt = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_FULLSCAN_STEP,1);
    jdb->sortCnt = sqlite3_stmt_status(pStmt,SQLITE_STMTSTATUS_SORT,1);
    if (release==0 && rcs==SQLITE_OK)
        return JSI_BREAK;
    dbReleaseColumnNames(p);
    p->pPreStmt = 0;

    if( rcs!=SQLITE_OK ) {
        /* If a run-time error occurs, report the error and stop reading
        ** the SQL.  */
        Jsi_LogError("%s", sqlite3_errmsg(jdb->db));
        dbReleaseStmt(jdb, pPreStmt, 1);
        return JSI_ERROR;
    } else {
        dbReleaseStmt(jdb, pPreStmt, p->nocache);
    }
    return JSI_BREAK;
}

Project: jsish

static void MutexDone(Jsi_Mutex *mtx) { pthread_mutex_destroy(&mtx->mtx); }

Project: jsish

jsi_ScopeChain *jsi_ScopeChainNew(Jsi_Interp *interp, int cnt)
{
    jsi_ScopeChain *r = (jsi_ScopeChain *)Jsi_Calloc(1, sizeof(*r));
    r->interp = interp;
    SIGINIT(r,SCOPE);
    r->chains = (Jsi_Value **)Jsi_Calloc(cnt, sizeof(r->chains[0]));
    r->chains_cnt = cnt;
    return r;
}

Project: jsish

Jsi_Value *jsi_LoadFunction(Jsi_Interp *interp, const char *str, Jsi_Value *tret) {
    Jsi_DString dStr = {};
    Jsi_Value *v;
    int i;
    const char *curFile = interp->curFile;
    interp->curFile = "<jsiLoadFunction>";
    for (i=0; i<2; i++) {
        Jsi_DSAppend(&dStr, "Jsi_Auto.", str, NULL);
        Jsi_VarLookup(interp, Jsi_DSValue(&dStr));
        v = Jsi_NameLookup(interp, Jsi_DSValue(&dStr));
        if (v)
            jsi_ValueDebugLabel(v, "jsiLoadFunction","f1");
        Jsi_DSFree(&dStr);
        if (v) {
            const char *cp = Jsi_ValueGetDString(interp, v, &dStr, 0);
            v = NULL;
            if (Jsi_EvalString(interp, cp, 0) == JSI_OK) {
                v = Jsi_NameLookup(interp, str);
                if (v)
                    jsi_ValueDebugLabel(v, "jsiLoadFunction","f2");
            }
            Jsi_DSFree(&dStr);
            if (v) {
                tret = v;
                break;
            }
        }
        if (interp->autoLoaded++ || i>0)
            break;
        /*  Index not in memory, so try loading Jsi_Auto from the autoload.jsi file. */
        if (interp->autoFiles == NULL)
            return tret;
        Jsi_Value **ifs = &interp->autoFiles;
        int i, ifn = 1;
        if (Jsi_ValueIsArray(interp, interp->autoFiles)) {
            ifs = interp->autoFiles->d.obj->arr;
            ifn = interp->autoFiles->d.obj->arrCnt;
        }
        for (i=0; i<ifn; i++) {  
            if (Jsi_EvalFile(interp, ifs[i], 0) != JSI_OK)
                break;
            interp->autoLoaded++;
        }
    }
    interp->curFile = curFile;
    return tret;
}

Project: jsish

static Jsi_RC mdbEvalStepSub(MyDbEvalContext *eval, int release, int *erc) {
    MySqlObj *jdb = eval->jdb;
    Jsi_Interp *interp = jdb->interp;
    MysqlPrep *prep = eval->prep;
    SQLSIGASSERT(prep, STMT);
    MYSQL_STMT *myStmt = prep->myStmt;

    if (jdb->debug & mdbTMODE_STEP)
        JSI_DBQUERY_PRINTF( "DEBUG: step: %s\n", prep->zSql);
    int m = mysql_stmt_fetch(myStmt);
    if (m == MYSQL_NO_DATA)
        return JSI_BREAK;
    if (m) 
        return Jsi_LogError("fetch failed: %s", mysql_error(jdb->db));
    return JSI_OK;
#if 0
    if( eval->pArray ) {
        mdbEvalRowInfo(eval, 0, 0, 0);
    }
    if (release==0)
        return JSI_BREAK;
    eval->prep = 0;
    return JSI_OK;
#endif
}

Project: jsish

static int dbExecCmd(Jsi_Db *jdb, const char *zQuery, int *erc)
{
    int rc = sqlite3_exec(jdb->db, zQuery, 0, 0, 0);
    if (rc == SQLITE_BUSY) {
        if (erc) *erc = -2;
    } else if (rc != SQLITE_OK) {
        if (erc) *erc = -1;
    } else
        return 1;
    return 0;
}

Project: jsish

static int dbWalHandler( void *cd, sqlite3 *db, const char *zDb, int nEntry ){
    int rc;
    Jsi_Db *jdb = (Jsi_Db*)cd;
    Jsi_Interp *interp = jdb->interp;
    Jsi_Value *vpargs, *items[3] = {}, *ret;

    items[0] = Jsi_ValueNewObj(interp, jdb->fobj);
    items[1] = Jsi_ValueMakeStringDup(interp, NULL, zDb);
    items[2] = Jsi_ValueMakeNumber(interp, NULL, (Jsi_Number)nEntry);
    vpargs = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, items, 3, 0));
    Jsi_IncrRefCount(interp, vpargs);
    ret = Jsi_ValueNew(interp);
    rc = Jsi_FunctionInvoke(interp, jdb->onWalHook, vpargs, &ret, NULL);
    Jsi_DecrRefCount(interp, vpargs);
    if (rc != JSI_OK) {
        jdb->errCnt++;
        rc = 1;
    } else
        rc = dbGetIntBool(jdb->interp, ret);
    Jsi_DecrRefCount(interp, ret);
    return rc;
}

Project: jsish

Jsi_RC jsi_InitValue(Jsi_Interp *interp, int release)
{
    return JSI_OK;
}

Project: jsish

Jsi_HashSearchFirst(Jsi_Hash *tablePtr, Jsi_HashSearch *searchPtr) 
{
    searchPtr->tablePtr = tablePtr;
    searchPtr->nextIndex = 0;
    searchPtr->nextEntryPtr = NULL;
    return Jsi_HashSearchNext(searchPtr);
}

Project: jsish

static void dbFlushStmtCache( Jsi_Db *jdb ) {
    Jsi_ListClear(jdb->stmtCache);
    jdb->stmtCacheCnt = 0;
}

Project: jsish

static Jsi_RC dbPrepareAndBind(
    Jsi_Db *jdb,                  /* Database object */
    char const *zIn,                /* SQL to compile */
    char const **pzOut,             /* OUT: Pointer to next SQL statement */
    SqlPreparedStmt **ppPreStmt     /* OUT: Object used to cache statement */
) {
    if (dbPrepareStmt(jdb, zIn, pzOut, ppPreStmt) != JSI_OK)
        return JSI_ERROR;
    return dbBindStmt(jdb, *ppPreStmt);
}

Project: jsish

Jsi_Wide Jsi_Tell(Jsi_Interp *interp, Jsi_Channel chan) {
    if (chan->fsPtr==0 || !chan->fsPtr->tellProc) return -1;
    return chan->fsPtr->tellProc(chan);
}

Project: jsish

static jsi_CaseExprStat *exprstat_new(jsi_Pstate *pstate, Jsi_OpCodes *expr, Jsi_OpCodes *stat, int isdef)
{
    jsi_CaseExprStat *r = (jsi_CaseExprStat*)Jsi_Calloc(1,sizeof(*r));
    r->sig = JSI_SIG_CASESTAT;
    r->expr = expr;
    r->stat = stat;
    r->isdefault = isdef;
    return r;
}

Project: jsish

HashStringCreate( Jsi_Hash *tablePtr, const void *key, bool *newPtr)
{
    jsi_Hash hval;
    Jsi_HashEntry **bucketPtr;
    Jsi_HashEntry *hPtr;
    size_t size, hindex;

    hval = HashString((const char*)key);
    hindex = hval & tablePtr->mask;

    for (hPtr = tablePtr->buckets[hindex]; hPtr != NULL;
            hPtr = hPtr->nextPtr) {
        if (hPtr->hval == hval) {
            const char *p1, *p2;

            for (p1 = (const char*)key, p2 = hPtr->key.string; ; p1++, p2++) {
                if (*p1 != *p2) {
                    break;
                }
                if (*p1 == '\0') {
                    if (newPtr)
                        *newPtr = 0;
                    return hPtr;
                }
            }
        }
    }

    if (newPtr)
        *newPtr = 1;
    size = sizeof(Jsi_HashEntry) + Jsi_Strlen((char*)key) - sizeof(jsi_HashKey) + 1;
    hPtr = (Jsi_HashEntry*)Jsi_Calloc(1, size);
    hPtr->typ = JSI_MAP_HASH;
    bucketPtr = tablePtr->buckets + hindex;
    hPtr->tablePtr = tablePtr;
    hPtr->nextPtr = *bucketPtr;
    hPtr->hval = hval;
    hPtr->clientData = 0;
    Jsi_Strcpy(hPtr->key.string, (char*)key);
    *bucketPtr = hPtr;
    tablePtr->numEntries++;

    if (tablePtr->numEntries >= tablePtr->rebuildSize)
        RebuildTable(tablePtr);
        
    return hPtr;
}

Project: jsish

static const char* jsw_getReasonStr(enum lws_callback_reasons reason) {
    typedef struct { enum lws_callback_reasons r; const char *name; } ssType;
    static ssType ss[] = {
#define MKLCBS(n) { n, #n }
    MKLCBS(LWS_CALLBACK_PROTOCOL_INIT),
    MKLCBS(LWS_CALLBACK_PROTOCOL_DESTROY), MKLCBS(LWS_CALLBACK_WSI_CREATE),
    MKLCBS(LWS_CALLBACK_WSI_DESTROY),MKLCBS(LWS_CALLBACK_OPENSSL_LOAD_EXTRA_CLIENT_VERIFY_CERTS),
    MKLCBS(LWS_CALLBACK_OPENSSL_LOAD_EXTRA_SERVER_VERIFY_CERTS),
    MKLCBS(LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION),
    MKLCBS(LWS_CALLBACK_OPENSSL_CONTEXT_REQUIRES_PRIVATE_KEY),
    MKLCBS(LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED), MKLCBS(LWS_CALLBACK_HTTP),
    MKLCBS(LWS_CALLBACK_HTTP_BODY), MKLCBS(LWS_CALLBACK_HTTP_BODY_COMPLETION),
    MKLCBS(LWS_CALLBACK_HTTP_FILE_COMPLETION), MKLCBS(LWS_CALLBACK_HTTP_WRITEABLE),
    MKLCBS(LWS_CALLBACK_CLOSED_HTTP), MKLCBS(LWS_CALLBACK_FILTER_HTTP_CONNECTION),
    MKLCBS(LWS_CALLBACK_ESTABLISHED),
    MKLCBS(LWS_CALLBACK_CLOSED),
    MKLCBS(LWS_CALLBACK_SERVER_WRITEABLE),
    MKLCBS(LWS_CALLBACK_RECEIVE), MKLCBS(LWS_CALLBACK_RECEIVE_PONG),
    MKLCBS(LWS_CALLBACK_WS_PEER_INITIATED_CLOSE), MKLCBS(LWS_CALLBACK_FILTER_PROTOCOL_CONNECTION),
    MKLCBS(LWS_CALLBACK_CONFIRM_EXTENSION_OKAY),
    MKLCBS(LWS_CALLBACK_CLIENT_CONNECTION_ERROR),
    MKLCBS(LWS_CALLBACK_CLIENT_FILTER_PRE_ESTABLISH), MKLCBS(LWS_CALLBACK_CLIENT_ESTABLISHED),
    MKLCBS(LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER), MKLCBS(LWS_CALLBACK_CLIENT_RECEIVE),
    MKLCBS(LWS_CALLBACK_CLIENT_RECEIVE_PONG),
    MKLCBS(LWS_CALLBACK_CLIENT_WRITEABLE), MKLCBS(LWS_CALLBACK_CLIENT_CONFIRM_EXTENSION_SUPPORTED),
    MKLCBS(LWS_CALLBACK_WS_EXT_DEFAULTS), MKLCBS(LWS_CALLBACK_FILTER_NETWORK_CONNECTION),
    MKLCBS(LWS_CALLBACK_GET_THREAD_ID), MKLCBS(LWS_CALLBACK_ADD_POLL_FD),
    MKLCBS(LWS_CALLBACK_DEL_POLL_FD), MKLCBS(LWS_CALLBACK_CHANGE_MODE_POLL_FD), MKLCBS(LWS_CALLBACK_LOCK_POLL),
    MKLCBS(LWS_CALLBACK_UNLOCK_POLL),
#if (LWS_LIBRARY_VERSION_MAJOR>1)
    MKLCBS(LWS_CALLBACK_CGI),
    MKLCBS(LWS_CALLBACK_CGI_TERMINATED),
    MKLCBS(LWS_CALLBACK_CGI_STDIN_DATA),
    MKLCBS(LWS_CALLBACK_CGI_STDIN_COMPLETED),
    MKLCBS(LWS_CALLBACK_SESSION_INFO),
    MKLCBS(LWS_CALLBACK_GS_EVENT),
    MKLCBS(LWS_CALLBACK_HTTP_PMO),
    MKLCBS(LWS_CALLBACK_RAW_RX),
    MKLCBS(LWS_CALLBACK_RAW_CLOSE),
    MKLCBS(LWS_CALLBACK_RAW_WRITEABLE),
    MKLCBS(LWS_CALLBACK_RAW_ADOPT),
    MKLCBS(LWS_CALLBACK_RAW_ADOPT_FILE),
    MKLCBS(LWS_CALLBACK_ADD_HEADERS),
    MKLCBS(LWS_CALLBACK_CHECK_ACCESS_RIGHTS),
    MKLCBS(LWS_CALLBACK_PROCESS_HTML),
    MKLCBS(LWS_CALLBACK_ESTABLISHED_CLIENT_HTTP),
    MKLCBS(LWS_CALLBACK_CLOSED_CLIENT_HTTP),
    MKLCBS(LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ),
    MKLCBS(LWS_CALLBACK_RECEIVE_CLIENT_HTTP),
    MKLCBS(LWS_CALLBACK_COMPLETED_CLIENT_HTTP),
    MKLCBS(LWS_CALLBACK_CLIENT_HTTP_WRITEABLE),
    MKLCBS(LWS_CALLBACK_HTTP_BIND_PROTOCOL),
    MKLCBS(LWS_CALLBACK_HTTP_DROP_PROTOCOL),
    MKLCBS(LWS_CALLBACK_OPENSSL_PERFORM_SERVER_CERT_VERIFICATION),
    MKLCBS(LWS_CALLBACK_RAW_RX_FILE),
    MKLCBS(LWS_CALLBACK_RAW_WRITEABLE_FILE),
    MKLCBS(LWS_CALLBACK_RAW_CLOSE_FILE),
    MKLCBS(LWS_CALLBACK_USER),
#endif
#if (LWS_LIBRARY_VERSION_NUMBER>=3000000)
    MKLCBS(LWS_CALLBACK_SSL_INFO),
    MKLCBS(LWS_CALLBACK_CGI_PROCESS_ATTACH),
    MKLCBS(LWS_CALLBACK_CLIENT_CLOSED),
    MKLCBS(LWS_CALLBACK_TIMER),
    MKLCBS(LWS_CALLBACK_EVENT_WAIT_CANCELLED),
    MKLCBS(LWS_CALLBACK_CHILD_CLOSING),
    MKLCBS(LWS_CALLBACK_CHILD_WRITE_VIA_PARENT),
    MKLCBS(LWS_CALLBACK_VHOST_CERT_AGING),
    MKLCBS(LWS_CALLBACK_VHOST_CERT_UPDATE),
#endif
    {(enum lws_callback_reasons)0, NULL }
    };
    int i = -1;
    while (ss[++i].name)
        if (ss[i].r == reason)
            return ss[i].name;
    return "";
}

Project: jsish

const char *Jsi_ObjTypeStr(Jsi_Interp *interp, Jsi_Obj *o)
{
     switch (o->ot) {
        case JSI_OT_BOOL: return "boolean"; break;
        case JSI_OT_FUNCTION: return "function"; break;
        case JSI_OT_NUMBER: return "number"; break;
        case JSI_OT_STRING: return "string"; break;  
        case JSI_OT_REGEXP: return "regexp"; break;  
        case JSI_OT_ITER: return "iter"; break;  
        case JSI_OT_OBJECT: if (!o->isarrlist) return "object";
        case JSI_OT_ARRAY: return "array"; break;  
        case JSI_OT_USEROBJ:
            if (o->__proto__) {
                Jsi_HashEntry *hPtr;
                Jsi_HashSearch search;
                            
                for (hPtr = Jsi_HashSearchFirst(interp->thisTbl,&search); hPtr != NULL;
                    hPtr = Jsi_HashSearchNext(&search))
                    if (Jsi_HashValueGet(hPtr) == o->__proto__)
                        return (char*)Jsi_HashKeyGet(hPtr);
            }
            
            return "userobj";
            break;
            //return Jsi_ObjGetType(interp, v->d.obj);
        default:
            break;
     }
     return "";
}

Project: jsish

static void delete_case2(Jsi_TreeEntry* n) {
    Jsi_TreeEntry* s = sibling(n);
    if (node_color(s) == _JSI_TREE_RED) {
        set_color(n->parent, _JSI_TREE_RED);
        set_color(s, _JSI_TREE_BLACK);
        if (n == n->parent->left)
            rotate_left(n->parent);
        else
            rotate_right(n->parent);
    }
    delete_case3(n);
}

Project: jsish

static Jsi_RC jsi_BitsetToValue(Jsi_Interp *interp, Jsi_OptionSpec* spec, Jsi_Value **outValue, Jsi_DString *outStr, void *record, Jsi_Wide flags)
{
    int i, n, cnt = 0, *s = (int*)(((char*)record) + spec->offset);
    const char **list = (const char**)spec->data;
    if (!list) 
        return Jsi_LogError("custom enum spec did not set data: %s", spec->name);
    if (outStr) {
        n = *s;
        for (i=0; list[i]; i++) {
            if (i >= (int)(spec->size*8)) 
                return Jsi_LogError("list larger than field size: %s", spec->name);
            if (!(n & (1<<i)))
                continue;
            if (cnt++)
                Jsi_DSAppendLen(outStr, ", ", 1);
            Jsi_DSAppendLen(outStr, list[i], -1);
        }
        return JSI_OK;
    }
#ifndef JSI_LITE_ONLY
    Jsi_Obj *obj = Jsi_ObjNewType(interp, JSI_OT_ARRAY);
    n = *s;
    for (i=0; list[i]; i++) {
        Jsi_Value *v;
        if (!(n&(1<<i))) continue;
        cnt++;
        v = Jsi_ValueMakeStringKey(interp, NULL, list[i]);
        Jsi_ObjArrayAdd(interp, obj, v);
    }
    Jsi_ValueMakeArrayObject(interp, outValue, obj);
#endif
    return JSI_OK;
}

Project: jsish

void Jsi_MapSearchDone (Jsi_MapSearch *searchPtr){
    switch (searchPtr->typ) {
        case JSI_MAP_HASH: break;
        case JSI_MAP_TREE: Jsi_TreeSearchDone(&searchPtr->v.tree); break;
        case JSI_MAP_LIST: break;
        case JSI_MAP_NONE: break;
    }
}

Project: jsish

Jsi_RC jsi_evalStrFile(Jsi_Interp* interp, Jsi_Value *path, const char *str, int flags, int level)
{
    Jsi_Channel tinput = NULL, input = Jsi_GetStdChannel(interp, 0);
    Jsi_Value *npath = path;
    Jsi_RC rc = JSI_ERROR;
    const char *ustr = NULL, *ostr = str;
    if (Jsi_MutexLock(interp, interp->Mutex) != JSI_OK)
        return rc;
    int oldSp, uskip = 0, fncOfs = 0, fnLen;
    int oldef = interp->evalFlags;
    jsi_Pstate *oldps = interp->ps;
    const char *oldFile = interp->curFile;
    char *origFile = Jsi_ValueString(interp, path, &fnLen);
    const char *fname = origFile;
    char *oldDir = interp->curDir, *cp;
    char dirBuf[PATH_MAX];
    jsi_Pstate *ps = NULL;
    jsi_FileInfo *fi = NULL;
    int exists = (flags&JSI_EVAL_EXISTS);
    int ignore = (flags&JSI_EVAL_ERRIGNORE);
    if (flags & JSI_EVAL_GLOBAL)
        level = 1;
    int oisi = interp->isMain;
    if (flags & JSI_EVAL_ISMAIN)
        interp->isMain = 1;
    
    oldSp = interp->framePtr->Sp;
    dirBuf[0] = 0;
    Jsi_DString dStr, fStr;
    Jsi_DSInit(&dStr);
    Jsi_DSInit(&fStr);

    if (str == NULL) {
        if (fname != NULL) {
            if (fnLen>2 && fname[fnLen-1]=='/') {
                Jsi_DSAppendLen(&fStr, fname, fnLen-1);
                const char *fcp = Jsi_DSValue(&fStr), *fce = strrchr(fcp,'/');
                if (fce) {
                    fname = Jsi_DSAppend(&fStr, fce, ".jsi", NULL);
                    npath = Jsi_ValueNewStringDup(interp, fname);
                    Jsi_IncrRefCount(interp, npath);
                }
            }
            if (!Jsi_Strcmp(fname,"-"))
                input = Jsi_GetStdChannel(interp, 0);
            else {
                /* Use translated FileName. */
                if (interp->curDir && fname[0] != '/' && fname[0] != '~') {
                    char dirBuf2[PATH_MAX], *np;
                    snprintf(dirBuf, sizeof(dirBuf), "%s/%s", interp->curDir, fname);
                    if ((np=Jsi_FileRealpathStr(interp, dirBuf, dirBuf2)) == NULL) {
                        Jsi_LogError("Can not open '%s'", fname);
                        goto bail;
                    }
                    if (npath != path)
                        Jsi_IncrRefCount(interp, npath);
                    npath = Jsi_ValueNewStringDup(interp, np);
                    Jsi_IncrRefCount(interp, npath);
                    fname = Jsi_ValueString(interp, npath, NULL);
                }
                if (flags&JSI_EVAL_ARGV0) {
                    if (interp->argv0)
                        Jsi_DecrRefCount(interp, interp->argv0);
                    interp->argv0 = Jsi_ValueNewStringDup(interp, fname);
                    Jsi_IncrRefCount(interp, interp->argv0);
                }
                
                bool osafe = interp->isSafe;
                if (interp->startSafe  && flags&JSI_EVAL_ARGV0) {
                    if (interp->safeReadDirs || interp->safeMode == jsi_safe_None)
                        interp->isSafe = 0;
                    else {
                        char vds[PATH_MAX], *cp;
                        const char *vda[2] = {vds};
                        Jsi_Strncpy(vds, Jsi_ValueString(interp, npath, NULL), sizeof(vds)-1);
                        vds[sizeof(vds)-1] = 0;
                        cp = Jsi_Strrchr(vds, '/');
                        if (cp)
                            cp[1] = 0;
                        Jsi_DString pStr = {};
                        vda[1] = Jsi_GetCwd(interp, &pStr);
                        interp->safeReadDirs = Jsi_ValueNewArray(interp, vda, 2);
                        Jsi_IncrRefCount(interp, interp->safeReadDirs);
                        if (interp->safeMode == jsi_safe_WriteRead || interp->safeMode == jsi_safe_Lockdown) {
                            if (!interp->safeWriteDirs) {
                                interp->safeWriteDirs = interp->safeReadDirs;
                                Jsi_IncrRefCount(interp, interp->safeWriteDirs);
                            }
                        } else if (interp->safeMode == jsi_safe_Write) {
                            interp->safeWriteDirs = Jsi_ValueNewArray(interp, vda+1, 1);
                            Jsi_IncrRefCount(interp, interp->safeWriteDirs);
                        }
                        Jsi_DSFree(&pStr);
                    }
                }
                tinput = input = Jsi_Open(interp, npath, (exists?"-r":"r"));
                interp->isSafe = osafe;
                if (!input) {
                    if (exists)
                        rc = JSI_OK;
                    //Jsi_LogError("Can not open '%s'", fname);
                    goto bail;
                }
            }
            cp = Jsi_Strrchr(fname, '.');
            if (cp && !Jsi_Strcmp(cp, ".jsi") && interp->isMain) {
                interp->typeCheck.parse = interp->typeCheck.run = interp->typeCheck.all = 1;
                interp->strict = 1;
            }
            bool isNew;
            Jsi_HashEntry *hPtr;
            hPtr = Jsi_HashEntryNew(interp->fileTbl, fname, &isNew);
            if (isNew == 0 && hPtr) {
                if ((flags & JSI_EVAL_ONCE)) {
                    rc = JSI_OK;
                    goto bail;
                }
                fi = (jsi_FileInfo *)Jsi_HashValueGet(hPtr);
                if (!fi) goto bail;
                interp->curFile = fi->fileName;
                interp->curDir = fi->dirName;
                
            } else {
                fi = (jsi_FileInfo *)Jsi_Calloc(1,sizeof(*fi));
                if (!fi) goto bail;
                Jsi_HashValueSet(hPtr, fi);
                fi->origFile = (char*)Jsi_KeyAdd(interp, origFile);
                interp->curFile = fi->fileName = (char*)Jsi_KeyAdd(interp, fname);
                char *dfname = Jsi_Strdup(fname);
                if ((cp = Jsi_Strrchr(dfname,'/')))
                    *cp = 0;
                interp->curDir = fi->dirName = (char*)Jsi_KeyAdd(interp, dfname);
                Jsi_Free(dfname);
            }
            if (!input->fname)
                input->fname = interp->curFile;

            int cnt = 0, noncmt = 0, jppOpts[4]={};
            uint ilen;
            char buf[JSI_BUFSIZ*2];
            const char *jpp;
            while (cnt<MAX_LOOP_COUNT) {
                if (!Jsi_Gets(interp, input, buf, sizeof(buf)))
                    break;
                if (++cnt==1 && (!(flags&JSI_EVAL_NOSKIPBANG)) && (buf[0] == '#' && buf[1] == '!')) {
                    Jsi_DSAppend(&dStr, "\n", NULL);
                    uskip=1;
                    continue;
                }
                if (!noncmt) {
                    int bi;
                    if (!buf[0] || (buf[0] == '/' && buf[1] == '/'))
                        goto cont;
                    for (bi=0; buf[bi]; bi++) if (!isspace(buf[bi])) break;
                    if (!buf[bi])
                        goto cont;
                }
                if (!noncmt++)
                    fncOfs = Jsi_DSLength(&dStr)-uskip;
                jpp = interp->jsppChars;
                if (jpp || interp->unitTest)
                    ilen = Jsi_Strlen(buf);
                if (interp->unitTest && buf[0]==';' && buf[1] && buf[2]) {
                    if (interp->unitTest&1 && jsi_JsPreprocessLine(interp, buf, sizeof(buf), ilen, jppOpts, cnt) != JSI_OK)
                        goto bail;
                } else if (interp->jsppCallback && interp->jsppChars) {
                    if (jsi_JsPreprocessLineCB(interp, buf, sizeof(buf), ilen, jppOpts, cnt) != JSI_OK)
                        goto bail;
                }
cont:
                Jsi_DSAppend(&dStr, buf,  NULL);
            }
            if (cnt>=MAX_LOOP_COUNT)
                Jsi_LogError("source file too large");
            str = Jsi_DSValue(&dStr);

        }
        if (interp->curDir && (flags&JSI_EVAL_AUTOINDEX))
            Jsi_AddAutoFiles(interp, interp->curDir);
    }
    ustr = str + fncOfs;
    // See if "use XXX" is on first non // or empty line (or second if there is a #! on first line)
    if (ustr && *ustr && !Jsi_Strncmp(ustr+uskip, "\"use ", 5)) {
        ustr += 5+uskip;
        const char *cpe = ustr;
        while (*cpe && *cpe != '\"' && *cpe != '\n' && (isalpha(*cpe) || *cpe ==',' || *cpe =='!')) cpe++;
        if (*cpe == '\"') {
            Jsi_DString cStr;
            Jsi_DSInit(&cStr);
            cpe = Jsi_DSAppendLen(&cStr, ustr, (cpe-ustr));
            rc = jsi_ParseTypeCheckStr(interp, cpe);
            Jsi_DSFree(&cStr);
            if (rc != JSI_OK)
                goto bail;
        }
    }

    /* TODO: cleanup interp->framePtr->Sp stuff. */
    oldSp = interp->framePtr->Sp;
    // Evaluate code.
    rc = JSI_OK;
    ps = jsiNewParser(interp, str, input, 0);
    interp->evalFlags = flags;
    if (!ps)
        rc = JSI_ERROR;
    else {
        Jsi_ValueMakeUndef(interp, &interp->retValue);
        interp->ps = ps;
        Jsi_Value *retValue = interp->retValue;
        if (!interp->strict)
            interp->strict = (jsi_GetDirective(interp, ps->opcodes, "use strict")!=NULL);
        const char *curFile = interp->curFile;

        if (level <= 0)
            rc = jsi_evalcode(ps, NULL, ps->opcodes, interp->gsc, interp->csc, interp->csc, &retValue);
        else {
            jsi_Frame *fptr = interp->framePtr;
            while (fptr && fptr->level > level)
                fptr = fptr->parent;
            if (!fptr)
                rc = JSI_ERROR;
            else
                rc = jsi_evalcode(ps, NULL, ps->opcodes, fptr->ingsc, fptr->incsc, fptr->inthis, &retValue);
        }
        interp->curFile = curFile;
        if (rc != JSI_OK)
            rc = (ignore?JSI_OK:JSI_ERROR);
        else if (ps->last_exception || oldps->last_exception)
            Jsi_ValueDup2(interp, &oldps->last_exception, ps->last_exception); //TODO: dup even if null?
        interp->ps = oldps;
        interp->evalFlags = oldef;
        if (rc == JSI_OK && ps && !ostr)
            fi->str = ps->lexer->d.str;
    }
    
bail:
    interp->curFile = oldFile;
    interp->curDir = oldDir;
    interp->framePtr->Sp = oldSp;
    interp->isMain = oisi;
    if (path != npath)
        Jsi_DecrRefCount(interp, npath);
    Jsi_DSFree(&dStr);
    Jsi_DSFree(&fStr);
    if (tinput)
        Jsi_Close(interp, tinput);
    Jsi_MutexUnlock(interp, interp->Mutex);
    if (interp->exited && interp->level <= 0)
    {
        rc = JSI_EXIT;
        if (!interp->parent)
            Jsi_InterpDelete(interp);
    }

    return rc;
}

Project: jsish

static void DeBackSlashify(char *cp) {
    char *dp = cp;
    while (*cp) {
        if (*cp == '\\') {
            *dp = '/';
        } else
            *dp = *cp;
        cp++; dp++;
    }
    *dp = 0;
}

Project: jsish

static Jsi_OpCodes *code_ewith(jsi_Pstate *p, jsi_Pline *line) { JSI_NEW_CODESLN(0,OP_EWITH, 0); }

Project: jsish

static int jsi_wsService(jsi_wsCmdObj *cmdPtr)
{
    int n = 0;
    struct timeval tv;
    if (cmdPtr->inUpdate) return 0;
    cmdPtr->inUpdate = 1;

    gettimeofday(&tv, NULL);
    int to = cmdPtr->recvBufTimeout;
    if (to>0 && cmdPtr->recvBufCnt) { // Flush buffered data.
        jsi_wsPss *pss = NULL;
        Jsi_HashEntry *hPtr;
        Jsi_HashSearch cursor;
        for (hPtr = Jsi_HashSearchFirst(cmdPtr->pssTable, &cursor);
            hPtr != NULL; hPtr = Jsi_HashSearchNext(&cursor)) {
            pss = (jsi_wsPss*)Jsi_HashValueGet(hPtr);
            WSSIGASSERT(pss, PWS);
            if (pss->state == PWS_DEAD) continue;
            if (Jsi_DSLength(&pss->recvBuf)<=0) continue;
            if (pss->stats.recvLast && difftime(time(NULL), pss->stats.recvLast)<(double)to) continue;
            jsi_wsrecv_flush(cmdPtr, pss);
        }
    }

    /*
     * This provokes the LWS_CALLBACK_SERVER_WRITEABLE for every
     * live websocket connection using the DUMB_INCREMENT protocol,
     * as soon as it can take more packets (usually immediately)
     */

    if (((unsigned int)tv.tv_usec - cmdPtr->oldus) > 50000) {
        lws_callback_on_writable_all_protocol(cmdPtr->context, &cmdPtr->protocols[JWS_PROTOCOL_WEBSOCK]);
        cmdPtr->oldus = tv.tv_usec;
    }

#ifdef EXTERNAL_POLL

    /*
     * this represents an existing server's single poll action
     * which also includes libwebsocket sockets
     */

    n = poll(jsi_wspollfds, jsi_wsnum_pollfds, 50);
    if (n < 0) {
        n = 0;
        goto done;
    }

    if (n)
        for (n = 0; n < jsi_wsnum_pollfds; n++)
            if (jsi_wspollfds[n].revents)
                /*
                * returns immediately if the fd does not
                * match anything under libwebsockets
                * control
                */
                if (lws_service_fd(context, &jsi_wspollfds[n]) < 0) {
                    n = -1;
                    goto done;
                }
done:
#else
    n = lws_service(cmdPtr->context, 50);
#endif
    if (cmdPtr->onModify) {
        jsi_wsOnModify(cmdPtr);
    }
    cmdPtr->inUpdate = 0;
    return n;
}

Project: jsish

static int jsi_wscallback_http(struct lws *wsi,
                         enum lws_callback_reasons reason, void *user,
                         void *in, size_t len)
{
    struct lws_context *context = lws_get_context(wsi);
    const char *inPtr = (char*)in;
    char client_name[128], client_ip[128];
    const char *res = "";
#ifdef EXTERNAL_POLL
    int m;
    int fd = (int)(long)user;
#endif
    jsi_wsCmdObj *cmdPtr = (jsi_wsCmdObj *)lws_context_user(context);
    if (!cmdPtr) {
        fprintf(stderr, "bad ws context\n");
        return -1;
    }
    jsi_wsPss *pss = NULL;
    Jsi_Interp *interp = cmdPtr->interp;
    Jsi_Value* callPtr = NULL;
    int rc = 0, deflt = 0;

    WSSIGASSERT(cmdPtr, OBJ);
    if (Jsi_InterpGone(interp))
        cmdPtr->deleted = 1;

    if (cmdPtr->debug>=128)
        fprintf(stderr, "HTTP CALLBACK: len=%d, %p %d:%s\n", (int)len, user, reason, jsw_getReasonStr(reason));

    switch (reason) {
#ifndef EXTERNAL_POLL
    case LWS_CALLBACK_GET_THREAD_ID:
    case LWS_CALLBACK_UNLOCK_POLL:
    case LWS_CALLBACK_PROTOCOL_INIT:
    case LWS_CALLBACK_ADD_POLL_FD:
    case LWS_CALLBACK_DEL_POLL_FD:
    case LWS_CALLBACK_CHANGE_MODE_POLL_FD:
    case LWS_CALLBACK_LOCK_POLL:
        return rc;
#else
        /*
         * callbacks for managing the external poll() array appear in
         * protocol 0 callback
         */

    case LWS_CALLBACK_ADD_POLL_FD:

        if (jsi_wsnum_pollfds >= max_poll_elements) {
            lwsl_err("LWS_CALLBACK_ADD_POLL_FD: too many sockets to track\n");
            return 1;
        }

        fd_lookup[fd] = jsi_wsnum_pollfds;
        jsi_wspollfds[jsi_wsnum_pollfds].fd = fd;
        jsi_wspollfds[jsi_wsnum_pollfds].events = (int)(long)len;
        jsi_wspollfds[jsi_wsnum_pollfds++].revents = 0;
        break;

    case LWS_CALLBACK_DEL_POLL_FD:
        if (!--jsi_wsnum_pollfds)
            break;
        m = fd_lookup[fd];
        /* have the last guy take up the vacant slot */
        jsi_wspollfds[m] = jsi_wspollfds[jsi_wsnum_pollfds];
        fd_lookup[jsi_wspollfds[jsi_wsnum_pollfds].fd] = m;
        break;

#endif

    default:
        deflt = 1;
        break;

    }

    if (deflt && cmdPtr->debug>16 && cmdPtr->debug<128) {
        fprintf(stderr, "HTTP CALLBACK: len=%d, %p %d:%s\n", (int)len, user, reason, jsw_getReasonStr(reason));
    }

    switch (reason) {
    case LWS_CALLBACK_WSI_DESTROY:
        break;

#if (LWS_LIBRARY_VERSION_MAJOR>1)
    // Handle GET file download in client mode.
    case LWS_CALLBACK_RECEIVE_CLIENT_HTTP: {
        char buffer[1024 + LWS_PRE];
        char *px = buffer + LWS_PRE;
        int lenx = sizeof(buffer) - LWS_PRE;

        if (lws_http_client_read(wsi, &px, &lenx) < 0)
            return -1;
        break;
    }
    case LWS_CALLBACK_RECEIVE_CLIENT_HTTP_READ:
        if (jsi_wsrecv_callback(interp, cmdPtr, pss, inPtr, len, 0) != JSI_OK)
            rc = 1;
        break;

    case LWS_CALLBACK_COMPLETED_CLIENT_HTTP:
        if (jsi_wsrecv_callback(interp, cmdPtr, pss, inPtr, len, 1) != JSI_OK)
            rc = 1;
        break;

    case LWS_CALLBACK_CLIENT_APPEND_HANDSHAKE_HEADER:
        if (cmdPtr->post) {
            unsigned char **p = (unsigned char **)in, *end = (*p) + len;
            int n = 0;
            char buf[JSI_MAX_NUMBER_STRING];
            Jsi_ValueString(interp, cmdPtr->post, &n);
            snprintf(buf, sizeof(buf), "%d", n);

            if (lws_add_http_header_by_token(wsi,
                    WSI_TOKEN_HTTP_CONTENT_LENGTH,
                    (unsigned char *)buf, 2, p, end))
                return -1;
            if (lws_add_http_header_by_token(wsi,
                    WSI_TOKEN_HTTP_CONTENT_TYPE,
                    (unsigned char *)"application/x-www-form-urlencoded", 33, p, end))
                return -1;

            /* inform lws we have http body to send */
            lws_client_http_body_pending(wsi, 1);
            lws_callback_on_writable(wsi);
        }
        break;

    case LWS_CALLBACK_CLIENT_HTTP_WRITEABLE: {
        int n = 0;
        char *cps = Jsi_ValueString(interp, cmdPtr->post, &n);
        char *buf = (char*)Jsi_Calloc(1, LWS_PRE + n + 1);
        Jsi_Strcpy(buf + LWS_PRE, cps);
        n = lws_write(wsi, (unsigned char *)&buf[LWS_PRE], strlen(&buf[LWS_PRE]), LWS_WRITE_HTTP);
        Jsi_Free(buf);
        if (n < 0)
            return -1;
        /* we only had one thing to send, so inform lws we are done
         * if we had more to send, call lws_callback_on_writable(wsi);
         * and just return 0 from callback.  On having sent the last
         * part, call the below api instead.*/
        lws_client_http_body_pending(wsi, 0);
        break;
    }
#endif

    case LWS_CALLBACK_FILTER_NETWORK_CONNECTION:
        break;
    case LWS_CALLBACK_PROTOCOL_INIT:
        break;
    case LWS_CALLBACK_CLOSED_HTTP:
        if (cmdPtr->debug>2)
            fprintf(stderr, "CLOSED\n");
        if (!pss)
            pss = jsi_wsgetPss(cmdPtr, wsi, user, 0, 1);
        if (pss)
            jsi_wsdeletePss(pss);
        break;
    case LWS_CALLBACK_WSI_CREATE:
        break;

    case LWS_CALLBACK_CONFIRM_EXTENSION_OKAY:
        break;

    case LWS_CALLBACK_FILTER_HTTP_CONNECTION:
        if (cmdPtr->debug>1)
            fprintf(stderr, "FILTER CONNECTION: %s\n", inPtr);
        pss = jsi_wsgetPss(cmdPtr, wsi, user, 1, 1);
        Jsi_DSSet(&pss->url, inPtr);
        jsi_wsgetUriArgValue(interp, wsi, &pss->query, &pss->queryObj);

        if (cmdPtr->instCtx == context && (cmdPtr->clientName[0] || cmdPtr->clientIP[0])) {
            pss->clientName = cmdPtr->clientName;
            pss->clientIP = cmdPtr->clientIP;
        }

        Jsi_DSSetLength(&pss->dHdrs, 0);
        pss->hdrNum = jsi_wsGetHeaders(pss, wsi, &pss->dHdrs, pss->hdrSz, sizeof(pss->hdrSz)/sizeof(int));

        if (cmdPtr->onFilter && !cmdPtr->deleted) {
            // 4 args: ws, id, url, bool
            int killcon = 0, n = 0;
            Jsi_Obj *oarg1;
            Jsi_Value *vpargs, *vargs[10], *ret = Jsi_ValueNew1(interp);
            vargs[n++] = Jsi_ValueNewObj(interp, cmdPtr->fobj);
            vargs[n++] = Jsi_ValueNewNumber(interp, (Jsi_Number)(pss->wid));
            vargs[n++] = Jsi_ValueNewBlob(interp, (uchar*)in, len);
            vargs[n++] = Jsi_ValueNewBoolean(interp, 1);
            vpargs = Jsi_ValueMakeObject(interp, NULL, oarg1 = Jsi_ObjNewArray(interp, vargs, n, 0));
            Jsi_IncrRefCount(interp, vpargs);
            Jsi_ValueMakeUndef(interp, &ret);
            rc = Jsi_FunctionInvoke(interp, cmdPtr->onFilter, vpargs, &ret, NULL);
            if (rc == JSI_OK && Jsi_ValueIsFalse(interp, ret)) {
                if (cmdPtr->debug>1)
                    fprintf(stderr, "WS:KILLING CONNECTION: %p\n", pss);
                killcon = 1;
            }

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

    case LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED:
        client_name[0] = 0;
        client_ip[0] = 0;
        lws_get_peer_addresses(wsi, lws_get_socket_fd(wsi), client_name,
                                         sizeof(client_name), client_ip, sizeof(client_ip));
        if (client_name[0])
            cmdPtr->clientName = Jsi_KeyAdd(interp, client_name);
        if (client_ip[0])
            cmdPtr->clientIP = Jsi_KeyAdd(interp, client_ip);

        if (cmdPtr->clientName || cmdPtr->clientIP) {
            const char *loname = cmdPtr->localhostName;
            if (!loname) loname = "localhost";
            cmdPtr->instCtx = context;
            if (cmdPtr->debug>1)
                fprintf(stderr,  "Received network connect from %s (%s)\n",
                     cmdPtr->clientName, cmdPtr->clientIP);
#ifndef __WIN32
            if (cmdPtr->local && (cmdPtr->clientName && Jsi_Strcmp(cmdPtr->clientName, loname))) {
                if (cmdPtr->debug>1)
                    fprintf(stderr,  "Dropping non-localhost connection\n");
                return 1;
            }
#endif
        }

        if (cmdPtr->maxConnects && cmdPtr->stats.connectCnt>=cmdPtr->maxConnects) {
            if (cmdPtr->debug>1)
                fprintf(stderr, "maxConnects exceeded: rejecting connection <%p>\n", user);
            rc = -1;
        }
        /* if we returned non-zero from here, we kill the connection */
        break;

    case LWS_CALLBACK_HTTP:
    {
        Jsi_DString dStr;
        Jsi_DSInit(&dStr);
        if (cmdPtr->debug)
            fprintf(stderr, "HTTP GET: %s\n", inPtr);
        rc = jsi_wsHttp(interp, cmdPtr, wsi, user, context, inPtr, &dStr, pss);
        Jsi_DSFree(&dStr);
        if (rc<0)
            return -1;
        if (rc==1) {
            goto try_to_reuse;
        }
        break;
    }

#if (LWS_LIBRARY_VERSION_MAJOR>1)
    case LWS_CALLBACK_HTTP_BODY: {
        if (!pss)
            pss = jsi_wsgetPss(cmdPtr, wsi, user, 0, 1);
        if (!pss) break;
        callPtr = (pss->onUpload?pss->onUpload:cmdPtr->onUpload);
        if (cmdPtr->maxUpload<=0 || !callPtr) {
            if (cmdPtr->noWarn==0)
                fprintf(stderr, "Upload disabled: maxUpload=%d, onUpload=%p\n", cmdPtr->maxUpload, callPtr);
            return -1;
        }

        if (!pss->spa) {
            /* create the POST argument parser */
            if (!pss->paramv) {
                if (cmdPtr->formParams && cmdPtr->formParams != jsi_wsparam_str)
                    Jsi_SplitStr(cmdPtr->formParams, &pss->paramc, &pss->paramv, ",", &pss->paramDS);
                else {
                    pss->paramv = (typeof(pss->paramv))jsi_wsparam_names;
                    pss->paramc = ARRAY_SIZE(jsi_wsparam_names);
                }
            }
            pss->spa = lws_spa_create(wsi, (const char*const*)pss->paramv,
                pss->paramc, 4096, jsi_wsFileUploadCB, pss);
            if (!pss->spa)
                    return -1;

            pss->filename[0] = '\0';
            pss->file_length = 0;
        }

        cmdPtr->stats.uploadLast = pss->stats.uploadLast = time(NULL);

        /* let it parse the POST data */
        if (lws_spa_process(pss->spa, inPtr, len))
                return -1;


        if (!pss->stats.uploadStart) {
            cmdPtr->stats.uploadEnd = pss->stats.uploadEnd = 0;
            cmdPtr->stats.uploadStart = pss->stats.uploadStart = time(NULL);
            cmdPtr->stats.uploadCnt++;
            pss->stats.uploadCnt++;
        }
        break;
    }

    case LWS_CALLBACK_HTTP_BODY_COMPLETION:
        if (!pss) {
            pss = jsi_wsgetPss(cmdPtr, wsi, user, 0, 1);
            callPtr = (pss&&pss->onUpload?pss->onUpload:cmdPtr->onUpload);
        }
        if (pss && pss->spa)
            lws_spa_finalize(pss->spa);
        res = Jsi_DSValue(&pss->resultStr);
        if (!res[0]) {
            if (!pss->resultCode)
                res = "<html><body>Upload complete</body></html>";
            else
                res = "<html><body>Upload error</body></html>";
        }
        jsi_wsServeString(pss, wsi, res, pss->resultCode==JSI_OK?0:500, NULL, NULL);
        if (cmdPtr->maxUpload<=0 || !callPtr) {
            if (cmdPtr->noWarn==0)
                fprintf(stderr, "Upload disabled: maxUpload=%d, onUpload=%p\n", cmdPtr->maxUpload, callPtr);
            return -1;
        }
        cmdPtr->stats.uploadEnd = pss->stats.uploadEnd = time(NULL);
        lws_return_http_status(wsi, HTTP_STATUS_OK, NULL);
        goto try_to_reuse;

    case LWS_CALLBACK_HTTP_DROP_PROTOCOL:
        pss = jsi_wsgetPss(cmdPtr, wsi, user, 0, 1);
        if (pss && pss->spa) {
            lws_spa_destroy(pss->spa);
            pss->spa = NULL;
        }
        break;
#endif

    case LWS_CALLBACK_HTTP_FILE_COMPLETION:
        goto try_to_reuse;

    case LWS_CALLBACK_HTTP_WRITEABLE: {
        lwsl_info("LWS_CALLBACK_HTTP_WRITEABLE\n");
        if (!pss)
            pss = jsi_wsgetPss(cmdPtr, wsi, user, 0, 1);

        if (!pss || !pss->fop_fd)
            goto try_to_reuse;

        /*
         * we can send more of whatever it is we were sending
         */
        int sent = 0;
        unsigned char buffer[JSI_BUFSIZ*10 + LWS_PRE];
        do {
            int n = sizeof(buffer) - LWS_PRE;
            int m = lws_get_peer_write_allowance(wsi);
            if (m == 0)
                goto later;

            if (m != -1 && m < n)
                n = m;

            lws_filepos_t amount = 0;
            n = lws_vfs_file_read(pss->fop_fd, &amount, buffer + LWS_PRE, n);
            if (n < 0) {
                lwsl_err("problem reading file\n");
                goto bail;
            }
            n = (int)amount;
            if (n == 0)
                goto penultimate;
            /*
             * To support HTTP2, must take care about preamble space
             *
             * identification of when we send the last payload frame
             * is handled by the library itself if you sent a
             * content-length header
             */
            m = jsi_wswrite(pss, wsi, buffer + LWS_PRE, n, LWS_WRITE_HTTP);
            if (m < 0) {
                lwsl_err("write failed\n");
                /* write failed, close conn */
                goto bail;
            }
            if (m) /* while still active, extend timeout */
                lws_set_timeout(wsi, PENDING_TIMEOUT_HTTP_CONTENT, 5);
            sent += m;

        } while (!lws_send_pipe_choked(wsi) && (sent < 500 * 1024 * 1024));
later:
        lws_callback_on_writable(wsi);
        break;
penultimate:
        lws_vfs_file_close(&pss->fop_fd);
        goto try_to_reuse;

bail:
        lws_vfs_file_close(&pss->fop_fd);
        rc = -1;
        goto doret;
    }

    default:
        break;
    }

    goto doret;

try_to_reuse:
    if (lws_http_transaction_completed(wsi))
         rc = -1;
    else
        rc = 0;
    goto doret;

doret:
    if (cmdPtr->debug>2)
        fprintf(stderr, "<---HTTP RET = %d\n", rc);
    return rc;
}

Project: jsish

RebuildTable(Jsi_Hash *tablePtr)
{
    Jsi_HashEntry **bucketPtr, **oldBuckets;
    Jsi_HashEntry **oldChainPtr, **endPtr;
    Jsi_HashEntry *hPtr, *nextPtr;
    size_t hindex;

    oldBuckets = tablePtr->buckets;
    endPtr = tablePtr->buckets + tablePtr->numBuckets;
    tablePtr->numBuckets <<= 2;
    tablePtr->buckets = (Jsi_HashEntry**)Jsi_Calloc(tablePtr->numBuckets, 
                   sizeof(Jsi_HashEntry *));
    tablePtr->rebuildSize <<= 2;
    tablePtr->downShift -= 2;
    tablePtr->mask = tablePtr->numBuckets - 1;

 
    if (tablePtr->keyType == JSI_KEYS_ONEWORD) {

        for (oldChainPtr = oldBuckets; oldChainPtr < endPtr; oldChainPtr++) {
            for (hPtr = *oldChainPtr; hPtr != NULL; hPtr = nextPtr) {
            nextPtr = hPtr->nextPtr;
            hindex = RANDOM_INDEX(tablePtr, hPtr->key.oneWordValue);
            bucketPtr = tablePtr->buckets + hindex;
            hPtr->nextPtr = *bucketPtr;
            *bucketPtr = hPtr;
            }
        }
    } else {
        for (oldChainPtr = oldBuckets; oldChainPtr < endPtr; oldChainPtr++) {
            for (hPtr = *oldChainPtr; hPtr != NULL; hPtr = nextPtr) {
            nextPtr = hPtr->nextPtr;
            hindex = hPtr->hval & tablePtr->mask;
            bucketPtr = tablePtr->buckets + hindex;
            hPtr->nextPtr = *bucketPtr;
            *bucketPtr = hPtr;
            }
        }
    }

    if (oldBuckets != tablePtr->staticBuckets) {
        Jsi_Free(oldBuckets);
    }
}

Project: jsish

bool Jsi_NumberIsFinite(Jsi_Number value)
{
    Jsi_Number r = INFINITY;
    return (Jsi_NumberIsNaN(value)==0 && value != r && r != -value);
}

Project: jsish

Jsi_RC Jsi_GetLongFromValue(Jsi_Interp* interp, Jsi_Value *value, long *n)
{
    /* TODO: inefficient to convert to double then back. */
    if (!value)
        return JSI_ERROR;
    if (!interp->strict)
        jsi_ValueToOInt32(interp, value);
    if (!Jsi_ValueIsNumber(interp, value))
    
        return Jsi_LogError("invalid number");
    *n = (long)(value->vt == JSI_VT_NUMBER ? value->d.num : value->d.obj->d.num);
    return JSI_OK;
}

Project: jsish

const char* Jsi_ValueToString(Jsi_Interp *interp, Jsi_Value *v, int *lenPtr)
{
    Jsi_Number d;
    const char *ntxt = "undefined";
    int kflag = 1;
    int isKey = 0;
    char *key = NULL;
    if (!v)
        goto done;
    if (lenPtr) *lenPtr = 0;
    char unibuf[JSI_MAX_NUMBER_STRING*2];
    switch(v->vt) {
        case JSI_VT_STRING:
            ntxt = v->d.s.str;
            goto done;
        case JSI_VT_UNDEF:
            break;
        case JSI_VT_BOOL:
            ntxt = v->d.val ? "true":"false";
            break;
        case JSI_VT_NULL:
            ntxt = "null";
            break;
        case JSI_VT_NUMBER: {
            d = v->d.num;
fmtnum:
            if (Jsi_NumberIsInteger(d)) {
                Jsi_NumberItoA10((Jsi_Wide)d, unibuf, sizeof(unibuf));
                kflag = 0;
                ntxt = unibuf;
            } else if (Jsi_NumberIsNormal(d)) {
                Jsi_NumberDtoA(interp, d, unibuf, sizeof(unibuf), 0);
                kflag = 0;
                ntxt = unibuf;
            } else if (Jsi_NumberIsNaN(v->d.num)) {
                ntxt = "NaN";
            } else {
                int s = Jsi_NumberIsInfinity(d);
                if (s > 0) ntxt = "Infinity";
                else if (s < 0) ntxt = "-Infinity";
                else Jsi_LogBug("Ieee function got problem");
            }
            break;
        }
        case JSI_VT_OBJECT: {
            Jsi_Obj *obj = v->d.obj;
            switch(obj->ot) {
                case JSI_OT_STRING:
                    ntxt = obj->d.s.str;
                    goto done;
                case JSI_OT_BOOL:
                    ntxt = obj->d.val ? "true":"false";
                    break;
                case JSI_OT_NUMBER:
                    d = obj->d.num;
                    goto fmtnum;
                    break;
                default:
                    ntxt = "[object Object]";
                    break;
            }
            break;
        }
        default:
            Jsi_LogBug("Convert a unknown type: 0x%x to string", v->vt);
            break;
    }
    Jsi_ValueReset(interp, &v);
    if (!kflag) {
        Jsi_ValueMakeStringDup(interp, &v, ntxt);
        return Jsi_ValueString(interp, v, lenPtr);
    }
    
    key = jsi_KeyFind(interp, ntxt, 0, &isKey);
    if (key)
        Jsi_ValueMakeStringKey(interp, &v, key);
    else
        Jsi_ValueMakeString(interp, &v, ntxt);
    ntxt = v->d.s.str;
    
done:
    if (lenPtr) *lenPtr = Jsi_Strlen(ntxt);
    return ntxt;
}

Project: jsish

static Jsi_RC StringSearchCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{

    int sLen, bLen;
    const char *source_str;
    ChkString(_this, funcPtr, source_str, &sLen, &bLen);
    
    char *v = _this->d.obj->d.s.str;
    Jsi_Value *seq = Jsi_ValueArrayIndex(interp, args, skip);

    if (Jsi_ValueIsString(interp, seq)) {
        char *ce, *cp = Jsi_ValueString(interp, seq, NULL);
        int n = -1;
        if ((ce = Jsi_Strstr(source_str, cp))) {
            n = (ce-source_str);
        }
        Jsi_ValueMakeNumber(interp, ret, n);
        return JSI_OK;
    }
    if (!seq || seq->vt != JSI_VT_OBJECT || seq->d.obj->ot != JSI_OT_REGEXP) {
        Jsi_ValueMakeNumber(interp, ret, -1);
        return JSI_OK;
    }

    regex_t *reg = &seq->d.obj->d.robj->reg;
    
    regmatch_t pos[MAX_SUBREGEX] = {};
    int r;
    if ((r = regexec(reg, v, MAX_SUBREGEX, pos, 0)) != 0) {
        if (r == REG_NOMATCH) {
            Jsi_ValueMakeNumber(interp, ret, -1.0);
            return JSI_OK;
        }
        if (r >= REG_BADPAT) {
            char buf[JSI_MAX_NUMBER_STRING];

            regerror(r, reg, buf, sizeof(buf));
            Jsi_LogError("error while matching pattern: %s", buf);
            return JSI_ERROR;
        }

    }
    Jsi_ValueMakeNumber(interp, ret, (Jsi_Number)pos[0].rm_so);
    return JSI_OK;
}

Project: jsish

Jsi_RC jsi_ArgTypeCheck(Jsi_Interp *interp, int typ,  Jsi_Value *arg, const char *p1,
    const char *p2, int index, Jsi_Func *func, bool isdefault) {
    Jsi_RC rc = JSI_OK;
    char idxBuf[JSI_MAX_NUMBER_STRING*2];
    idxBuf[0] = 0;
    if (func && arg->vt == JSI_VT_UNDEF && !interp->typeCheck.noundef && index>0 && !isdefault && !(typ&JSI_TT_UNDEFINED)) {
        snprintf(idxBuf, sizeof(idxBuf), " arg %d", index);
        jsi_TypeMismatch(interp);
       
        Jsi_DString fStr = {};
        rc = Jsi_LogType("call with undefined var %s%s '%s'%s", p1, idxBuf, p2, jsiFuncInfo(interp, &fStr, func, arg));
        Jsi_DSFree(&fStr);
        return rc;
    }
    if (typ <= 0)
        return JSI_OK;
    //if (typ&JSI_TT_VOID)
    //    return JSI_OK;
    if (interp->typeCheck.all==0) {
        if (func ? (interp->typeCheck.run==0) : (interp->typeCheck.parse==0))
            return JSI_OK;
    }
    if (index == 0 && func && func->type == FC_BUILDIN && 
        interp->typeCheck.all == 0) // Normally do not check return types for builtins.
        return JSI_OK; 
    if ((typ&JSI_TT_ANY)) return JSI_OK;
    if (index == 0 && arg->vt == JSI_VT_UNDEF) {
        if (!(typ&JSI_TT_VOID)) 
            goto done;
        return JSI_OK;
    }
    if (isdefault && index && typ&JSI_TT_VOID && arg->vt == JSI_VT_UNDEF)
        return JSI_OK;
    if (typ&JSI_TT_UNDEFINED && Jsi_ValueIsUndef(interp, arg)) return rc;
    if (typ&JSI_TT_NUMBER && Jsi_ValueIsNumber(interp, arg)) return rc;
    if (typ&JSI_TT_STRING && Jsi_ValueIsString(interp, arg)) return rc;
    if (typ&JSI_TT_BOOLEAN && Jsi_ValueIsBoolean(interp, arg))  return rc;
    if (typ&JSI_TT_ARRAY && Jsi_ValueIsArray(interp, arg))   return rc;
    if (typ&JSI_TT_FUNCTION && Jsi_ValueIsFunction(interp, arg)) return rc;
    if (typ&JSI_TT_REGEXP && Jsi_ValueIsObjType(interp, arg, JSI_OT_REGEXP)) return rc;
    if (typ&JSI_TT_USEROBJ && Jsi_ValueIsObjType(interp, arg, JSI_OT_USEROBJ)) return rc;
    if (typ&JSI_TT_ITEROBJ && Jsi_ValueIsObjType(interp, arg, JSI_OT_ITER)) return rc;
    if (typ&JSI_TT_OBJECT && (
        Jsi_ValueIsObjType(interp, arg, JSI_OT_OBJECT) && Jsi_ValueIsArray(interp, arg)==0))
        return rc;
    if (typ&JSI_TT_NULL && Jsi_ValueIsNull(interp, arg)) return rc;
done:
    {
        Jsi_DString dStr = {};
        const char *exp = jsi_typeName(interp, typ, &dStr);
        const char *vtyp = jsi_ValueTypeName(interp, arg);
        if (index>0)
            snprintf(idxBuf, sizeof(idxBuf), " arg %d", index);
        if (interp->typeCheck.error)
            rc = JSI_ERROR;
        jsi_TypeMismatch(interp);
        Jsi_DString fStr = {};
        rc = Jsi_LogType("type mismatch %s%s '%s': expected \"%s\" but got \"%s\"%s",
            p1, idxBuf, p2, exp, vtyp, jsiFuncInfo(interp, &fStr, func, arg));
        Jsi_DSFree(&fStr);
        Jsi_DSFree(&dStr);
    }
    return rc;
}

Project: jsish

static int jsi_wswrite(jsi_wsPss* pss, struct lws *wsi, unsigned char *buf, int len, enum lws_write_protocol proto)
{
    jsi_wsCmdObj *cmdPtr = pss->cmdPtr;
    int m = lws_write(wsi, buf, len, proto);
    if (m >= 0) {
        cmdPtr->stats.sentCnt++;
        cmdPtr->stats.sentLast = time(NULL);
        pss->stats.sentCnt++;
        pss->stats.sentLast = time(NULL);
    } else {
        pss->state = PWS_SENDERR;
        pss->stats.sentErrCnt++;
        pss->stats.sentErrLast = time(NULL);
        cmdPtr->stats.sentErrCnt++;
        cmdPtr->stats.sentErrLast = time(NULL);
    }
    return m;
}

Project: jsish

static Jsi_RC jsi_ValueAssignCheck(Jsi_Interp *interp, Jsi_Value *val, int lop) {
    if (lop == OP_FCALL || lop == OP_NEWFCALL)
        return JSI_OK;
    if (val->f.bits.lookupfailed && val->d.lookupFail)
        return Jsi_LogType("assign from undef var: %s", val->d.lookupFail);
    return JSI_OK;
}

Project: jsish

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

Project: jsish

static Jsi_RC mdbPrepareStmt(MyDbEvalContext *p)
{
    MySqlObj *jdb = p->jdb;
    //int namedParams = !jdb->optPtr->noNamedParams;
    const char *zSql = p->zSql;         /* Pointer to first SQL statement in zIn */
    MYSQL_STMT *myStmt;            /* Prepared statement object */
    MysqlPrep *prep = 0;  /* Pointer to cached statement */
    Jsi_RC rc = JSI_OK;
    Jsi_Interp *interp = jdb->interp;


    Jsi_HashEntry *entry = Jsi_HashEntryFind(jdb->stmtHash, zSql);
    if (entry && ((prep = (MysqlPrep*)Jsi_HashValueGet(entry)))) {
        
        if (jdb->debug & mdbTMODE_PREPARE)
            JSI_DBQUERY_PRINTF( "DEBUG: prepare cache-hit: %s\n", zSql);
        myStmt = prep->myStmt;

        /* When a prepared statement is found, unlink it from the
        ** cache list.  It will later be added back to the beginning
        ** of the cache list in order to implement LRU replacement.
        */
        Jsi_ListPop(jdb->stmtCache, prep->elPtr);
        jdb->numStmts = Jsi_ListSize(jdb->stmtCache);
        
        // Sanity check for schema check: right now we just use number of columns
        if (prep->numCol != (int)mysql_stmt_field_count(prep->myStmt)) {
            mdbRelease1Stmt(jdb, prep);
            prep = NULL;
        }
    }
    
    /* If no prepared statement was found. Compile the SQL text. Also allocate
    ** a new MysqlPrep structure.  */
    if (!prep) {
        myStmt = mysql_stmt_init(jdb->db);
        if (!myStmt) 
            return Jsi_LogError("can't get statement: %s", mysql_error(jdb->db));
        char **paramNames;
        int paramCnt;
        int namedParams = 0;
        Jsi_DString *naStr = NULL;
        if (p->namedParams)
        {
            Jsi_RC rc = JSI_OK;
            Jsi_DString nsStr, nnStr;
            Jsi_DSInit(&nsStr);
            Jsi_DSInit(&nnStr);
            if (MySqlExtractParmNames(jdb, zSql, &nsStr, &nnStr) != JSI_OK)
                rc = Jsi_LogError("parsing names from query: %s", zSql);
            else if (Jsi_DSLength(&nnStr)) {
                namedParams = 1;
                zSql = Jsi_DSFreeDup(&nsStr);
                naStr = (Jsi_DString*)Jsi_Calloc(1, sizeof(*naStr));
                Jsi_SplitStr(Jsi_DSValue(&nnStr), &paramCnt, &paramNames, " ", naStr);
            }
            Jsi_DSFree(&nsStr);
            Jsi_DSFree(&nnStr);
            if (rc != JSI_OK)
                return rc;
        }

        if (mysql_stmt_prepare(myStmt, zSql, Jsi_Strlen(zSql)) )
        {
            Jsi_LogError("error in sql: %s", mysql_error(jdb->db));
            mysql_stmt_close(myStmt);
            if (namedParams) {
                Jsi_DSFree(naStr);
                Jsi_Free(naStr);
                Jsi_Free((char*)zSql);
            }
            return JSI_ERROR;
        }

        if (jdb->debug & mdbTMODE_PREPARE)
            JSI_DBQUERY_PRINTF( "DEBUG: prepare new: %s\n", zSql);
        assert( prep==0 );
        prep = (MysqlPrep*)Jsi_Calloc(1, sizeof(MysqlPrep));
        jdb->numStmts++;
        prep->sig = MYSQL_SIG_STMT;
        prep->myStmt = myStmt;
        if (!namedParams)
            prep->zSql = Jsi_Strdup(zSql);
        else {
            prep->zSql = (char*)zSql;
            prep->origSql = Jsi_Strdup(p->zSql);
            prep->naStr = naStr;
            prep->paramCnt = paramCnt;
            prep->paramNames = paramNames;
        }
        prep->paramMetaData = mysql_stmt_param_metadata(myStmt);
        prep->resultMetaData = mysql_stmt_result_metadata(myStmt);
        prep->numCol = mysql_stmt_field_count(myStmt);
        if (prep->numCol>0)
            prep->bindResult = (MYSQL_BIND *)Jsi_Calloc(prep->numCol, sizeof(MYSQL_BIND));
        prep->numParam = mysql_stmt_param_count(myStmt);
        if (prep->numParam>0 && !prep->bindParam) {
            prep->bindParam = (MYSQL_BIND *)Jsi_Calloc(prep->numParam, sizeof(MYSQL_BIND));
            prep->fieldParam = (SqlFieldResults*)Jsi_Calloc(prep->numParam, sizeof(*prep->fieldParam));
        }
        bool isNew = 0;
        prep->entry = Jsi_HashEntryNew(jdb->stmtHash, p->zSql, &isNew);
        if (!isNew)
            JSI_DBQUERY_PRINTF( "mysql dup stmt entry");
        Jsi_HashValueSet(prep->entry, prep);
    }
    p->prep = prep;
    return rc;
}

Project: jsish

static void jsiVarDeref(Jsi_Interp* interp, int n) {
    while(interp->framePtr->Sp<n) // Assert and Log may map-out Ops.
        jsiPush(interp, 1);
    int i;
    for (i=1; i<=n; i++) {
        Jsi_Value *vb = _jsi_STACKIDX(interp->framePtr->Sp - i);
        if (vb->vt == JSI_VT_VARIABLE) {
            SIGASSERTV(vb->d.lval, VALUE);
            Jsi_ValueCopy(interp, vb, vb->d.lval);
        }
    }
}

Project: jsish

bool Jsi_NumberIsSafeInteger(Jsi_Number n) {
    Jsi_Number n1 = fabsl(n),
    n2 = 9007199254740991LL;
    return (Jsi_NumberIsInteger(n) && n1<=n2);
}

Project: jsish

static char *FindEndB(char *cp) {
    
    if (*cp == '\"'||*cp == '\'') {
        char endc = *cp;
        cp++;
        while (*cp && *cp != endc) {
            if (*cp == '\\' && cp[1]) cp++;
            cp++;
        }
        if (*cp == endc)
            cp++;
        if (*cp != ']')
            return NULL;
        return cp;
    } else
        return Jsi_Strchr(cp, ']');
}

Project: jsish

static Jsi_RC MySqlCompleteCmd(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_Value *s = Jsi_ValueArrayIndex(interp, args, 0);
    const char *str =  Jsi_ValueString(interp, s, NULL);
    int isComplete = 0;
    if (str)
        isComplete = 0; // sqlite3_complete( str );
    Jsi_ValueMakeBool(interp, ret, isComplete);
    return JSI_OK;
}

Project: jsish

void jsi_ValueDebugUpdate_(Jsi_Interp *interp, jsi_ValueDebug *vd, void *v, Jsi_Hash* tbl, const char *fname, int line, const char *func)
{
    vd->fname = fname;
    vd->line = line;
    vd->func = func;
    if (!vd->Idx)
        vd->Idx = interp->dbPtr->memDebugCallIdx;
    vd->hPtr = Jsi_HashSet(tbl, v, 0);
    vd->ip = interp->curIp;
    if (vd->ip) {
        vd->ipLine = vd->ip->Line;
        vd->ipOp = vd->ip->op;
        vd->ipFname = vd->ip->fname;
    }
    vd->interp = interp;
    if (jsi_memDebugBreakIdx && jsi_memDebugBreakIdx == vd->Idx)
        jsi_memDebugBreak();
}

Project: jsish

static void jsi_ParentFuncFree(Jsi_Interp *interp, Jsi_OptionSpec* spec, void *ptr)
{
    Jsi_Value **v = (Jsi_Value **)(((char*)ptr));
    if (!interp->parent) return;
    if (*v)
        Jsi_DecrRefCount(interp->parent, *v);
    *v = NULL;
}

Project: jsish

char jsi_fromHexChar(char ch) {
    return isdigit(ch) ? ch - '0' : tolower(ch) - 'a' + 10;
}

Project: jsish

static void mdbOutputHtmlString(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

bool Jsi_ValueIsType(Jsi_Interp *interp, Jsi_Value *pv, Jsi_vtype vtype) {
    if (!pv) return 0;
    return pv->vt == vtype;
}

Project: jsish

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

Project: jsish

static Jsi_Value *ValueNew(Jsi_Interp *interp)
{
    interp->dbPtr->valueCnt++;
    interp->dbPtr->valueAllocCnt++;
    Jsi_Value *v = (Jsi_Value *)Jsi_Calloc(1,sizeof(*v));
    SIGINIT(v,VALUE)
    v->vt = JSI_VT_UNDEF;
    jsi_DebugValue(v,"New", jsi_DebugValueCallIdx(), interp);
    return v;
}

Project: jsish

static Jsi_RC MySqlResetCmd(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;
#if (MYSQL_VERSION_ID >= 50703 && !defined(JSI_NO_MYSQL_RESET))
    int oldMax = jdb->maxStmts;
    jdb->maxStmts = 0;
    mdbStmtLimit(jdb);
    jdb->maxStmts = oldMax;
    Jsi_ValueMakeNumber(interp, ret, (Jsi_Number)mysql_reset_connection(jdb->db));
#else
    Jsi_LogWarn("mysql reset unavailable: requires version 5.7.3+");
#endif
    return JSI_OK;    
}

Project: jsish

void Jsi_ListDelete(Jsi_List *list) {
    Jsi_ListClear(list);
    free(list);
}

Project: jsish

jsi_SetOption(Jsi_Interp *interp, Jsi_OptionSpec *specPtr, const char *string /*UNUSED*/, void* rec, Jsi_Value *argValue, Jsi_Wide flags, bool isSafe)
{
    Jsi_Value *oa = interp->lastParseOpt;
    interp->lastParseOpt = argValue;
    Jsi_RC rc = jsi_SetOption_(interp, specPtr, string, rec, argValue, flags, isSafe);
    interp->lastParseOpt = oa;
    return rc;
}

Project: jsish

Jsi_ScopeStrs *jsi_ArgsOptAdd(jsi_Pstate *pstate, Jsi_ScopeStrs *a)
{
    jsi_PstatePush(pstate);
    return a;
}

Project: jsish

const char* Jsi_DbKeyAdd(Jsi_Db *jdb, const char *str)
{
#ifndef JSI_LITE_ONLY
    if (jdb->interp)
        return Jsi_KeyAdd(jdb->interp, str);
#endif
    Jsi_HashEntry *hPtr;
    bool isNew;
    hPtr = Jsi_HashEntryNew(jdb->strKeyTbl, str, &isNew);
    assert(hPtr) ;
    return (const char*)Jsi_HashKeyGet(hPtr);
}

Project: jsish

int jsi_ValueToOInt32(Jsi_Interp *interp, Jsi_Value *v)
{
    Jsi_Number a = Jsi_ValueToNumberInt(interp, v, 1);
    Jsi_ValueReset(interp,&v);
    Jsi_ValueMakeNumber(interp, &v, a);
    return (int)a;
}

Project: jsish

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

Project: jsish

static Jsi_OpCodes *code_push_top() { JSI_NEW_CODES(0,OP_PUSHTOP, 0); }

Project: jsish

static int jsi_FSFlushProc(Jsi_Channel chan) { return fflush(_JSI_GETFP(chan,0));}

Project: jsish

Jsi_RC jsi_RunFuncCallCheck(Jsi_Interp *interp, Jsi_Func *func, int argc, const char *name, jsi_Pline *line, Jsi_OpCodes *argCodes, bool isParse)
{
    Jsi_RC rc = JSI_OK;
    if (interp->typeCheck.all==0) {
        if (!argCodes ? (interp->typeCheck.run==0) : (interp->typeCheck.parse==0))
            return JSI_OK;
    }

    Jsi_CmdSpec *spec = func->cmdSpec;
    Jsi_ScopeStrs *ss = func->argnames;
    if (ss==NULL && spec == NULL)
        return JSI_OK;
    int i, minArgs, maxArgs, mis = 0, varargs = 0;
    char nbuf[JSI_MAX_NUMBER_STRING];
    if (func->type == FC_BUILDIN) {
        varargs =  (spec->maxArgs<0);
        maxArgs = spec->maxArgs + func->callflags.bits.addargs;
        minArgs = spec->minArgs + func->callflags.bits.addargs;
    } else {
        varargs = ss->varargs;
        minArgs = (ss->firstDef>0 ? ss->firstDef-1 : ss->count);
        maxArgs = ss->count;
        mis = (argc != ss->count);
        if (func->retType == 0 && ss && ss->typeCnt == 0 && interp->typeCheck.all==0)
            return JSI_OK;
    }
    if (varargs) {
        if (argc >= minArgs)
            return JSI_OK;
        mis = (argc<minArgs);
    } else 
        mis = (argc<minArgs || argc>maxArgs);
    if (mis) {
        if (varargs)
            snprintf(nbuf, sizeof(nbuf), "%d or more", minArgs);
        else if (maxArgs > minArgs)
            snprintf(nbuf, sizeof(nbuf), "%d-%d", minArgs, maxArgs);
        else
            snprintf(nbuf, sizeof(nbuf), "%d", maxArgs);
        if (line)
            interp->parseLine = line;
        if (interp->typeCheck.error)
            rc = JSI_ERROR;
        Jsi_DString dStr = {};
        Jsi_FuncObjToString(interp, func, &dStr, 2);
        if (isParse)
            Jsi_LogWarn("got %d args, expected %s, calling %s", argc, nbuf, Jsi_DSValue(&dStr));
        else
            rc = Jsi_LogType("got %d args, expected %s, calling %s", argc, nbuf, Jsi_DSValue(&dStr));
        jsi_TypeMismatch(interp);
        Jsi_DSFree(&dStr);
        if (line)
            interp->parseLine = NULL;
        return rc;
    }
    if (argCodes && argCodes->code_len>=argc) {
        int cl = argCodes->code_len;
        int aind=argc-1;
        for (i=cl-1; rc == JSI_OK && i>=0 && aind>=0; i--,aind--) {
            Jsi_ttype atyp = JSI_TT_ANY;
            switch (argCodes->codes[i].op) {
                case OP_PUSHSTR: atyp=JSI_TT_STRING; break;
                case OP_PUSHNUM: atyp=JSI_TT_NUMBER; break;
                case OP_PUSHBOO: atyp=JSI_TT_BOOLEAN; break;
                case OP_PUSHFUN: atyp=JSI_TT_FUNCTION; break;
                case OP_PUSHTHS: atyp=JSI_TT_OBJECT; break;
                case OP_PUSHREG: atyp=JSI_TT_REGEXP; break;
                case OP_PUSHUND: atyp=JSI_TT_VOID; break;
                case OP_PUSHNULL: atyp=JSI_TT_NULL; break;
                case OP_PUSHARG: atyp=JSI_TT_ARRAY; break;
                case OP_SUBSCRIPT: i++; break;
                case OP_ARRAY: atyp=JSI_TT_ARRAY; i=jsiPopArgs(argCodes, i); break;
                case OP_OBJECT: atyp=JSI_TT_OBJECT; i=jsiPopArgs(argCodes, i); break;
                default: break;
            }
            if (atyp == JSI_TT_ANY) continue;
            rc = jsi_StaticArgTypeCheck(interp, atyp, "for argument", name, aind+1, func, line);  
        }
    }
    return rc;
}

Project: jsish

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

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


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

Project: jsish

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

Project: jsish

static void jsi_SuboptFree(Jsi_Interp *interp, Jsi_OptionSpec* spec, void *ptr)
{
    Jsi_OptionSpec *subspec = (Jsi_OptionSpec *)spec->data;
    Jsi_OptionsFree(interp, subspec, ptr, 0);
}

Project: jsish

static Jsi_RC MutexLock(Jsi_Interp *interp, Jsi_Mutex *mtx) {
    int timeout = mtx->lockTimeout;
    if (interp && timeout<0)
        timeout = interp->lockTimeout;
    if (timeout<=0)
        pthread_mutex_lock(&mtx->mtx);
    else {
        struct timespec ts;
        ts.tv_sec = timeout/1000;
        ts.tv_nsec = 1000 * (timeout%1000);
        int rc = pthread_mutex_timedlock(&mtx->mtx, &ts);
        if (rc != 0) {
            Jsi_LogError("lock timed out");
            if (interp)
                interp->threadErrCnt++;
            mtx->threadErrCnt++;
            return JSI_ERROR;
        }
    }
    return JSI_OK;
}

Project: jsish

Jsi_Wide Jsi_Seek(Jsi_Interp *interp, Jsi_Channel chan, Jsi_Wide offset, int mode) {
    if (chan->fsPtr==0 || !chan->fsPtr->seekProc) return -1;
    return chan->fsPtr->seekProc(chan, offset, mode);
}

Project: jsish

static Jsi_RC inline jsi_ValueAssign(Jsi_Interp *interp, Jsi_Value *dst, Jsi_Value* src, int lop)
{
    Jsi_Value *v;
    if (dst->vt != JSI_VT_VARIABLE) {
        if (interp->strict) 
            return Jsi_LogError("operand not a left value");
    } else {
        v = dst->d.lval;
        SIGASSERT(v, VALUE);
        int strict = interp->strict;
        if (strict && lop == OP_PUSHFUN && interp->curIp[-1].local)
            dst->f.bits.local = 1;
        if (strict && dst->f.bits.local==0) {
            const char *varname = "";
            if (v->f.bits.lookupfailed)
                varname = v->d.lookupFail;
            Jsi_RC rc = Jsi_LogType("function created global: \"%s\"", varname);
            dst->f.bits.local=1;
            if (rc != JSI_OK)
                return rc;
        }
        if (v == src)
            return JSI_OK;
        if (v->f.bits.readonly) {
            if (interp->strict) 
                return Jsi_LogError("assign to readonly variable");
            return JSI_OK;
        }
        if (Jsi_ValueIsFunction(interp, src))
            Jsi_ValueMove(interp,v, src);
        else
            Jsi_ValueCopy(interp,v, src);
        SIGASSERT(v, VALUE);
        v->f.bits.readonly = 0;
#ifdef JSI_MEM_DEBUG
    if (!v->VD.label2)
        v->VD.label2 = "ValueAssign";
#endif
    }
    return JSI_OK;
}

Project: jsish

Jsi_Value *jsi_MakeFuncValue(Jsi_Interp *interp, Jsi_CmdProc *callback, const char *name, Jsi_Value** toVal, Jsi_CmdSpec *cspec)
{
    Jsi_Obj *o = Jsi_ObjNew(interp);
    Jsi_Func *f = jsi_FuncNew(interp);
    Jsi_ObjIncrRefCount(interp, o);
    o->ot = JSI_OT_FUNCTION;
    f->type = FC_BUILDIN;
    f->callback = callback;
    f->privData = NULL;
    o->d.fobj = jsi_FuncObjNew(interp, f);
    f->cmdSpec = cspec;
    if (!cspec) {
        f->cmdSpec = (Jsi_CmdSpec*)Jsi_Calloc(2,sizeof(Jsi_CmdSpec));
        f->cmdSpec->reserved[3] = (void*)0x1;
        f->cmdSpec->maxArgs = -1;
        if (name)
            f->cmdSpec->name = (char*)Jsi_KeyAdd(interp, name);
    }
    f->script = interp->curFile;
    f->callback = callback;
    return Jsi_ValueMakeObject(interp, toVal, o);
}

Project: jsish

jsi_HashArrayCreate( Jsi_Hash *tablePtr,  const void *key, bool *newPtr)
{
    jsi_Hash hval = jsi_HashArray(key, tablePtr->keyType);
    size_t size, hindex = hval & tablePtr->mask;
    Jsi_HashEntry *hPtr = tablePtr->buckets[hindex];

    for (; hPtr != NULL; hPtr = hPtr->nextPtr)
        if (hPtr->hval == hval && !memcmp(hPtr->key.string, key, tablePtr->keyType)) {
            if (newPtr)
                *newPtr = 0;
            return hPtr;
        }

    /* Entry not found.  Add a new one to the bucket. */
    if (newPtr)
        *newPtr = 1;
    assert(tablePtr->keyType >= JSI_KEYS_STRUCT_MINSIZE);
    size = sizeof(Jsi_HashEntry) + tablePtr->keyType;
    if ((uint)tablePtr->keyType > sizeof(jsi_HashKey)) // Avoid memory checker problems by not truncating struct.
        size -= sizeof(jsi_HashKey);
    hPtr = (Jsi_HashEntry*)Jsi_Calloc(1, size);
    Jsi_HashEntry **bucketPtr = tablePtr->buckets + hindex;
    hPtr->typ = JSI_MAP_HASH;
    hPtr->tablePtr = tablePtr;
    hPtr->nextPtr = *bucketPtr;
    hPtr->hval = hval;
    hPtr->clientData = 0;
    memcpy(hPtr->key.string, key, tablePtr->keyType);
    *bucketPtr = hPtr;
    tablePtr->numEntries++;

    /*
     * If the table has exceeded a decent size, rebuild it with many
     * more buckets.
     */
    if (tablePtr->numEntries >= tablePtr->rebuildSize)
        RebuildTable(tablePtr);

    return hPtr;
}

Project: jsish

void jsi_TraceFuncCall(Jsi_Interp *interp, Jsi_Func *fstatic, jsi_OpCode *iPtr,
    Jsi_Value *_this, Jsi_Value* args, Jsi_Value *ret, int tc)
{
    jsi_OpCode *ip = (iPtr ? iPtr : interp->curIp);
    if (!ip)
        return;
    const char *ff, *fname = ip->fname?ip->fname:"";
    if ((tc&jsi_callTraceFullPath)==0 && ((ff=Jsi_Strrchr(fname,'/'))))
        fname = ff+1;
    if (interp->traceHook)
        (*interp->traceHook)(interp, fstatic->name, ip->fname, ip->Line, fstatic->cmdSpec, _this, args, ret);
    else {
        const char *fp = ((tc&jsi_callTraceNoParent)?NULL:fstatic->parentName);
        if (fp && !*fp)
            fp = NULL;
        Jsi_DString aStr;
        Jsi_DSInit(&aStr);
        Jsi_DString dStr;
        Jsi_DSInit(&dStr);
        Jsi_DString pStr;
        Jsi_DSInit(&pStr);
        Jsi_DString *sPtr = NULL;
        int plen = 0;
        if (ret) {
            sPtr = &dStr;
            Jsi_DSAppend(sPtr, " <-- ", NULL);
            plen = Jsi_DSLength(sPtr);
            Jsi_ValueGetDString(interp, ret, sPtr, 0);
        } else if ((tc&jsi_callTraceArgs)) {
            sPtr = &aStr;
            Jsi_ValueGetDString(interp, args, sPtr, JSI_OUTPUT_JSON);
        }
        if (sPtr) {
            if (!(tc&jsi_callTraceNoTrunc)) {
                const char *cp0 = Jsi_DSValue(sPtr), *cp1 = Jsi_Strchr(cp0, '\n');
                int nlen = 0, clen = Jsi_DSLength(sPtr);
                if (cp1) {
                    nlen = (cp1-cp0);
                    if (nlen>60) nlen = 60;
                }  else if (clen>60)
                    nlen = 60;
                else nlen = clen;
                if (nlen != clen && clen>plen) {
                    Jsi_DSSetLength(sPtr, nlen);
                    Jsi_DSAppend(sPtr, "...", NULL);
                }
            }
        }
        if (interp->parent && interp->debugOpts.traceCallback) {
            Jsi_DString jStr={}, kStr={}, lStr={};
            Jsi_DSPrintf(&kStr, "[\"%s%s%s\", %s, %s, \"%s\", %d, %d ]",
                (fp?fp:""), (fp?".":""), fstatic->name, 
                (ret?"null":Jsi_JSONQuote(interp, Jsi_DSValue(&aStr),-1, &jStr)),
                (ret?Jsi_JSONQuote(interp, Jsi_DSValue(&dStr),-1, &lStr):"null"),
                 fname, ip->Line, ip->Lofs);
            if (Jsi_FunctionInvokeJSON(interp->parent, interp->debugOpts.traceCallback, Jsi_DSValue(&kStr), NULL) != JSI_OK)
                Jsi_DSPrintf(&pStr, "failed trace call\n");
            Jsi_DSFree(&jStr);
            Jsi_DSFree(&kStr);
            Jsi_DSFree(&lStr);
        } else if ((tc&jsi_callTraceBefore))
            Jsi_DSPrintf(&pStr, "%s:%d %*s#%d: %c %s%s%s(%s) %s\n",
                fname, ip->Line,
                (interp->level-1)*2, "", interp->level,
                (ret?'<':'>'), (fp?fp:""), (fp?".":""), fstatic->name, Jsi_DSValue(&aStr), Jsi_DSValue(&dStr));
        else {
            if (!interp->curIp || !interp->logOpts.before) {
                Jsi_DSPrintf(&pStr, "%*s#%d: %c %s%s%s(%s) in %s:%d%s\n", (interp->level-1)*2, "", interp->level,
                    (ret?'<':'>'), (fp?fp:""), (fp?".":""), fstatic->name, Jsi_DSValue(&aStr),
                fname, ip->Line, Jsi_DSValue(&dStr));
            } else {
                int quote = 0;
                jsi_SysPutsCmdPrefix(interp, &interp->logOpts, &pStr, &quote, NULL);
                Jsi_DSPrintf(&pStr, "%*s#%d: %c %s%s%s(%s) \n", (interp->level-1)*2, "", interp->level,
                    (ret?'<':'>'), (fp?fp:""), (fp?".":""), fstatic->name, Jsi_DSValue(&aStr));
                Jsi_DSAppend(&pStr, Jsi_DSValue(&dStr), NULL);
            }
        }
        if (Jsi_DSLength(&pStr))
            Jsi_Puts(interp, jsi_Stderr, Jsi_DSValue(&pStr), -1);
        Jsi_DSFree(&pStr);
        Jsi_DSFree(&dStr);
        Jsi_DSFree(&aStr);
    }
}

Project: jsish

bool Jsi_ValueIsUndef(Jsi_Interp *interp, Jsi_Value *pv)
{
    return (pv->vt == JSI_VT_UNDEF);
}

Project: jsish

Jsi_MapEntry* Jsi_MapSet(Jsi_Map *mapPtr, const void *key, const void *value){
    SIGASSERT(mapPtr, MAP);
    Jsi_MapEntry* mptr = NULL;
    switch (mapPtr->typ) {
        case JSI_MAP_HASH: mptr = (Jsi_MapEntry*)Jsi_HashSet(mapPtr->v.hash, (void*)key, (void*)value); break;
        case JSI_MAP_TREE: mptr = (Jsi_MapEntry*)Jsi_TreeSet(mapPtr->v.tree, (void*)key, (void*)value); break;
        case JSI_MAP_LIST: {
            mptr = Jsi_MapEntryNew(mapPtr, key, NULL);
            Jsi_MapValueSet(mptr, (void*)value);
            break;
        }
        case JSI_MAP_NONE: break;
    }
    return mptr;
}

Project: jsish

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

Project: jsish

Jsi_Channel Jsi_FSNameToChannel(Jsi_Interp *interp, const char *name)
{
    if (Jsi_Strlen(name)==1) {
        switch (name[0]) {
            case '0' : return jsiIntData.stdChans;
            case '1' : return jsiIntData.stdChans+1;
            case '2' : return jsiIntData.stdChans+2;
        }
    }
    if (!Jsi_Strcmp(name, "stdin")) return jsiIntData.stdChans;
    if (!Jsi_Strcmp(name, "stdout")) return jsiIntData.stdChans+1;
    if (!Jsi_Strcmp(name, "stderr")) return jsiIntData.stdChans+2;
    Jsi_Obj *obj = jsi_UserObjFromName(interp, name);
    if (!obj)
        return NULL;
    Jsi_UserObj *uobj = obj->d.uobj;
    if (uobj->reg != &fileobject)
        return NULL;
    FileObj *fobj = (FileObj *)uobj->data;
    return fobj->chan;
}

Project: jsish

static bool jsi_FSPathInFilesystemProc(Jsi_Interp *interp, Jsi_Value* path,void **clientDataPtr) {return 1;}

Project: jsish

static Jsi_Value *jsonNewStringObj(Jsi_Interp *interp, const char* str, int len)
{
    Jsi_Value *v = NULL;
    Jsi_DString dStr;
    Jsi_DSInit(&dStr);
    jsonNewDString(interp, &dStr, str, len);
    v = Jsi_ValueNew(interp);
    Jsi_ValueFromDS(interp, &dStr, &v);
    //Jsi_DSFree(&dStr);
    return v;
}

Project: jsish

static int tree_inorder(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_inorder(treePtr, hPtr->right, callback, data) != JSI_OK || epoch != treePtr->epoch)
            return JSI_ERROR;
    }
    if (callback(treePtr, hPtr, data) != JSI_OK || epoch != treePtr->epoch)
        return JSI_ERROR;
    Assert(hPtr->treePtr);
    if (hPtr->left != NULL) {
        if (tree_inorder(treePtr, hPtr->left, callback, data) != JSI_OK || epoch != treePtr->epoch)
            return JSI_ERROR;
    }
    return JSI_OK;
}

Project: jsish

jsi_GetOption(Jsi_Interp *interp, Jsi_OptionSpec *specPtr, void* record, const char *option, Jsi_Value **valuePtr, Jsi_Wide flags)
{
    char *ptr;
    
    if (specPtr == NULL) 
        return Jsi_LogError("no such option: %s", option);
    //isNull = ((*string == '\0') && (specPtr->flags & JSI_OPTION_NULL_OK));
    
    ptr = (char *)record + specPtr->offset;
    if (_JSICASTINT(specPtr->id)<0 || specPtr->id>=JSI_OPTION_END) 
        return Jsi_LogError("no such option: %s", option);
    if (specPtr->custom) {
        Jsi_OptionCustom* cust = Jsi_OptionCustomBuiltin(specPtr->custom);
        if (cust->formatProc)
            return (*cust->formatProc) (interp, specPtr, valuePtr, NULL, record, flags);
    }

    switch (specPtr->id) {
    case JSI_OPTION_BOOL:
        Jsi_ValueMakeBool(interp, valuePtr,*(bool*)ptr );
        break;
    case JSI_OPTION_INT:
        Jsi_ValueMakeNumber(interp, valuePtr, (Jsi_Number)(*(int *)ptr));
        break;
    case JSI_OPTION_UINT:
        Jsi_ValueMakeNumber(interp, valuePtr, (Jsi_Number)(*(uint *)ptr));
        break;
    case JSI_OPTION_INT8:
        Jsi_ValueMakeNumber(interp, valuePtr, (Jsi_Number)(*(int8_t *)ptr));
        break;
    case JSI_OPTION_INT16:
        Jsi_ValueMakeNumber(interp, valuePtr, (Jsi_Number)(*(int16_t *)ptr));
        break;
    case JSI_OPTION_INT32:
        Jsi_ValueMakeNumber(interp, valuePtr, (Jsi_Number)(*(int32_t *)ptr));
        break;
    case JSI_OPTION_INT64:
        Jsi_ValueMakeNumber(interp, valuePtr, (Jsi_Number)(*(Jsi_Wide *)ptr));
        break;
    case JSI_OPTION_UINT8:
        Jsi_ValueMakeNumber(interp, valuePtr, (Jsi_Number)(*(uint8_t *)ptr));
        break;
    case JSI_OPTION_UINT16:
        Jsi_ValueMakeNumber(interp, valuePtr, (Jsi_Number)(*(uint16_t *)ptr));
        break;
    case JSI_OPTION_UINT32:
        Jsi_ValueMakeNumber(interp, valuePtr, (Jsi_Number)(*(uint32_t *)ptr));
        break;
    case JSI_OPTION_UINT64:
        Jsi_ValueMakeNumber(interp, valuePtr, (Jsi_Number)(*(uint64_t *)ptr));
        break;
    case JSI_OPTION_FLOAT:
        Jsi_ValueMakeNumber(interp, valuePtr, (Jsi_Number)*(float *)ptr);
        break;
    case JSI_OPTION_DOUBLE:
        Jsi_ValueMakeNumber(interp, valuePtr, (Jsi_Number)*(double *)ptr);
        break;
    case JSI_OPTION_LDOUBLE:
        Jsi_ValueMakeNumber(interp, valuePtr, (Jsi_Number)*(ldouble *)ptr);
        break;
    case JSI_OPTION_NUMBER:
        Jsi_ValueMakeNumber(interp, valuePtr, *(Jsi_Number *)ptr);
        break;
    case JSI_OPTION_SIZE_T:
        Jsi_ValueMakeNumber(interp, valuePtr, *(size_t *)ptr);
        break;
    case JSI_OPTION_SSIZE_T:
        Jsi_ValueMakeNumber(interp, valuePtr, *(ssize_t *)ptr);
        break;
    case JSI_OPTION_INTPTR_T:
        Jsi_ValueMakeNumber(interp, valuePtr, *(intptr_t *)ptr);
        break;
    case JSI_OPTION_UINTPTR_T:
        Jsi_ValueMakeNumber(interp, valuePtr, *(uintptr_t *)ptr);
        break;
    case JSI_OPTION_LONG:
        Jsi_ValueMakeNumber(interp, valuePtr, *(long *)ptr);
        break;
    case JSI_OPTION_ULONG:
        Jsi_ValueMakeNumber(interp, valuePtr, *(ulong *)ptr);
        break;
    case JSI_OPTION_SHORT:
        Jsi_ValueMakeNumber(interp, valuePtr, *(short *)ptr);
        break;
    case JSI_OPTION_USHORT:
        Jsi_ValueMakeNumber(interp, valuePtr, *(ushort *)ptr);
        break;
        
    case JSI_OPTION_DSTRING:
        Jsi_ValueFromDS(interp, (Jsi_DString*)ptr, valuePtr);
        break;
    
    case JSI_OPTION_TIME_W: {
        Jsi_DString dStr = {};
        Jsi_DatetimeFormat(interp, (Jsi_Number)(*(Jsi_Wide*)ptr), "", 0, &dStr);
        Jsi_ValueFromDS(interp, &dStr, valuePtr);
        break;
    }
    case JSI_OPTION_TIME_D: {
        Jsi_DString dStr = {};
        Jsi_DatetimeFormat(interp, (Jsi_Number)(*(double*)ptr), "", 0, &dStr);
        Jsi_ValueFromDS(interp, &dStr, valuePtr);
        break;
    }
    case JSI_OPTION_TIME_T: {
        Jsi_DString dStr = {};
        Jsi_DatetimeFormat(interp, 1000LL* (Jsi_Number)*(time_t*)ptr, "%Y-%m-%d %H:%M:%S", 0, &dStr);
        Jsi_ValueFromDS(interp, &dStr, valuePtr);
        break;
    }
    case JSI_OPTION_STRBUF:
        if (ptr)
            Jsi_ValueMakeStringDup(interp, valuePtr, ptr);
        else
            Jsi_ValueMakeNull(interp, valuePtr);
        break;

    case JSI_OPTION_STRKEY:
        ptr = *(char **)ptr;
        if (ptr)
            Jsi_ValueMakeStringDup(interp, valuePtr, ptr);
        else
            Jsi_ValueMakeNull(interp, valuePtr);
        break;

    case JSI_OPTION_STRING:
    case JSI_OPTION_VAR:
    case JSI_OPTION_FUNC:
    case JSI_OPTION_USEROBJ:
    case JSI_OPTION_OBJ:
    case JSI_OPTION_VALUE:
    case JSI_OPTION_REGEXP:
    case JSI_OPTION_ARRAY:
        if (*(Jsi_Value **)ptr)
            Jsi_ValueCopy(interp, *valuePtr, *(Jsi_Value **)ptr);
        else
            Jsi_ValueMakeNull(interp, valuePtr);
        break;

    case JSI_OPTION_CUSTOM:
        break;

#ifdef __cplusplus
    case JSI_OPTION_END:
#else
    default:
#endif
        Jsi_LogBug("invalid option id %d", specPtr->id);
        return JSI_ERROR;
    }

    return JSI_OK;
}

Project: jsish

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

    if (value->vt == JSI_VT_NUMBER) {
        *n = value->d.num;
        return JSI_OK;
    }
    if (value->vt == JSI_VT_OBJECT && value->d.obj->ot == JSI_OT_NUMBER) {
        *n = value->d.obj->d.num;
        return JSI_OK;
    }
    if (interp)
        Jsi_LogError("invalid number");
    return JSI_ERROR;
}

Project: jsish

static void ValueFree(Jsi_Interp *interp, Jsi_Value* v)
{
    SIGASSERTV(v,VALUE);
    //printf("FREE: %d\n", interp->valueCnt);
    switch (v->vt) {
        case JSI_VT_OBJECT:
            Jsi_ObjDecrRefCount(interp, v->d.obj);
            break;
        case JSI_VT_VARIABLE:
            assert(v->d.lval != v);
            Jsi_DecrRefCount(interp, v->d.lval);
            break;
        case JSI_VT_STRING:
            if (v->d.s.str && !v->f.bits.isstrkey) {
                Jsi_Free(v->d.s.str);
                /*Jsi_HashEntry *hPtr;
                if ((hPtr = Jsi_HashEntryFind(strDebug, v->d.s.str)))
                    Jsi_HashEntryDelete(hPtr);*/
            }
            break;
        default:
            break;
    }
    v->vt = JSI_VT_UNDEF;
}

Project: jsish

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

Project: jsish

Jsi_RC jsi_InitOptions(Jsi_Interp *interp, int release) {
    if (release) return JSI_OK;
    assert((sizeof(jsi_OptTypeInfo)/sizeof(jsi_OptTypeInfo[0])) == (JSI_OPTION_END+1));
    int i;
    for (i=JSI_OPTION_BOOL; i<JSI_OPTION_END; i++)
        assert(jsi_OptTypeInfo[i].id == (Jsi_OptionId)i);
    return JSI_OK;
}