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

Jsi_Interp* Jsi_Main(Jsi_InterpOpts *opts)
{
    Jsi_RC rc = JSI_OK;
    Jsi_Interp* interp = NULL;
    int argc = 0, first = 1;
    char **argv = NULL;
    if (opts) {
        interp = opts->interp;
        argc = opts->argc;
        argv = opts->argv;
    }
    if (!interp)
        interp = Jsi_InterpNew(opts);
    if (!interp)
        return NULL;
    Jsi_InterpOnDelete(interp, &jsi_InterpDelete, (void*)&jsi_InterpDelete);
    argc -= interp->iskips;
    argv += interp->iskips;

#ifndef NO_JAZ
    /* Mount zip at end of executable */
    Jsi_Value *v = Jsi_Executable(interp);
    const char *exeFile = (v?Jsi_ValueString(interp, v, NULL):NULL);
    int jsFound = 0;
    if (v && (argc != 2 || Jsi_Strcmp(argv[1], "--nozvfs"))) {
        rc = Jsi_EvalZip(interp, exeFile, JSI_ZVFS_DIR, &jsFound);
        if (rc == JSI_OK) {
            interp->selfZvfs = 1;
            if (!jsFound) {
#if (JSI__FILESYS && JSI__ZVFS)
                fprintf(stderr, "warning: no main.jsi or autoload.jsi\n");
#endif
            }
            if (jsi_deleted)
                return jsi_DoExit(interp, jsi_exitCode);
            else if (rc != 0) {
                fprintf(stderr, "Error\n");
                return jsi_DoExit(interp, 1);
            }
        }
    }
#endif
    const char *ext = NULL, *ai1, *iext = (argc<=1?NULL:Jsi_Strrchr(argv[1], '.'));
    if (interp->selfZvfs && iext && Jsi_Strcmp(iext,".fossil")==0) {
        rc = Jsi_EvalString(interp, "runModule('Archive');", JSI_EVAL_ISMAIN);
        goto done;
    }
    Jsi_ShiftArgs(interp, NULL);
    if (argc <= 1) {
        if (interp->opts.no_interactive && !interp->interactive)
            return interp;
        rc = Jsi_Interactive(interp, JSI_OUTPUT_QUOTE|JSI_OUTPUT_NEWLINES);
        goto done;
    }
    ai1 = argv[1];
    if ((!Jsi_Strcmp(ai1, "-help") || !Jsi_Strcmp(ai1, "-h")) && argc<=3) {
        if (argc>2) {
            if (Jsi_PkgRequire(interp, "Help", 0)>=0) {
                char tbuf[BUFSIZ];
                snprintf(tbuf, sizeof(tbuf), "return runModule('Help', '%s'.trim().split(null));", argv[2]);
                Jsi_RC rc = Jsi_EvalString(interp, tbuf, 0);
                const char *hstr = Jsi_ValueToString(interp, interp->retValue, NULL);
                if (rc == JSI_OK)
                    puts(hstr);
                return jsi_DoExit(interp, 1);
            }
        }
        dohelp:
        puts("USAGE:\n  jsish [PREFIX-OPTS] [COMMAND-OPTS|FILE] ...\n"
          "\nPREFIX-OPTS:\n"
          "  --C FILE\tOption file of config options.\n"
          "  --F\t\tTrace all function calls and returns.\n"
          "  --I OPT:VAL\tInterp option: equivalent to Interp.conf({OPT:VAL}).\n"
          "  --L PATH\tSet safeMode to \"lockdown\" using PATH for safe(Read/Write)Dirs.\n"
          "  --T OPT\tTypecheck option: equivalent to \"use OPT\".\n"
          "  --U\t\tDisplay unittest output, minus pass/fail compare.\n"
          "  --V\t\tSame as --U, but adds file and line number to output.\n"
          "\nCOMMAND-OPTS:\n"
          "  -a\t\tArchive: mount an archive (zip, sqlar or fossil repo) and run module.\n"
          "  -c\t\tCData: generate .c or JSON output from a .jsc description.\n"
          "  -d\t\tDebug: console script debugger.\n"
          "  -e CODE ...\tEvaluate javascript CODE.\n"
          "  -g\t\tGendeep: generate html output from markdeep source.\n"
          "  -h ?CMD?\tHelp: show help for jsish or its commands.\n"
          "  -m\t\tModule: utility create/manage/invoke a Module.\n"
          "  -s\t\tSafe: runs script in safe sub-interp.\n"
          "  -u\t\tUnitTest: test script file(s) or directories .js/.jsi files.\n"
          "  -w\t\tWget: web client to download file from url.\n"
          "  -v\t\tVersion: show version detail: add an arg to show only X.Y.Z\n"
          "  -z\t\tZip: append/manage zip files at end of executable.\n"
          "  -D\t\tDebugUI: web-gui script debugger.\n"
          "  -J\t\tJSpp: preprocess javascript for web.\n"
          "  -S\t\tSqliteUI: web-gui for sqlite database file.\n"
          "  -W\t\tWebsrv: web server to serve out content.\n"
          "\nInterp options may also be set via the confFile.'\n"
           );
        return jsi_DoExit(interp, 1);
    }
    if (!Jsi_Strcmp(ai1, "-version"))
        ai1 = "-v";
    if (ai1[0] == '-') {
        switch (ai1[1]) {
            case 'a':
                rc = Jsi_EvalString(interp, "runModule('Archive');", JSI_EVAL_ISMAIN);
                break;
            case 'c':
                rc = Jsi_EvalString(interp, "runModule('Cdata');", JSI_EVAL_ISMAIN);
                break;
            case 'd':
                interp->debugOpts.isDebugger = 1;
                rc = Jsi_EvalString(interp, "runModule('Debug');", JSI_EVAL_ISMAIN);
                break;
            case 'D':
                interp->debugOpts.isDebugger = 1;
                rc = Jsi_EvalString(interp, "runModule('DebugUI');", JSI_EVAL_ISMAIN);
                break;
            case 'e':
                if (argc < 3)
                    rc = Jsi_LogError("missing argument");
                else {
                    rc = Jsi_EvalString(interp, argv[2], JSI_EVAL_ISMAIN|JSI_EVAL_NOSKIPBANG);
                    if (rc == JSI_OK && argc>3) {
                        first += 2;
                        Jsi_ShiftArgs(interp, NULL);
                        Jsi_ShiftArgs(interp, NULL);
                        goto dofile;
                    }
                }
                break;
            case 'g':
                rc = Jsi_EvalString(interp, "runModule('GenDeep');", JSI_EVAL_ISMAIN);
                break;
            case 'h':
                goto dohelp;
            case 'J':
                rc = Jsi_EvalString(interp, "runModule('Jspp');", JSI_EVAL_ISMAIN);
                break;
            case 'm':
                if (argc <= 2 || argv[2][0] == '-')
                    rc = Jsi_EvalString(interp, "runModule('Module');", JSI_EVAL_ISMAIN);
                else {
                    Jsi_DString dStr = {}, eStr = {};
                    const char *cps, *cpe;
                    cps = Jsi_Strrchr(argv[2], '/');
                    if (cps) cps++; else cps = argv[2];
                    cpe = Jsi_Strrchr(cps, '.');
                    int len = (cpe?cpe-cps:(int)Jsi_Strlen(cps));
                    if (cpe)
                        Jsi_DSPrintf(&dStr, "source(\"%s\");", argv[2]);
                    else
                        Jsi_DSPrintf(&dStr, "require(\"%s\");", argv[2]);
                    Jsi_DSPrintf(&dStr, "puts(runModule(\"%.*s\",console.args.slice(1)));", len, cps);
                    rc = Jsi_EvalString(interp, Jsi_DSValue(&dStr), JSI_EVAL_NOSKIPBANG);
                    Jsi_DSFree(&dStr);
                    Jsi_DSFree(&eStr);
                }
                break;
            case 's':
                rc = Jsi_EvalString(interp, "runModule('Safe');", JSI_EVAL_ISMAIN);
                break;
            case 'S':
                rc = Jsi_EvalString(interp, "runModule('SqliteUI');", JSI_EVAL_ISMAIN);
                break;
            case 'u':
                rc = Jsi_EvalString(interp, "exit(runModule('UnitTest'));", JSI_EVAL_ISMAIN);
                break;
            case 'v': {
                char str[200] = "\n";
                    
                Jsi_Value* fval = Jsi_ValueNewStringKey(interp, "/zvfs/lib/sourceid.txt");
                if (!Jsi_Access(interp, fval, R_OK)) {
                    Jsi_Channel chan = Jsi_Open(interp, fval, "r");
                    if (chan)
                        Jsi_Read(interp, chan, str, sizeof(str));
                }
                if (argc>2)
                    printf("%u.%u.%u\n", JSI_VERSION_MAJOR, JSI_VERSION_MINOR, JSI_VERSION_RELEASE);
                else 
                    printf("%u.%u.%u %." JSI_VERFMT_LEN JSI_NUMGFMT " %s", JSI_VERSION_MAJOR, JSI_VERSION_MINOR, JSI_VERSION_RELEASE, Jsi_Version(), str);
                return jsi_DoExit(interp, 1);
            }
            case 'w':
                rc = Jsi_EvalString(interp, "runModule('Wget');", JSI_EVAL_ISMAIN);
                break;
            case 'W':
                rc = Jsi_EvalString(interp, "runModule('Websrv');", JSI_EVAL_ISMAIN);
                break;
            case 'z':
                rc = Jsi_EvalString(interp, "runModule('Zip');", JSI_EVAL_ISMAIN);
                break;
            default:
                puts("usage: jsish [  --C FILE | --I OPT:VAL | --L PATH | --T OPT | --U | --V | --F ] | -e STRING |\n\t"
                "| -a | -c | -d | -D | -h | -m | -s | -S | -u | -v | -w | -W | -z | FILE ...\nUse -help for long help.");
                return jsi_DoExit(interp, 1);
        }
    } else {
dofile:
        ext = Jsi_Strrchr(argv[first], '.');

        /* Support running "main.jsi" from a zip file. */
        if (ext && (Jsi_Strcmp(ext,".zip")==0 ||Jsi_Strcmp(ext,".jsz")==0 ) ) {
            rc = Jsi_EvalZip(interp, argv[first], NULL, &jsFound);
            if (rc<0) {
                fprintf(stderr, "zip mount failed\n");
                return jsi_DoExit(interp, 1);
            }
            if (!(jsFound&JSI_ZIP_MAIN)) {
                fprintf(stderr, "main.jsi not found\n");
                return jsi_DoExit(interp, 1);
            }
        } else if (ext && !Jsi_Strcmp(ext,".jsc")) {
            Jsi_DString dStr = {};
            Jsi_DSPrintf(&dStr, "console.args.unshift('%s'); runModule('CData');", argv[first]);
            rc = Jsi_EvalString(interp, Jsi_DSValue(&dStr), JSI_EVAL_ISMAIN|JSI_EVAL_NOSKIPBANG);
            Jsi_DSFree(&dStr);

        } else {
            if (argc>1) {
                jsi_vf = Jsi_ValueNewStringKey(interp, argv[first]);
                Jsi_IncrRefCount(interp, jsi_vf);
            }
            rc = Jsi_EvalFile(interp, jsi_vf, JSI_EVAL_ARGV0|JSI_EVAL_AUTOINDEX|JSI_EVAL_ISMAIN);
            if (jsi_vf) {
                Jsi_DecrRefCount(interp, jsi_vf);
                jsi_vf = NULL;
            }

        }
    }
    if (jsi_deleted) //TODO: rationalize jsi_deleted, jsi_exitCode, etc
        return jsi_DoExit(rc==JSI_EXIT?NULL:interp, jsi_exitCode);
    if (rc == JSI_OK) {
        /* Skip output from an ending semicolon which evaluates to undefined */
        Jsi_Value *ret = Jsi_ReturnValue(interp);
        if (!Jsi_ValueIsType(interp, ret, JSI_VT_UNDEF)) {
            Jsi_DString dStr = {};
            fputs(Jsi_ValueGetDString(interp, ret, &dStr, 0), stdout);
            Jsi_DSFree(&dStr);
            fputs("\n", stdout);
        }
    } else {
        if (!interp->parent && !interp->isHelp)
            fprintf(stderr, "ERROR: %s\n", interp->errMsgBuf);
        return jsi_DoExit(interp, 1);
    }

done:
    if (rc == JSI_EXIT) {
        if (opts)
            opts->exitCode = jsi_exitCode;
        return NULL;
    }
    if (jsi_deleted == 0 && interp->opts.auto_delete) {
        Jsi_InterpDelete(interp);
        return NULL;
    }
    return interp;
}

Project: jsish

Jsi_RC Jsi_ListConf(Jsi_List *listPtr, Jsi_MapOpts *opts, bool set)
{
    if (set) {
        listPtr->opts = *opts;
    } else {
        *opts = listPtr->opts;
    }
    return JSI_OK;
}

Project: jsish

static void dbRollbackHandler(void *cd) {
    Jsi_Db *jdb = (Jsi_Db*)cd;
    Jsi_Interp *interp = jdb->interp;
    Jsi_Value *vpargs, *items[2] = {}, *ret = Jsi_ValueNew1(interp);
    
    items[0] = Jsi_ValueNewObj(interp, jdb->fobj);
    vpargs = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, items, 1, 0));
    Jsi_IncrRefCount(interp, vpargs);
    Jsi_FunctionInvoke(interp, jdb->onRollback, NULL, &ret, NULL);
    Jsi_DecrRefCount(interp, vpargs);
    Jsi_DecrRefCount(interp, ret);
}

Project: jsish

static char * jsi_FSGetsProc(Jsi_Channel chan, char *s, int size) {
    return fgets(s, size, _JSI_GETFP(chan,1));
}

Project: jsish

static void jsiPush(Jsi_Interp* interp, int n) {
    int i = 0;
    do {
        if (!_jsi_STACKIDX(interp->framePtr->Sp))
            _jsi_STACKIDX(interp->framePtr->Sp) = Jsi_ValueNew1(interp);
        if (!_jsi_THISIDX(interp->framePtr->Sp))
            _jsi_THISIDX(interp->framePtr->Sp) = Jsi_ValueNew1(interp);
        if (i++ >= n) break;
        interp->framePtr->Sp++;
    } while (1);
}

Project: jsish

Jsi_Value* Jsi_ValueMakeString(Jsi_Interp *interp, Jsi_Value **vPtr, const char *s) {
    return Jsi_ValueMakeBlob(interp, vPtr, (unsigned char *)s, Jsi_Strlen(s));
}

Project: jsish

static int dbProgressHandler(void *cd) {
    Jsi_Db *jdb = (Jsi_Db*)cd;
    Jsi_Value *vpargs, *items[3] = {}, *ret;
    Jsi_Interp *interp = jdb->interp;

    items[0] = Jsi_ValueNewObj(interp, jdb->fobj);
    vpargs = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, items, 1, 0));
    Jsi_IncrRefCount(interp, vpargs);
    ret = Jsi_ValueNew1(interp);
    int rc = Jsi_FunctionInvoke(interp, jdb->onProgress, vpargs, &ret, NULL);
    if( JSI_OK!=rc ) {
        jdb->errCnt++;
        rc = 1;
    } else
        rc = dbGetIntBool(interp, ret);
    Jsi_DecrRefCount(interp, vpargs);
    Jsi_DecrRefCount(interp, ret);
    return rc;
}

Project: jsish

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

Project: jsish

bool Jsi_ValueIsObjType(Jsi_Interp *interp, Jsi_Value *v, Jsi_otype otype)
{
    if (v == NULL || v->vt != JSI_VT_OBJECT)
        return 0;
    if (otype != JSI_OT_ARRAY)
        return (v->d.obj->ot == otype);
    if (v->d.obj->ot != JSI_OT_OBJECT || !v->d.obj->isarrlist)
        return 0;
    return 1;
}

Project: jsish

Jsi_OptionsDup(Jsi_Interp *interp, const Jsi_OptionSpec *staticSpecs)
{
    unsigned int entrySpace = sizeof(Jsi_OptionSpec);
    const Jsi_OptionSpec *staticSpecPtr;
    Jsi_OptionSpec *newSpecs;

    for (staticSpecPtr=staticSpecs; staticSpecPtr->id>=JSI_OPTION_BOOL && staticSpecPtr->id<JSI_OPTION_END && staticSpecPtr->name;
            staticSpecPtr++) {
        entrySpace += sizeof(Jsi_OptionSpec);
    }

    newSpecs = (Jsi_OptionSpec *) Jsi_Malloc(entrySpace);
    memcpy((void *) newSpecs, (void *) staticSpecs, entrySpace);
    return newSpecs;
}

Project: jsish

static Jsi_RC jsi_wsfreeHandlers(Jsi_Interp *interp, Jsi_HashEntry* hPtr, void *ptr) {
    jsi_wsHander *h = (jsi_wsHander*)ptr;
    if (!h)
        return JSI_OK;
    if (h->val)
        Jsi_DecrRefCount(interp, h->val);
    if (h->objVar)
        Jsi_DecrRefCount(interp, h->objVar);
    Jsi_Free(h);
    return JSI_OK;
}

Project: jsish

static Jsi_Filesystem *jsi_FilesysFind(const char *name)
{
    FSList *fsPtr = jsiFSList;
    while (fsPtr != NULL) {
        if (!Jsi_Strcmp(fsPtr->fsPtr->typeName, name))
            return fsPtr->fsPtr;
        fsPtr = fsPtr->next;
    }
    return NULL;
}

Project: jsish

const char *jsi_DbOptionTypeStr(Jsi_OptionId typ, bool cname)
{
    const Jsi_OptionTypedef* ti = Jsi_OptionTypeInfo(typ);
    if (ti)
        return (cname?ti->cName:ti->idName);
    return NULL;
}

Project: jsish

int Jsi_OptionsChanged(Jsi_Interp *interp, Jsi_OptionSpec *spec, const char *pattern, ...)
{
    va_list argList;
    Jsi_OptionSpec *specPtr;
    const char *option = pattern;
    int cnt = 0;
    
    va_start(argList, pattern);
    spec = jsi_GetCachedOptionSpecs(interp, spec);
    do  {
        for (specPtr = spec; specPtr->id>=JSI_OPTION_BOOL && specPtr->id < JSI_OPTION_END; specPtr++) {
            if ((Jsi_GlobMatch(option, specPtr->name, 0)) &&
                    (specPtr->flags & JSI_OPT_IS_SPECIFIED)) {
                cnt++;
            }
        }
        assert(specPtr->id == JSI_OPTION_END);

    } while ((option = va_arg(argList, char *)) != NULL);
    va_end(argList);
    return cnt;
}

Project: jsish

static Jsi_RC FilesysOpenCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    UdfGet(udf, _this, funcPtr);
    if (try_open_file(interp, udf, args) != JSI_OK) {
        Jsi_ValueMakeBool(interp, ret, 0);
    }
    Jsi_ValueMakeBool(interp, ret, 1);
    return JSI_OK;
}

Project: jsish

static Jsi_RC MySqlConfCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                         Jsi_Value **ret, Jsi_Func *funcPtr)
{
    MySqlObj *jdb;
    if (!(jdb = _mysql_getDbHandle(interp, _this, funcPtr))) return JSI_ERROR;
    int oms = jdb->maxStmts;
    const char *oldu = jdb->user, *oldpw = jdb->password, *olddb = jdb->database;
    Jsi_RC rc = Jsi_OptionsConf(interp, SqlOptions, jdb, Jsi_ValueArrayIndex(interp, args, 0), ret, 0);
    if (jdb->maxStmts<0 || jdb->maxStmts>MAX_PREPARED_STMTS) {
        JSI_DBQUERY_PRINTF( "option maxStmts value %d is not in range 0..%d", jdb->maxStmts, MAX_PREPARED_STMTS);
        jdb->maxStmts = oms;
        rc = JSI_ERROR;
    }
    if (oldu != jdb->user || oldpw != jdb->password || olddb != jdb->database)
        if (!mysql_change_user(jdb->db, jdb->user, jdb->password, jdb->database)) {
            rc = JSI_ERROR;
            jdb->user = oldu;
            jdb->password = oldpw;
            jdb->database = olddb;
        }
    mdbStmtLimit(jdb);
    return rc;
}

Project: jsish

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

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

Project: jsish

static Jsi_RC DebugAddCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    if (!interp->breakpointHash)
        interp->breakpointHash = Jsi_HashNew(interp, JSI_KEYS_STRING, jsi_HashFree);
    int argc = Jsi_ValueGetLength(interp, args);
    jsi_BreakPoint *bptr, bp = {};
    Jsi_Number vnum;
    if (argc>1 && Jsi_ValueGetBoolean(interp, Jsi_ValueArrayIndex(interp, args, 1), &bp.temp) != JSI_OK) 
        return Jsi_LogError("bad boolean");
    Jsi_Value *v = Jsi_ValueArrayIndex(interp, args, 0);
    if (Jsi_ValueGetNumber(interp, v, &vnum) == JSI_OK) {
        bp.line = (int)vnum;
        bp.file = interp->curFile;
    } else {
        const char *val = Jsi_ValueArrayIndexToStr(interp, args, 0, NULL);
        const char *cp;
        
        if (isdigit(val[0])) {
            if (Jsi_GetInt(interp, val, &bp.line, 0) != JSI_OK) 
                return Jsi_LogError("bad number");
            bp.file = interp->curFile;
        } else if ((cp = Jsi_Strchr(val, ':'))) {
            if (Jsi_GetInt(interp, cp+1, &bp.line, 0) != JSI_OK) 
                return Jsi_LogError("bad number");
            Jsi_DString dStr = {};
            Jsi_DSAppendLen(&dStr, val, cp-val);
            bp.file = Jsi_KeyAdd(interp, Jsi_DSValue(&dStr));
            Jsi_DSFree(&dStr);
        } else {
            bp.func = Jsi_KeyAdd(interp, val);
        }
    }
    if (bp.line<=0 && !bp.func) 
        return Jsi_LogError("bad number");
    char nbuf[JSI_MAX_NUMBER_STRING];
    bp.id = ++interp->debugOpts.breakIdx;
    bp.enabled = 1;
    snprintf(nbuf, sizeof(nbuf), "%d", bp.id);
    bptr = (jsi_BreakPoint*)Jsi_Malloc(sizeof(*bptr));
    *bptr = bp;
    Jsi_HashSet(interp->breakpointHash, (void*)nbuf, bptr);
    Jsi_ValueMakeNumber(interp, ret, (Jsi_Number)bp.id);
    return JSI_OK;
}

Project: jsish

static Jsi_OpCodes *code_subscript(jsi_Pstate *p, jsi_Pline *line, int right_val) { JSI_NEW_CODESLN(0,OP_SUBSCRIPT, right_val); }

Project: jsish

Jsi_Value *Jsi_StringSplit(Jsi_Interp *interp, const char *str, const char *spliton)
{
    char **argv; int argc;
    Jsi_DString dStr;
    Jsi_DSInit(&dStr);
    Jsi_SplitStr(str, &argc, &argv, spliton, &dStr);
    Jsi_Value *nret = Jsi_ValueNewArray(interp, NULL, 0);
    Jsi_Obj *obj = nret->d.obj;
    int i;
    for (i = 0; i < argc; ++i) {
        Jsi_ObjArraySet(interp, obj, Jsi_ValueNewStringDup(interp, argv[i]), i);
    }
    Jsi_ObjSetLength(interp, obj, argc);
    Jsi_ValueMakeArrayObject(interp, &nret, obj);
    Jsi_DSFree(&dStr);
    return nret;
}

Project: jsish

static void jsi_wswebsocketObjErase(jsi_wsCmdObj *cmdPtr)
{
    if (cmdPtr->interp) {
        if (cmdPtr->event)
            Jsi_EventFree(cmdPtr->interp, cmdPtr->event);
        cmdPtr->event = NULL;
        if (cmdPtr->hasOpts)
            Jsi_OptionsFree(cmdPtr->interp, WSOptions, cmdPtr, 0);
        cmdPtr->hasOpts = 0;
        if (cmdPtr->handlers)
            Jsi_HashDelete(cmdPtr->handlers);
        cmdPtr->handlers = NULL;
        if (cmdPtr->pssTable)
            Jsi_HashDelete(cmdPtr->pssTable);
        cmdPtr->pssTable = NULL;
        if (cmdPtr->fileHash)
            Jsi_HashDelete(cmdPtr->fileHash);
        cmdPtr->fileHash = NULL;
    }
    cmdPtr->interp = NULL;
}

Project: jsish

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

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

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

Project: jsish

static void jsiDumpInstr(Jsi_Interp *interp, jsi_Pstate *ps, Jsi_Value *_this,
    jsi_TryList *trylist, jsi_OpCode *ip, Jsi_OpCodes *opcodes)
{
    int i;
    char buf[JSI_MAX_NUMBER_STRING*2];
    jsi_code_decode(interp, ip, ip - opcodes->codes, buf, sizeof(buf));
    Jsi_Printf(interp, jsi_Stderr, "%p: %-30.200s : THIS=%s, STACK=[", ip, buf, jsi_evalprint(_this));
    for (i = 0; i < interp->framePtr->Sp; ++i) {
        Jsi_Printf(interp, jsi_Stderr, "%s%s", (i>0?", ":""), jsi_evalprint(_jsi_STACKIDX(i)));
    }
    Jsi_Printf(interp, jsi_Stderr, "]");
    if (ip->fname) {
        const char *fn = ip->fname,  *cp = Jsi_Strrchr(fn, '/');
        if (cp) fn = cp+1;
        Jsi_Printf(interp, jsi_Stderr, ", %s:%d", fn, ip->Line);
    }
    Jsi_Printf(interp, jsi_Stderr, "\n");
    jsi_TryList *tlt = trylist;
    for (i = 0; tlt; tlt = tlt->next) i++;
    if (ps->last_exception)
        Jsi_Printf(interp, jsi_Stderr, "TL: %d, excpt: %s\n", i, jsi_evalprint(ps->last_exception));
}

Project: jsish

static Jsi_Number mdbMyTimeToJS(MYSQL_TIME* mtm)
{
    struct tm tm;
    tm.tm_sec = mtm->second;
    tm.tm_min = mtm->minute;
    tm.tm_hour = mtm->hour;
    tm.tm_mday = mtm->day;
    tm.tm_mon = mtm->month-1;
    tm.tm_year = mtm->year - 1900;
    time_t tim = mktime(&tm);
    if (tim == (time_t)-1)
        return -1;
    return (Jsi_Number)tim*1000 + (Jsi_Number)mtm->second_part/1000000.0;
}

Project: jsish

char *jsiLNhints(const char *buf, int *color, int *bold) {
    int i, len = Jsi_Strlen(buf);
    for (i=0; jsiFilePreCmds[i]; i++)
        if (!Jsi_Strncmp(buf, jsiFilePreCmds[i], Jsi_Strlen(jsiFilePreCmds[i]))) break;
    if (jsiFilePreCmds[i]) {
        const char *ce = buf+len-1, *cp = "('<file>";
        if ((*ce =='\'' || *ce =='\"') && buf[len-2]=='(') cp+=2;
        else if (*ce=='(') cp++;
        else return NULL;
        
        *color = 35;
        *bold = 0;
        return (char*)cp;
    }
    return NULL;
}

Project: jsish

static void jsiClearStack(Jsi_Interp *interp, int ofs) {
    Jsi_Value **vPtr = &_jsi_STACKIDX(interp->framePtr->Sp-ofs), *v = *vPtr;
    if (!v) return;
#ifndef XX_NEWSTACK
    Jsi_ValueReset(interp, vPtr);
#else
    if (v->refCnt<=1)
        Jsi_ValueReset(interp, vPtr);
    else {
        Jsi_DecrRefCount(interp, v);
        _jsi_STACKIDX(interp->framePtr->Sp-ofs) = Jsi_ValueNew1(interp);
    }
#endif
}

Project: jsish

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

Project: jsish

static Jsi_RC MySqlQueryCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                             Jsi_Value **ret, Jsi_Func *funcPtr)
{
    Jsi_RC rc = JSI_OK;
    MySqlObj *jdb;
    if (!(jdb = _mysql_getDbHandle(interp, _this, funcPtr))) return JSI_ERROR;
    Jsi_Value *vSql = Jsi_ValueArrayIndex(interp, args, 0);
    Jsi_DString eStr = {};
    JSI_DSTRING_VAR(dStr, JSI_DB_DSTRING_SIZE);
    const char *zSql = Jsi_ValueGetDString(interp, vSql, &eStr, 0);
    Jsi_Value *arg = Jsi_ValueArrayIndex(interp, args, 1);
    int cnt = 0;
    char **apColName = NULL;
    Jsi_OptionId *apColType = NULL;
    int isopts = 0;
    MyDbEvalContext sEval = {};
    QueryOpts opts, *oEopt = NULL;
    opts = jdb->queryOpts;
    opts.callback = NULL;
    opts.width = NULL;
    Jsi_Value *callback = NULL, *width = NULL;
            
    if (arg) {
        if (Jsi_ValueIsFunction(interp,arg))
            callback = opts.callback = arg;
        else if (Jsi_ValueIsString(interp, arg))
            opts.objName = Jsi_ValueString(interp, arg, NULL);
        else if (Jsi_ValueIsObjType(interp, arg, JSI_OT_ARRAY))
            opts.values = arg;
        else if (Jsi_ValueIsObjType(interp, arg, JSI_OT_OBJECT))
            isopts = 1;
        else  {
            rc = Jsi_LogError("argument must be null, a function, string, array or options");
            goto bail;
        }
    }

    if (isopts) {
        if (Jsi_OptionsProcess(interp, QueryFmtOptions, &opts, arg, 0) < 0) {
            rc = JSI_ERROR;
            goto bail;
        }
        callback = (opts.callback ? opts.callback : jdb->queryOpts.callback);
        width = (opts.width ? opts.width : jdb->queryOpts.width);
    }
/*    if (jdb->queryOpts.CData) {
        char *cdata = (char*)jdb->queryOpts.CData;
        MySqlObjMultipleBind* copts = Jsi_CarrayLookup(interp, cdata);
        if (!copts) 
            return Jsi_LogError("unknown CData option: %s", jdb->queryOpts.CData);
        int n = MySqlObjQuery(jdb, copts->opts, copts->data, copts->numData, zSql, copts->flags);
        Jsi_ValueMakeNumber(interp, ret, (Jsi_Number)n);
        return JSI_OK;
    } */
    if (opts.objName) {
        if (Jsi_SqlObjBinds(interp, &eStr, opts.objName,  !(opts.objOpts&OBJMODE_NOTYPES), 
        !(opts.objOpts&OBJMODE_NODEFAULTS), (opts.objOpts&OBJMODE_NULLDEFAULTS)!=0) != JSI_OK)
            goto bail;
        zSql = Jsi_DSValue(&eStr);
    }
    if (!opts.separator) {
        switch (opts.mode) {
            case _mdb_EF_LIST: opts.separator = "|"; break;
            case _mdb_EF_COLUMN: opts.separator = " "; break;
            case _mdb_EF_TABS: opts.separator = "\t"; break;
            default: opts.separator = ",";
        }
    }
    Jsi_DString sStr;
    Jsi_DSInit(&sStr);
    sEval.nocache = opts.nocache;
    if (mdbEvalInit(interp, &sEval, jdb, zSql, &sStr, 0, 0) != JSI_OK) {
        rc = JSI_ERROR;
        goto bail;
    }
    sEval.namedParams = (opts.noNamedParams==0 && !opts.values);
    sEval.ret = *ret;
    oEopt = jdb->optPtr;
    jdb->optPtr = &opts;
    
    if (sEval.namedParams) {
        rc = mdbEvalPrep(&sEval);
        if (rc == JSI_ERROR)
            goto bail;
        if (rc == JSI_BREAK) {
            rc = JSI_OK;
            goto bail;
        }
    }
    if (opts.mode == _mdb_EF_NONE)
        goto bail;
    if (callback) {
        sEval.tocall = callback;
        if (opts.mode != _mdb_EF_ROWS)
            rc = Jsi_LogError("'mode' must be 'rows' with 'callback'");
        else 
            rc = mdbEvalCallCmd(&sEval, interp, JSI_OK);
        goto bail;
    }
    switch (opts.mode) {
        case _mdb_EF_NONE:
            while(JSI_OK==(rc = mdbEvalStep(&sEval)) ) {
                cnt++;
                if (opts.limit && cnt>=opts.limit) break;
            }
            goto bail;
            break;
        case _mdb_EF_JSON:
            if (opts.headers) {
                Jsi_DSAppend(dStr, "[ ", NULL);
                while( JSI_OK==(rc = mdbEvalStep(&sEval)) ) {
                    int i;
                    int nCol;
                    mdbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
                    if (cnt == 0) {
                        Jsi_DSAppend(dStr, "[", NULL);
                        for(i=0; i<nCol; i++) {
                            if (i)
                                Jsi_DSAppend(dStr, ", ", NULL);
                            Jsi_JSONQuote(interp, apColName[i], -1, dStr);
                        }
                        Jsi_DSAppend(dStr, "]", NULL);
                        cnt++;
                    }
                    if (cnt)
                        Jsi_DSAppend(dStr, ", ", NULL);
                    Jsi_DSAppend(dStr, "[", NULL);
                    for(i=0; i<nCol; i++) {
                        if (i)
                            Jsi_DSAppend(dStr, ", ", NULL);
                        mdbEvalSetColumnJSON(&sEval, i, dStr);
                    }
                    Jsi_DSAppend(dStr, "]", NULL);
                    cnt++;
                    if (opts.limit && cnt>opts.limit) break;
                }
                Jsi_DSAppend(dStr, " ]", NULL);
                
            } else {
                Jsi_DSAppend(dStr, "[ ", NULL);
                while( JSI_OK==(rc = mdbEvalStep(&sEval)) ) {
                    int i;
                    int nCol;
                    mdbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
                    if (cnt)
                        Jsi_DSAppend(dStr, ", ", NULL);
                    Jsi_DSAppend(dStr, "{", NULL);
                    for(i=0; i<nCol; i++) {
                        if (i)
                            Jsi_DSAppend(dStr, ", ", NULL);
                        Jsi_JSONQuote(interp, apColName[i], -1, dStr);
                        Jsi_DSAppend(dStr, ":", NULL);
                        mdbEvalSetColumnJSON(&sEval, i, dStr);
                    }
                    Jsi_DSAppend(dStr, "}", NULL);
                    cnt++;
                    if (opts.limit && cnt>=opts.limit) break;
                }
                Jsi_DSAppend(dStr, " ]", NULL);
            }
            break;
            
        case _mdb_EF_JSON2: {
                while( JSI_OK==(rc = mdbEvalStep(&sEval)) ) {
                    int i;
                    int nCol;
                    mdbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
                    if (cnt == 0 && 1) {
                        Jsi_DSAppend(dStr, "{ \"names\": [ ", NULL);
                        for(i=0; i<nCol; i++) {
                            if (i)
                                Jsi_DSAppend(dStr, ", ", NULL);
                            Jsi_JSONQuote(interp, apColName[i], -1, dStr);
                        }
                        Jsi_DSAppend(dStr, " ], \"values\": [ ", NULL);
                    }
                    if (cnt)
                        Jsi_DSAppend(dStr, ", ", NULL);
                    Jsi_DSAppend(dStr, "[", NULL);
                    for(i=0; i<nCol; i++) {
                        if (i)
                            Jsi_DSAppend(dStr, ", ", NULL);
                        mdbEvalSetColumnJSON(&sEval, i, dStr);
                    }
                    Jsi_DSAppend(dStr, " ]", NULL);
                    cnt++;
                    if (opts.limit && cnt>=opts.limit) break;
                }
                if (cnt)
                    Jsi_DSAppend(dStr, " ] } ", NULL);
            }
            break;
            
        case _mdb_EF_LIST:
            while( JSI_OK==(rc = mdbEvalStep(&sEval)) ) {
                int i;
                int nCol;
                mdbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
                if (cnt == 0 && opts.headers) {
                    for(i=0; i<nCol; i++) {
                        if (i)
                            Jsi_DSAppend(dStr, opts.separator, NULL);
                        Jsi_DSAppend(dStr, apColName[i], NULL);
                    }
                }
    
                if (cnt || opts.headers)
                    Jsi_DSAppend(dStr, "\n", NULL);
                for(i=0; i<nCol; i++) {
                    if (i)
                        Jsi_DSAppend(dStr, opts.separator, NULL);
                    mdbEvalSetColumn(&sEval, i, dStr);
                }
                cnt++;
                if (opts.limit && cnt>=opts.limit) break;
            }
            break;
            
        case _mdb_EF_COLUMN: {
            int *wids = NULL;
            Jsi_DString vStr = {};
            while( JSI_OK==(rc = mdbEvalStep(&sEval)) ) {
                int i, w;
                int nCol;
                
                mdbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
                if (cnt == 0 && nCol>0) {
                    Jsi_DString sStr;
                    wids = (int*)Jsi_Calloc(nCol, sizeof(int));
                    Jsi_DSInit(&sStr);
                    for(i=0; i<nCol; i++) {
                        int j = Jsi_Strlen(apColName[i]);
                        wids[i] = (j<10?10:j);
                        if (width) {
                            Jsi_Value *wv = Jsi_ValueArrayIndex(interp, width, i);
                            if (wv) {
                                Jsi_Number dv;
                                Jsi_ValueGetNumber(interp, wv, &dv);
                                if (dv>0)
                                    wids[i] = (int)dv;
                            }
                        }
                        w = (j<wids[i] ? j : wids[i]);
                        Jsi_DSAppendLen(dStr, apColName[i], w);
                        w = (j<wids[i] ? wids[i]-j+1 : 0);
                        while (w-- > 0)
                            Jsi_DSAppend(dStr, " ", NULL);
                    }
                    for(i=0; i<nCol && opts.headers; i++) {
                        w = wids[i];
                        w -= Jsi_Strlen(apColName[i]);
                        if (i) {
                            Jsi_DSAppend(dStr, opts.separator, NULL);
                            Jsi_DSAppend(&sStr, opts.separator, NULL);
                        }
                        w = wids[i];
                        while (w-- > 0)
                            Jsi_DSAppend(&sStr, "-", NULL);
                    }
                    if (opts.headers)
                        Jsi_DSAppend(dStr, "\n", Jsi_DSValue(&sStr), "\n", NULL);
                    Jsi_DSFree(&sStr);
                }
    
                if (cnt)
                    Jsi_DSAppend(dStr, "\n", NULL);
                for(i=0; i<nCol; i++) {
                    if (i)
                        Jsi_DSAppend(dStr, opts.separator, NULL);
                    Jsi_DSSetLength(&vStr, 0);
                    mdbEvalSetColumn(&sEval, i, &vStr);
                    int nl = Jsi_DSLength(&vStr);
                    if (nl > wids[i]) {
                        Jsi_DSSetLength(&vStr, wids[i]);
                        w = 0;
                    } else {
                        w = wids[i]-nl;
                    }
                    Jsi_DSAppend(dStr, Jsi_DSValue(&vStr), NULL);
                    while (w-- > 0)
                        Jsi_DSAppend(dStr, " ", NULL);
                }
                cnt++;
                if (opts.limit && cnt>=opts.limit) break;
            }
            Jsi_DSFree(&vStr);
            if (wids)
                Jsi_Free(wids);
            break;
        }
        
        case _mdb_EF_INSERT: {
            Jsi_DString vStr = {};    
            while( JSI_OK==(rc = mdbEvalStep(&sEval)) ) {
                int i;
                int nCol;
                const char *tbl = (opts.table ? opts.table : "table");
                if (cnt)
                    Jsi_DSAppend(dStr, "\n", NULL);
                Jsi_DSAppend(dStr, "INSERT INTO ", tbl, " VALUES(", NULL);
                mdbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
                for(i=0; i<nCol; i++) {
                    Jsi_Number dv;
                    const char *azArg;
                    Jsi_DSSetLength(&vStr, 0);
                    mdbEvalSetColumn(&sEval, i, &vStr);
                    
                    MysqlPrep *prep = sEval.prep;
                    Jsi_OptionId ptype = prep->fieldResult[i].jsiTypeMap;
                    
                    azArg = Jsi_DSValue(&vStr);
                    const char *zSep = i>0 ? ",": "";
                    if (azArg[i]==0 && ptype != JSI_OPTION_STRING) {
                      Jsi_DSAppend(dStr, zSep, "NULL", NULL);
                    } else if( ptype ==JSI_OPTION_STRING) {
                      if( zSep[0] ) Jsi_DSAppend(dStr,zSep, NULL);
                      mdbOutputQuotedString(dStr, azArg);
                    } else if (ptype==JSI_OPTION_BOOL || ptype ==JSI_OPTION_DOUBLE) {
                      Jsi_DSAppend(dStr, zSep, azArg, NULL);
                    } else if( Jsi_GetDouble(interp, azArg, &dv) == JSI_OK ) {
                      Jsi_DSAppend(dStr, zSep, azArg, NULL);
                    } else {
                      if( zSep[0] ) Jsi_DSAppend(dStr,zSep, NULL);
                      mdbOutputQuotedString(dStr, azArg);
                    }
                }
                Jsi_DSAppend(dStr, ");", NULL);
                cnt++;
                if (opts.limit && cnt>=opts.limit) break;
            }
            Jsi_DSFree(&vStr);
        }
    
        case _mdb_EF_TABS:
        case _mdb_EF_CSV: {
            Jsi_DString vStr = {};  
            while( JSI_OK==(rc = mdbEvalStep(&sEval)) ) {
                int i;
                int nCol;
                mdbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
                if (cnt == 0 && opts.headers) {
                    for(i=0; i<nCol; i++) {
                        if (i)
                            Jsi_DSAppend(dStr, opts.separator, NULL);
                        Jsi_DSAppend(dStr, apColName[i], NULL);
                    }
                }
    
                if (cnt || opts.headers)
                    Jsi_DSAppend(dStr, "\n", NULL);
                for(i=0; i<nCol; i++) {
                    if (i)
                        Jsi_DSAppend(dStr, opts.separator, NULL);
                    Jsi_DSSetLength(&vStr, 0);
                    mdbEvalSetColumn(&sEval, i, &vStr);
                    if (opts.mode == _mdb_EF_CSV)
                        mdbOutputCsv(&opts, Jsi_DSValue(&vStr), dStr, 0);
                    else
                        Jsi_DSAppend(dStr, Jsi_DSValue(&vStr), NULL);
                }
                cnt++;
                if (opts.limit && cnt>=opts.limit) break;
            }
            Jsi_DSFree(&vStr);
            break;
        }
            
        case _mdb_EF_LINE: {
            int i, w = 5, ww;
            int nCol;
            Jsi_DString vStr = {};   
            while( JSI_OK==(rc = mdbEvalStep(&sEval)) ) {
                mdbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
                if (cnt == 0) {
                    for(i=0; i<nCol; i++) {
                        ww = Jsi_Strlen(apColName[i]);
                        if (ww>w)
                            w = ww;
                    }
                }
    
                for(i=0; i<nCol; i++) {
                    Jsi_DString eStr;
                    Jsi_DSInit(&eStr);
                    Jsi_DSSetLength(&vStr, 0);
                    mdbEvalSetColumn(&sEval, i, &vStr);
                    Jsi_DSPrintf(&eStr, "%*s = %s", w, apColName[i], Jsi_DSValue(&vStr));
                    Jsi_DSAppend(dStr, (cnt?"\n":""), Jsi_DSValue(&eStr), NULL);
                    Jsi_DSFree(&eStr);
                }
                cnt++;
                if (opts.limit && cnt>=opts.limit) break;
            }
            Jsi_DSFree(&vStr);
            break;
        }
            
        case _mdb_EF_HTML: {
            Jsi_DString vStr = {};   
            while( JSI_OK==(rc = mdbEvalStep(&sEval)) ) {
                int i;
                int nCol;
                mdbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
                if (cnt == 0 && opts.headers) {
                    Jsi_DSAppend(dStr, "<TR>", NULL);
                    for(i=0; i<nCol; i++) {
                        Jsi_DSAppend(dStr, "<TH>", NULL);
                        mdbOutputHtmlString(&opts, apColName[i], dStr);
                        Jsi_DSAppend(dStr, "</TH>", NULL);
                    }
                    Jsi_DSAppend(dStr, "</TR>", NULL);
                }
                if (cnt || opts.headers)
                    Jsi_DSAppend(dStr, "\n", NULL);
                Jsi_DSAppend(dStr, "<TR>", NULL);
                for(i=0; i<nCol; i++) {
                    Jsi_DSAppend(dStr, "<TD>", NULL);
                    Jsi_DSSetLength(&vStr, 0);
                    mdbEvalSetColumn(&sEval, i, &vStr);
                    mdbOutputHtmlString(&opts, Jsi_DSValue(&vStr), dStr);
                    Jsi_DSAppend(dStr, "</TD>", NULL);
                }
                Jsi_DSAppend(dStr, "</TR>", NULL);
                cnt++;
                if (opts.limit && cnt>=opts.limit) break;
            }
            Jsi_DSFree(&vStr);
            break;
        }
            
        case _mdb_EF_ROWS:
        {
            Jsi_Value *vcur, *vrow;
            int cnt = 0;
            Jsi_Obj *oall, *ocur;
            Jsi_ValueMakeArrayObject(interp, ret, oall = Jsi_ObjNewType(interp, JSI_OT_ARRAY));
    
            while( JSI_OK==(rc = mdbEvalStep(&sEval)) ) {
                int i;
                int nCol;
                mdbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
                ocur = Jsi_ObjNewType(interp, JSI_OT_OBJECT);
                vrow = Jsi_ValueMakeObject(interp, NULL, ocur);
                for(i=0; i<nCol; i++) {
                    vcur = mdbEvalSetColumnValue(&sEval, i, NULL);
                    Jsi_ObjInsert(interp, ocur, apColName[i], vcur, 0);
                }
                Jsi_ObjArrayAdd(interp, oall, vrow);
                cnt++;
                if (opts.limit && cnt>=opts.limit) break;
            }
            mdbEvalFinalize(&sEval);
            if (rc != JSI_ERROR)
                rc = JSI_OK;
            goto bail;
            break;
        }
        case _mdb_EF_ARRAYS:
        {
            Jsi_Value *vcur, *vrow;
            int cnt = 0;
            Jsi_Obj *oall, *ocur;
            Jsi_ValueMakeArrayObject(interp, ret, oall = Jsi_ObjNewType(interp, JSI_OT_ARRAY));
    
            while( JSI_OK==(rc = mdbEvalStep(&sEval)) ) {
                int i;
                int nCol;
                mdbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
                if (cnt == 0 && opts.headers) {
                    vrow = Jsi_ValueMakeArrayObject(interp, NULL, ocur = Jsi_ObjNewType(interp, JSI_OT_ARRAY));
                    for(i=0; i<nCol; i++) {
                        vcur = Jsi_ValueNewStringDup(interp, apColName[i]);
                        Jsi_ObjArrayAdd(interp, ocur, vcur);
                    }
                    Jsi_ObjArrayAdd(interp, oall, vrow);
                }
                vrow = Jsi_ValueMakeArrayObject(interp, NULL, ocur = Jsi_ObjNewType(interp, JSI_OT_ARRAY));
                for(i=0; i<nCol; i++) {
                    vcur = mdbEvalSetColumnValue(&sEval, i, NULL);
                    Jsi_ObjArrayAdd(interp, ocur, vcur);
                }
                Jsi_ObjArrayAdd(interp, oall, vrow);
                cnt++;
                if (opts.limit && cnt>=opts.limit) break;
            }
            mdbEvalFinalize(&sEval);
            if (rc != JSI_ERROR)
                rc = JSI_OK;
            goto bail;
            break;
        }
        case _mdb_EF_ARRAY1D:
        {
            Jsi_Value *vcur;
            int cnt = 0;
            Jsi_Obj *oall;
            Jsi_ValueMakeArrayObject(interp, ret, oall = Jsi_ObjNewType(interp, JSI_OT_ARRAY));
    
            while( JSI_OK==(rc = mdbEvalStep(&sEval)) ) {
                int i;
                int nCol;
                mdbEvalRowInfo(&sEval, &nCol, &apColName, &apColType);
                if (cnt == 0 && opts.headers) {
                    for(i=0; i<nCol; i++) {
                        vcur = Jsi_ValueNewStringDup(interp, apColName[i]);
                        Jsi_ObjArrayAdd(interp, oall, vcur);
                    }
                }
                for(i=0; i<nCol; i++) {
                    vcur = mdbEvalSetColumnValue(&sEval, i, NULL);
                    Jsi_ObjArrayAdd(interp, oall, vcur);
                }
                cnt++;
                if (opts.limit && cnt>=opts.limit) break;
            }
            mdbEvalFinalize(&sEval);
            if (rc != JSI_ERROR)
                rc = JSI_OK;
            goto bail;
            break;
        }
    }
    if( rc==JSI_BREAK ) {
        rc = JSI_OK;
    }
    Jsi_ValueMakeStringDup(interp, ret, Jsi_DSValue(dStr));
bail:
    mdbEvalFinalize(&sEval);
    if (isopts) {
        Jsi_OptionsFree(interp, QueryFmtOptions, &opts, 0);
    }
    Jsi_DSFree(dStr);
    Jsi_DSFree(&eStr);
    jdb->optPtr = oEopt;

    return rc;
}

Project: jsish

jsi_wsgetPss(jsi_wsCmdObj *cmdPtr, struct lws *wsi, void *user, int create, int ishttp)
{
    Jsi_HashEntry *hPtr;
    bool isNew = 0;
    jsi_wsPss *pss = NULL;
    if (user==NULL)
        return NULL;
    int sfd = lws_get_socket_fd(wsi);
    if (sfd<0) {
        return NULL;
    }
    int sid = ((sfd<<1)|ishttp);
    if (create)
        hPtr = Jsi_HashEntryNew(cmdPtr->pssTable, (void*)(intptr_t)sid, &isNew);
    else
        hPtr = Jsi_HashEntryFind(cmdPtr->pssTable, (void*)(intptr_t)sid);

    if (hPtr && !isNew)
        pss = (typeof(pss))Jsi_HashValueGet(hPtr);

    if (!pss) {
        if (!create)
            return NULL;
        pss = (typeof(pss))Jsi_Calloc(1, sizeof(*pss));
        Jsi_HashValueSet(hPtr, pss);
        pss->sig = JWS_SIG_PWS;
        pss->hPtr = hPtr;
        Jsi_HashValueSet(hPtr, pss);
        pss->cmdPtr = cmdPtr;
        pss->wsi = wsi;
        pss->user = user; /* unused. */
        pss->state = PWS_CONNECTED;
        pss->stats.createTime = time(NULL);
        pss->cnt = cmdPtr->idx++;
        pss->wid = sid;
        //pss->sfd = sfd;
        pss->udata = Jsi_ValueNewObj(cmdPtr->interp, NULL);
        Jsi_IncrRefCount(cmdPtr->interp, pss->udata);

        if (cmdPtr->debug>2)
            fprintf(stderr, "PSS CREATE: %p/%p/%p (http=%d) = %d\n", pss, user, wsi, ishttp, sid);
        if (!ishttp) {
            pss->isWebsock = 1;
            cmdPtr->stats.connectCnt++;
            cmdPtr->createCnt++;
            cmdPtr->createLast = time(NULL);
        }
    }
    if (pss) {
        WSSIGASSERT(pss, PWS);
        pss->stats.eventCnt++;
        pss->stats.eventLast = time(NULL);
        cmdPtr->stats.eventCnt++;
        cmdPtr->stats.eventLast = time(NULL);
    }
    return pss;
}

Project: jsish

Jsi_RC jsiEvalFunction(jsi_Pstate *ps, jsi_OpCode *ip, int discard) {
    Jsi_Interp *interp = ps->interp;
    int stackargc = (int)(uintptr_t)ip->data;
    jsiVarDeref(interp, stackargc + 1);
    int tocall_index = interp->framePtr->Sp - stackargc - 1;
    Jsi_Value *_this = _jsi_THISIDX(tocall_index),
        *tocall = _jsi_STACKIDX(tocall_index), **spargs = _jsi_STACK+(interp->framePtr->Sp - stackargc),   
        *spretPtr = Jsi_ValueNew1(interp), *spretPtrOld = spretPtr,
        *args = Jsi_ValueNewArrayObj(interp, spargs, stackargc, 1);
        
    Jsi_IncrRefCount(interp, args);
    if (_this->vt != JSI_VT_OBJECT)
        _this = Jsi_ValueDup(interp, interp->Top_object);
    else {
        _this = Jsi_ValueDup(interp, _this);
        jsiClearThis(interp, tocall_index);
    }   
    Jsi_RC rc = jsi_FunctionSubCall(interp, args, _this, &spretPtr, tocall, discard);
    
    jsiPop(interp, stackargc);
    jsiClearStack(interp,1);
    if (rc == JSI_CONTINUE) {
        Jsi_ValueMakeUndef(interp, &_jsi_TOP);
        rc = JSI_OK;
    }
    if (spretPtr == spretPtrOld) {
        Jsi_ValueMove(interp, _jsi_TOP, spretPtr);
        Jsi_DecrRefCount(interp, spretPtr);
    } else {
        /*  returning a (non-copied) value reference */
        Jsi_DecrRefCount(interp, _jsi_TOP);
        _jsi_TOP = spretPtr;
    }
    Jsi_DecrRefCount(interp, args);
    return rc;
}

Project: jsish

static void insert_case3(Jsi_TreeEntry* n) {
    Jsi_TreeEntry *g, *u = uncle(n);
    if (node_color(u) == _JSI_TREE_RED) {
        set_color(n->parent, _JSI_TREE_BLACK);
        set_color(u, _JSI_TREE_BLACK);
        g = grandparent(n);
        set_color(g, _JSI_TREE_RED);
        insert_case1(g);
    } else {
        insert_case4(n);
    }
}

Project: jsish

void jsi_PstatePush(jsi_Pstate *ps)
{
    Jsi_Interp *interp = ps->interp;
    if (interp->cur_scope >= (int)(JSI_MAX_SCOPE - 1)) {
        Jsi_LogBug("Scope chain too short");
        return;
    }
    interp->cur_scope++;
}

Project: jsish

const char *jsi_OptionTypeStr(Jsi_OptionId typ, bool cName)
{
    const Jsi_OptionTypedef* ti = Jsi_OptionTypeInfo(typ);
    if (ti)
        return (cName?ti->cName:ti->idName);
    return NULL;
}

Project: jsish

static Jsi_RC jsi_NumberIsFiniteCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr, int op)
{
    Jsi_Number num;
    Jsi_Value *v;
    bool b = 0;
    int skip = 0;
    ChkStringN(_this, funcPtr, v);
    if (Jsi_GetNumberFromValue(interp, v, &num) != JSI_OK)
        return JSI_ERROR;
    switch (op) {
        case 1: b = Jsi_NumberIsFinite(num); break;
        case 2: b = Jsi_NumberIsInteger(num); break;
        case 3: b = Jsi_NumberIsNaN(num); break;
        case 4: b = Jsi_NumberIsSafeInteger(num); break;
    }
    Jsi_ValueMakeBool(interp, ret, b);
    skip++;
    return JSI_OK;
}

Project: jsish

char *Jsi_Realpath(Jsi_Interp *interp, Jsi_Value *src, char *newname)
{
    /* TODO: resolve pwd first. */
    void *data;
    const char *cp = NULL;
    Jsi_Filesystem *fsPtr;
    Jsi_DString dStr;
    Jsi_DSInit(&dStr);
    const char *npath = Jsi_ValueNormalPath(interp, src, &dStr);
    if (!npath) return NULL;
    Jsi_Value *tPtr = Jsi_ValueNew1(interp);
    Jsi_ValueMakeStringDup(interp, &tPtr, npath);
    fsPtr = Jsi_FilesystemForPath(interp, tPtr, &data);
    if (fsPtr) {
        if (fsPtr->realpathProc)
            cp = fsPtr->realpathProc(interp, src, newname);
        else if (!newname)
            cp = Jsi_Strdup(npath);
        else {
            Jsi_Strncpy(newname, npath, PATH_MAX);
            if (Jsi_Strlen(npath)>=PATH_MAX)
                newname[PATH_MAX-1] = 0;
        }
    }
    Jsi_DSFree(&dStr);
    Jsi_DecrRefCount(interp, tPtr);
    return (char*)cp;
}

Project: jsish

Jsi_RC Jsi_RegExpMatch(Jsi_Interp *interp, Jsi_Value *pattern, const char *v, int *rc, Jsi_DString *dStr)
{
    Jsi_Regex *re;
    int regexec_flags = 0;
    if (rc)
        *rc = 0;
    if (pattern == NULL || pattern->vt != JSI_VT_OBJECT || pattern->d.obj->ot != JSI_OT_REGEXP) 
        return Jsi_LogError("expected pattern");
    re = pattern->d.obj->d.robj;
    regex_t *reg = &re->reg;
    
    regmatch_t pos = {};
    if (dStr)
        Jsi_DSInit(dStr);
        
    int r  = regexec(reg, v, 1, &pos, regexec_flags);

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

        regerror(r, reg, buf, sizeof(buf));
        return Jsi_LogError("error while matching pattern: %s", buf);
    }
    if (r != REG_NOMATCH) {
        if (rc) *rc = 1;
        if (dStr && pos.rm_so >= 0 && pos.rm_eo >= 0 &&  pos.rm_eo >= pos.rm_so)
            Jsi_DSAppendLen(dStr, v + pos.rm_so, pos.rm_eo - pos.rm_so);
    }
    
    return JSI_OK;
}

Project: jsish

static Jsi_OpCodes *code_less() { JSI_NEW_CODES(0,OP_LESS, 0); }

Project: jsish

Jsi_RC jsi_InitRegexp(Jsi_Interp *interp, int release)
{
    if (!release)
        interp->RegExp_prototype = Jsi_CommandCreateSpecs(interp, "RegExp", regexpCmds, NULL, JSI_CMDSPEC_ISOBJ);
    return JSI_OK;
}

Project: jsish

static Jsi_OpCodes *code_pop(int n) { JSI_NEW_CODES(0,OP_POP, n); }

Project: jsish

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

Project: jsish

Jsi_RC Jsi_FunctionInvoke(Jsi_Interp *interp, Jsi_Value *func, Jsi_Value *args, Jsi_Value **ret, Jsi_Value *_this)
{
    // Arrange for error reporting to point to called function.
    Jsi_Func *fstatic = func->d.obj->d.fobj->func;
    jsi_OpCode *oldops = interp->curIp;
    if (fstatic->opcodes)
        interp->curIp = fstatic->opcodes->codes;
    Jsi_RC rc = jsi_FunctionInvoke(interp, func, args, ret, _this);
    interp->curIp = oldops;
    if (Jsi_InterpGone(interp))
        return JSI_ERROR;
    return rc;
}

Project: jsish

Jsi_RC Jsi_ValueToObject(Jsi_Interp *interp, Jsi_Value *v)
{
    Jsi_RC rc = JSI_OK;
    if (v->vt == JSI_VT_OBJECT) return rc;
    Jsi_Obj *o = Jsi_ObjNew(interp);
    switch(v->vt) {
        case JSI_VT_UNDEF:
        case JSI_VT_NULL:
            if (interp->strict) {
                Jsi_LogError("converting a undefined/null value to object");
                rc = JSI_ERROR;
            }
            o->d.num = 0;
            o->ot = JSI_OT_NUMBER;
            o->__proto__ = interp->Number_prototype;
            break;
        case JSI_VT_BOOL: {
            o->d.val = v->d.val;
            o->ot = JSI_OT_BOOL;
            o->__proto__ = interp->Boolean_prototype;
            break;
        }
        case JSI_VT_NUMBER: {
            o->d.num = v->d.num;
            o->ot = JSI_OT_NUMBER;
            o->__proto__ = interp->Number_prototype;
            break;
        }
        case JSI_VT_STRING: {
            o->d.s = v->d.s;
            if (!v->f.bits.isstrkey)
                o->d.s.str = (char*)Jsi_KeyAdd(interp, v->d.s.str);
            o->isstrkey = 1;
            o->ot = JSI_OT_STRING;
            o->__proto__ = interp->String_prototype;
            break;
        }
        default:
            Jsi_LogBug("toobject, not suppose to reach here");
    }
    Jsi_ValueReset(interp,&v);
    Jsi_ValueMakeObject(interp, &v, o);
    return rc;
}

Project: jsish

static int jsi_FSCloseProc(Jsi_Channel chan) { return fclose(_JSI_GETFP(chan,1));}

Project: jsish

static jsi_CaseList *caselist_insert(jsi_Pstate *pstate, jsi_CaseList *a, jsi_CaseExprStat *es)
{
    jsi_CaseList *b = (jsi_CaseList*)Jsi_Calloc(1,sizeof(*b));
    a->sig = JSI_SIG_CASELIST;
    b->es = es;
    a->tail->next = b;
    a->tail = b;
    return a;
}

Project: jsish

static void dbProfileHandler(void *cd, const char *zSql, sqlite_uint64 tm) {
    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, zSql);
    items[2] = Jsi_ValueMakeNumber(interp, NULL, (Jsi_Number)tm);
    vpargs = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, items, 3, 0));
    Jsi_IncrRefCount(interp, vpargs);
    ret = Jsi_ValueNew1(interp);
    rc = Jsi_FunctionInvoke(interp, jdb->onProfile, vpargs, &ret, NULL);
    Jsi_DecrRefCount(interp, vpargs);
    Jsi_DecrRefCount(interp, ret);
    if (rc != JSI_OK)
        jdb->errCnt++;
}

Project: jsish

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

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

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

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

Project: jsish

const char* jsi_GetDirective(Jsi_Interp *interp, Jsi_OpCodes *ops, const char *str) {
    if (!ops) return NULL;
    if (!ops->code_len) return NULL;
    if (ops->codes[0].op != OP_PUSHSTR || !ops->codes[0].data) return NULL;
    if (Jsi_Strncmp((char*)ops->codes[0].data, str, Jsi_Strlen(str))) return NULL;
    return (char*)ops->codes[0].data;
}

Project: jsish

Jsi_Value* Jsi_ValueMakeObject(Jsi_Interp *interp, Jsi_Value **vPtr, Jsi_Obj *o)  {
    Jsi_Value *v = (vPtr?*vPtr:NULL);
    if (v && v->vt == JSI_VT_OBJECT && o == v->d.obj)
        return v;
    if (v)
        Jsi_ValueReset(interp, vPtr);
    else
        v = Jsi_ValueNew(interp);
    //Jsi_IncrRefCount(interp, v);
    if (!o)
        o = Jsi_ObjNewType(interp, JSI_OT_OBJECT);
    v->vt = JSI_VT_OBJECT;
    v->d.obj = o;
    Jsi_ObjIncrRefCount(interp,v->d.obj);
    return v;
}

Project: jsish

static Jsi_TreeEntry *StringPtrCreate( Jsi_Tree *treePtr, const void *key, bool *newPtr)
{
    return OneWordCreate(treePtr, key, newPtr);
}

Project: jsish

Jsi_Number Jsi_ValueToNumberInt(Jsi_Interp *interp, Jsi_Value *v, int isInt)
{
    char *endPtr = NULL, *sptr;
    Jsi_Number a = 0;
    switch(v->vt) {
        case JSI_VT_BOOL:
            a = (Jsi_Number)(v->d.val ? 1.0: 0);
            break;
        case JSI_VT_NULL:
            a = 0;
            break;
        case JSI_VT_OBJECT: {
            Jsi_Obj *obj = v->d.obj;
            switch(obj->ot) {
                case JSI_OT_BOOL:
                    a = (Jsi_Number)(obj->d.val ? 1.0: 0);
                    break;
                case JSI_OT_NUMBER:
                    a = obj->d.num;
                    break;
                case JSI_OT_STRING:
                    sptr = obj->d.s.str;
                    goto donum;
                    break;
                default:
                    a = 0;
                break;
            }
            break;
        }
        case JSI_VT_UNDEF:
            a = Jsi_NumberNaN();
            break;
        case JSI_VT_NUMBER:
            a = v->d.num;
            break;
        case JSI_VT_STRING:
            sptr = v->d.s.str;
donum:
            if (!isInt) {
                a = strtod(sptr, &endPtr);
                if (endPtr && *endPtr) {
                    a = interp->NaNValue->d.num;
                }
            } else {
                a = (Jsi_Number)strtol(sptr, &endPtr, 0);
                if (!isdigit(*sptr))
                    a = interp->NaNValue->d.num;
            }
            break;
        default:
            Jsi_LogBug("Convert a unknown type: 0x%x to number", v->vt);
            break;
    }
    if (isInt && Jsi_NumberIsNormal(a))
        a = (Jsi_Number)((int64_t)(a));
    return a;
}

Project: jsish

static SqlFunc *dbFindSqlFunc(Jsi_Db *jdb, const char *zName) {
    SqlFunc *p, *pNew;
    int i;
    pNew = (SqlFunc*)Jsi_Calloc(1, sizeof(*pNew) + Jsi_Strlen(zName) + 1 );
    pNew->sig = SQLITE_SIG_FUNC;
    pNew->zName = (char*)&pNew[1];
    for(i=0; zName[i]; i++) {
        pNew->zName[i] = tolower(zName[i]);
    }
    pNew->zName[i] = 0;
    for(p=jdb->pFunc; p; p=p->pNext) {
        if( Jsi_Strcmp(p->zName, pNew->zName)==0 ) {
            Jsi_Free((char*)pNew);
            return p;
        }
    }
    pNew->interp = jdb->interp;
    pNew->pScript = 0;
    Jsi_DSInit(&pNew->dScript);
    pNew->pNext = jdb->pFunc;
    jdb->pFunc = pNew;
    return pNew;
}

Project: jsish

Jsi_Value* Jsi_ValueMakeArrayObject(Jsi_Interp *interp, Jsi_Value **vPtr, Jsi_Obj *o)  {
    Jsi_Value *v = (vPtr?*vPtr:NULL);
    if (!o)
        o = Jsi_ObjNewType(interp, JSI_OT_ARRAY);
    if (!v)
        v = Jsi_ValueNew(interp);
    else {
       if (v->vt == JSI_VT_OBJECT && o == v->d.obj) {
            if (!o->isarrlist) {
                if (o->tree)
                    Jsi_TreeDelete( o->tree);
                o->tree = NULL;
                o->__proto__ = interp->Array_prototype;
                o->isarrlist = 1;
            }
            return v;
        }
        Jsi_ValueReset(interp, vPtr);
    }
    v->vt = JSI_VT_OBJECT;
    v->d.obj = o;
    o->ot = JSI_OT_OBJECT;
    o->__proto__ = interp->Array_prototype;
    o->isarrlist = 1;
    Jsi_ObjArraySizer(interp, o, 0);
    Jsi_ObjIncrRefCount(interp,v->d.obj);
    return v;
}

Project: jsish

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

Project: jsish

static Jsi_RC WebSocketConstructor(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    if (Jsi_InterpAccess(interp, NULL, JSI_INTACCESS_NETWORK ) != JSI_OK)
        return Jsi_LogError("WebSocket disallowed by Interp.noNetwork option");
    jsi_wsCmdObj *cmdPtr;
    Jsi_Value *toacc = NULL;
    Jsi_Value *arg = Jsi_ValueArrayIndex(interp, args, 0);

    cmdPtr = (jsi_wsCmdObj*)Jsi_Calloc(1, sizeof(*cmdPtr));
    cmdPtr->sig = JWS_SIG_OBJ;
    cmdPtr->_ = &wsObjCmd;
    cmdPtr->_->newCnt++;
    cmdPtr->_->activeCnt++;
    cmdPtr->port = 8080;
    cmdPtr->formParams = jsi_wsparam_str;
    cmdPtr->maxUpload = 100000;
    cmdPtr->interp = interp;
    cmdPtr->ietf_version = -1;
    cmdPtr->bufferPwr2 = 0;
    cmdPtr->ws_gid = -1;
    cmdPtr->ws_uid = -1;
    cmdPtr->startTime = time(NULL);
    cmdPtr->hasOpts = 1;
    cmdPtr->includeFile = "include.shtml";
    cmdPtr->jsiFnPattern = "jsig*.js";
    if ((arg != NULL && !Jsi_ValueIsNull(interp,arg))
        && Jsi_OptionsProcess(interp, WSOptions, cmdPtr, arg, 0) < 0) {
bail:
        jsi_wswebsocketObjFree(interp, cmdPtr);
        return JSI_ERROR;
    }
    if (!cmdPtr->udata) {
        cmdPtr->udata = Jsi_ValueNewObj(interp, NULL);
        Jsi_IncrRefCount(interp, cmdPtr->udata);
    }
    Jsi_PathNormalize(interp, &cmdPtr->rootdir);

    if (cmdPtr->headers && (Jsi_ValueGetLength(interp, cmdPtr->headers)%2)) {
        Jsi_LogError("Odd header length");
        goto bail;
    }
    const char *up = cmdPtr->urlPrefix, *ur = cmdPtr->urlRedirect;
    if (up && ur && Jsi_Strncmp(ur, up, Jsi_Strlen(up))) {
        Jsi_LogError("urlRedirect does not start with urlPrefix");
        goto bail;
    }
    const char* subprot = (cmdPtr->protocol&&cmdPtr->protocol[0]?cmdPtr->protocol:"ws");
    if (cmdPtr->protocol && !cmdPtr->protocol[0])
        Jsi_LogWarn("empty protocol string: forcing to 'ws'");
    cmdPtr->protocols[JWS_PROTOCOL_HTTP].name="http-only";
    cmdPtr->protocols[JWS_PROTOCOL_HTTP].callback=jsi_wscallback_http;
    cmdPtr->protocols[JWS_PROTOCOL_HTTP].per_session_data_size=sizeof(jsi_wsUser);
    cmdPtr->protocols[JWS_PROTOCOL_WEBSOCK].name=subprot;
    cmdPtr->protocols[JWS_PROTOCOL_WEBSOCK].callback=jsi_wscallback_websock;
    cmdPtr->protocols[JWS_PROTOCOL_WEBSOCK].per_session_data_size=sizeof(jsi_wsUser);

    if (cmdPtr->bufferPwr2 == 0)
        cmdPtr->bufferPwr2 = 16;
    if (cmdPtr->bufferPwr2>0) {
        if (cmdPtr->bufferPwr2>20) {
            Jsi_LogError("bufferPwr2 not in 0-20: %d", cmdPtr->bufferPwr2);
            goto bail;
        }
        cmdPtr->protocols[JWS_PROTOCOL_WEBSOCK].rx_buffer_size=(1<<cmdPtr->bufferPwr2);
    }

    cmdPtr->pssTable = Jsi_HashNew(interp, JSI_KEYS_ONEWORD, jsi_wsfreePss);
    if (cmdPtr->onModify)
        cmdPtr->fileHash = Jsi_HashNew(interp, JSI_KEYS_STRING, jsi_wsfreeFile);

    cmdPtr->info.port = (cmdPtr->client ? CONTEXT_PORT_NO_LISTEN : cmdPtr->port);
    cmdPtr->info.user = cmdPtr;
    cmdPtr->info.iface = cmdPtr->interface ? Jsi_ValueString(interp, cmdPtr->interface, NULL) : NULL;
    if (cmdPtr->local && !cmdPtr->info.iface)
        cmdPtr->info.iface = "lo";
#ifdef __WIN32
    cmdPtr->info.iface = NULL;
#endif
    cmdPtr->info.protocols = cmdPtr->protocols;
    if (!cmdPtr->noCompress)
        cmdPtr->info.extensions = jsi_lws_exts;

    cmdPtr->info.ssl_cert_filepath = cmdPtr->ssl_cert_filepath;
    cmdPtr->info.ssl_private_key_filepath = cmdPtr->ssl_private_key_filepath;
    cmdPtr->info.gid = cmdPtr->ws_gid;
    cmdPtr->info.uid = cmdPtr->ws_uid;
    cmdPtr->opts = LWS_SERVER_OPTION_SKIP_SERVER_CANONICAL_NAME|LWS_SERVER_OPTION_VALIDATE_UTF8;
    cmdPtr->info.options = cmdPtr->opts;
    cmdPtr->info.max_http_header_pool = 16;
    cmdPtr->info.timeout_secs = 5;
    cmdPtr->info.ssl_cipher_list = "ECDHE-ECDSA-AES256-GCM-SHA384:"
                   "ECDHE-RSA-AES256-GCM-SHA384:"
                   "DHE-RSA-AES256-GCM-SHA384:"
                   "ECDHE-RSA-AES256-SHA384:"
                   "HIGH:!aNULL:!eNULL:!EXPORT:"
                   "!DES:!MD5:!PSK:!RC4:!HMAC_SHA1:"
                   "!SHA1:!DHE-RSA-AES128-GCM-SHA256:"
                   "!DHE-RSA-AES128-SHA256:"
                   "!AES128-GCM-SHA256:"
                   "!AES128-SHA256:"
                   "!DHE-RSA-AES256-SHA256:"
                   "!AES256-GCM-SHA384:"
                   "!AES256-SHA256";

    lws_set_log_level(cmdPtr->debug>255?cmdPtr->debug/256:0, NULL);
    // TODO: WS2.2 Still leaks a small amount if server port unavailable.
    if (!cmdPtr->client)
        cmdPtr->info.options |= LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
    cmdPtr->context = lws_create_context(&cmdPtr->info);
    if (cmdPtr->context == NULL) {
fail:
        Jsi_LogError("libwebsocket init failed on port %d (try another port?)", cmdPtr->info.port);
        goto bail;
    }
    if (cmdPtr->info.options & LWS_SERVER_OPTION_EXPLICIT_VHOSTS) {
        cmdPtr->info.options &= ~LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
        if (!lws_create_vhost(cmdPtr->context, &cmdPtr->info))
            goto fail;
    }

    if (cmdPtr->client) {
        struct lws_client_connect_info lci = {};
        lci.context = cmdPtr->context;
        lci.address = cmdPtr->address ? Jsi_ValueString(cmdPtr->interp, cmdPtr->address, NULL) : "127.0.0.1";
        lci.port = cmdPtr->port;
        lci.ssl_connection = cmdPtr->use_ssl;
        lci.path = Jsi_ValueString(cmdPtr->interp, cmdPtr->rootdir, NULL);
        lci.host = cmdPtr->clientHost?cmdPtr->clientHost:lws_canonical_hostname( cmdPtr->context );
        lci.origin = cmdPtr->clientOrigin?cmdPtr->clientOrigin:"origin";
        lci.protocol = cmdPtr->protocols[JWS_PROTOCOL_WEBSOCK].name;
        lci.ietf_version_or_minus_one = cmdPtr->ietf_version;
#if (LWS_LIBRARY_VERSION_MAJOR>1)
        if (cmdPtr->post)
            lci.method = "POST";
        else if (!Jsi_Strcmp(subprot, "get"))
            lci.method = "GET";
#endif

        if (NULL == lws_client_connect_via_info(&lci))
        {
            Jsi_LogError("websock connect failed");
            jsi_wswebsocketObjFree(interp, cmdPtr);
            return JSI_ERROR;
        }
    } else if (cmdPtr->port == 0) {
        // Extract actually used port.
        char *cp, cbuf[JSI_BUFSIZ*2];
        cbuf[0] = 0;
        lws_json_dump_context(cmdPtr->context, cbuf, sizeof(cbuf), 0);
        cp = Jsi_Strstr(cbuf, "\"port\":\"");
        if (cp)
            cmdPtr->port = atoi(cp+8);
    }

    cmdPtr->event = Jsi_EventNew(interp, jsi_wswebsockUpdate, cmdPtr);
    if (Jsi_FunctionIsConstructor(funcPtr)) {
        toacc = _this;
    } else {
        Jsi_Obj *o = Jsi_ObjNew(interp);
        Jsi_PrototypeObjSet(interp, "WebSocket", o);
        Jsi_ValueMakeObject(interp, ret, o);
        toacc = *ret;
    }

    Jsi_Obj *fobj = Jsi_ValueGetObj(interp, toacc);
    if ((cmdPtr->objId = Jsi_UserObjNew(interp, &websockobject, fobj, cmdPtr))<0) {
        goto bail;
    }
    Jsi_UserObjName(interp, toacc, &cmdPtr->cName);

    cmdPtr->handlers = Jsi_HashNew(interp, JSI_KEYS_STRING, jsi_wsfreeHandlers);
    if (cmdPtr->extHandlers) {
        jsi_wsHandlerAdd(interp, cmdPtr, ".jsi",   "Jspp",     1);
        jsi_wsHandlerAdd(interp, cmdPtr, ".htmli", "Htmlpp",   1);
        jsi_wsHandlerAdd(interp, cmdPtr, ".cssi",  "Csspp",    1);
    }
    cmdPtr->fobj = fobj;
#ifdef LWS_LIBRARY_VERSION_NUMBER
    Jsi_JSONParseFmt(interp, &cmdPtr->version, "{libVer:\"%s\", hdrVer:\"%s\", hdrNum:%d, pkgVer:%d}",
        (char *)lws_get_library_version(), LWS_LIBRARY_VERSION, LWS_LIBRARY_VERSION_NUMBER, jsi_WsPkgVersion);
#endif
    return JSI_OK;
}

Project: jsish

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

Project: jsish

static Jsi_RC jsi_logic_less(Jsi_Interp* interp, int i1, int i2) {
    Jsi_Value *v, *v1 = _jsi_STACK[interp->framePtr->Sp-i1], *v2 = _jsi_STACK[interp->framePtr->Sp-i2], *res = _jsi_TOQ;
    int val = 0, l1 = 0, l2 = 0; 
    bool strict = interp->strict;
    Jsi_RC rc = JSI_OK;
    rc = _jsi_StrictUChk2(v1, v2);
    if (rc != JSI_OK)
        return JSI_ERROR;
    char *s1 = Jsi_ValueString(interp, v1, &l1);
    char *s2 = Jsi_ValueString(interp, v2, &l2);
    Jsi_Number n1, n2;

    if (s1 || s2) {
        char *str;
        if (!(s1 && s2)) {
            v = (s1 ? v2 : v1);
            jsi_ValueToPrimitive(interp, &v);
            Jsi_ValueToString(interp, v, NULL);
            str = Jsi_ValueString(interp, v, (s1?&l2:&l1));
            if (s1) s2 = str; else s1 = str;
        }
        Assert(l1>=0 && l1<=JSI_MAX_ALLOC_BUF);
        Assert(l2>=0 && l2<=JSI_MAX_ALLOC_BUF);
        //int mlen = (l1>l2?l1:l2);
        val = Jsi_Strcmp(s1, s2);
  
        if (val > 0) val = 0;
        else if (val < 0) val = 1;
        else val = (l1 < l2);
        jsiClearStack(interp,2);
        Jsi_ValueMakeBool(interp, &res, val);
    } else {
        Jsi_ValueToNumber(interp, v1);
        Jsi_ValueToNumber(interp, v2);
        rc = _jsi_StrictChk2(v1,v2);
        if (rc != JSI_OK)
            return JSI_ERROR;
        n1 = v1->d.num; n2 = v2->d.num;
        if (Jsi_NumberIsNaN(n1) || Jsi_NumberIsNaN(n2)) {
            jsiClearStack(interp,2);
            Jsi_ValueMakeUndef(interp, &res);
        } else {
            val = (n1 < n2);
            jsiClearStack(interp,2);
            Jsi_ValueMakeBool(interp, &res, val);
        }
    }
    return JSI_OK;
}

Project: jsish

static void mdbJsToMyTime(Jsi_Number time, MYSQL_TIME* mtm, int utc)
{
    struct tm tm;
    time_t tim = (time_t)(time/1000);
    if (utc)
        gmtime_r(&tim, &tm);
    else
        localtime_r(&tim, &tm);
    mtm->second = tm.tm_sec;
    mtm->minute = tm.tm_min;
    mtm->hour = tm.tm_hour;
    mtm->day = tm.tm_mday;
    mtm->month = tm.tm_mon+1;
    mtm->year = tm.tm_year + 1900;
    Jsi_Number secs = (tim/1000.0);
    mtm->second_part = (int)((secs-(int)secs)*1000000);
}

Project: jsish

static Jsi_RC FilesysPutsCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
   
    UdfGet(udf, _this, funcPtr);
    if (!udf->filename) {
        Jsi_ValueMakeBool(interp, ret, 0);
        return JSI_OK;
    }
    Jsi_Value *toput = Jsi_ValueArrayIndex(interp, args, 0);
    if (!toput) {
        Jsi_ValueMakeBool(interp, ret, 0);
        return JSI_OK;
    }
    const char * cp = Jsi_ValueToString(interp, toput, NULL);

    if (Jsi_Printf(interp, udf->chan, "%s\n", cp?cp:"") < 0) {
        Jsi_ValueMakeBool(interp, ret, 0);
        return JSI_OK;
    }
    Jsi_ValueMakeBool(interp, ret, 1);
    return JSI_OK;
}

Project: jsish

static Jsi_RC WebSocketConfCmd(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");
    Jsi_Value *opts = Jsi_ValueArrayIndex(interp, args, 0);
    if (cmdPtr->noConfig && opts && !Jsi_ValueIsString(interp, opts))
        return Jsi_LogError("WebSocket conf() is disabled for set");
    return Jsi_OptionsConf(interp, WSOptions, cmdPtr, opts, ret, 0);

}

Project: jsish

static Jsi_OptionSpec * jsi_GetCachedOptionSpecs(Jsi_Interp *interp, const Jsi_OptionSpec *staticSpecs)
{
    return (Jsi_OptionSpec *)staticSpecs;
}

Project: jsish

static Jsi_RC jsi_FunctionInvoke(Jsi_Interp *interp, Jsi_Value *tocall, Jsi_Value *args, Jsi_Value **ret, Jsi_Value *_this)
{
    if (interp->maxDepth>0 && interp->maxDepth && interp->callDepth>=interp->maxDepth)
        return Jsi_LogError("max call depth exceeded");
    if (interp->deleting)
        return JSI_ERROR;
    if (!Jsi_ValueIsFunction(interp, tocall)) 
        return Jsi_LogError("can not execute expression, expression is not a function");
    if (!tocall->d.obj->d.fobj) {   /* empty function */
        return JSI_OK;
    }
    if (!ret) {
        if (!interp->nullFuncRet) {
            interp->nullFuncRet = Jsi_ValueNew(interp);
            Jsi_IncrRefCount(interp, interp->nullFuncRet);
        }
        ret = &interp->nullFuncRet;
        Jsi_ValueMakeUndef(interp, ret);
    }
    if (!args) {
        if (!interp->nullFuncArg) {
            interp->nullFuncArg = Jsi_ValueMakeObject(interp, NULL, Jsi_ObjNewArray(interp, NULL, 0, 0));
            Jsi_IncrRefCount(interp, interp->nullFuncArg);
        }
        args = interp->nullFuncArg;
    }
    /* func to call */
    Jsi_Func *funcPtr = tocall->d.obj->d.fobj->func;
    SIGASSERT(funcPtr, FUNC);
    
    /* prepare args */
    if (args->vt != JSI_VT_OBJECT || !Jsi_ObjIsArray(interp, args->d.obj)) 
        return Jsi_LogError("argument must be an array");
    /* new this */
    Jsi_Value *fthis = Jsi_ValueDup(interp, _this ? _this : tocall);
    Jsi_Func *prevActive = interp->activeFunc;
    Jsi_RC res = jsi_SharedArgs(interp, args, funcPtr, 1);
    bool isalloc = 0;
    int calltrc = 0;
    int tc = interp->traceCall | (funcPtr->pkg?funcPtr->pkg->popts.modConf.traceCall:0);
    interp->callDepth++;
    if (res == JSI_OK) {
        jsi_InitLocalVar(interp, args, funcPtr);
        jsi_SetCallee(interp, args, tocall);
        isalloc = 1;
        Jsi_IncrRefCount(interp, args);
        if (funcPtr->type == FC_NORMAL) {
            if ((tc&jsi_callTraceFuncs) && funcPtr->name)
                calltrc = 1;
        } else {
            if ((tc&jsi_callTraceCmds) && funcPtr->name)
                calltrc = 1;
        }
        interp->activeFunc = funcPtr;
        if (funcPtr->type == FC_NORMAL) {
            if (calltrc)
                jsi_TraceFuncCall(interp, funcPtr, NULL, fthis, args, NULL, tc);
            res = jsi_evalcode(interp->ps, funcPtr, funcPtr->opcodes, tocall->d.obj->d.fobj->scope, 
                args, fthis, ret);
        } else {
            if (calltrc)
                jsi_TraceFuncCall(interp, funcPtr, NULL, fthis, args, NULL, tc);
            res = funcPtr->callback(interp, args, fthis, ret, funcPtr);
        }
        funcPtr->callCnt++;
    }
    interp->callDepth--;
    if (res == JSI_OK && funcPtr->retType)
        res = jsi_ArgTypeCheck(interp, funcPtr->retType, *ret, "returned from", funcPtr->name, 0, funcPtr, 0);
    if (calltrc && (tc&jsi_callTraceReturn))
        jsi_TraceFuncCall(interp, funcPtr, NULL, fthis, NULL, *ret, tc);
    interp->activeFunc = prevActive;
    jsi_SharedArgs(interp, args, funcPtr, 0);
    if (isalloc) 
        Jsi_DecrRefCount(interp, args);
    Jsi_DecrRefCount(interp, fthis);
    return res;
}

Project: jsish

static void searchSpace(Jsi_TreeSearch *searchPtr, int n)
{
    if ((searchPtr->top+n) >= searchPtr->max) {
        int i, cnt = (searchPtr->max *= 2);
        if (searchPtr->Ptrs == searchPtr->staticPtrs)
            searchPtr->Ptrs = (Jsi_TreeEntry**)Jsi_Calloc(cnt, sizeof(Jsi_TreeEntry*));
        else
            searchPtr->Ptrs = (Jsi_TreeEntry**)Jsi_Realloc(searchPtr->Ptrs, cnt* sizeof(Jsi_TreeEntry*));
        for (i=0; i<cnt; i++)
            SIGINIT((searchPtr->Ptrs[i]),TREEENTRY);

    }
}

Project: jsish

int Jsi_MapEntryDelete (Jsi_MapEntry *entryPtr){
    switch (jsi_GetListType(entryPtr)) {
        case JSI_MAP_HASH: return Jsi_HashEntryDelete((Jsi_HashEntry*)entryPtr);
        case JSI_MAP_TREE: return Jsi_TreeEntryDelete((Jsi_TreeEntry*)entryPtr);
        case JSI_MAP_LIST: {
            Jsi_ListEntry *lptr = (Jsi_ListEntry*)entryPtr;
            Jsi_ListPop(lptr->list, lptr);
            Jsi_ListEntryDelete(lptr);
            return 1;
        }
    }
    return JSI_OK;
}

Project: jsish

static void jsiClearThis(Jsi_Interp *interp, int ofs) {
    Jsi_Value **vPtr = &_jsi_THISIDX(ofs), *v = *vPtr;
    if (!v) return;
#ifndef XX_NEWSTACK
    Jsi_ValueReset(interp, vPtr);
#else
    if (v->refCnt<=1)
        Jsi_ValueReset(interp, vPtr);
    else {
        Jsi_DecrRefCount(interp, v);
        _jsi_THISIDX(ofs) = Jsi_ValueNew1(interp);
    }
#endif
}

Project: jsish

Jsi_RC Jsi_ThisDataSet(Jsi_Interp *interp, Jsi_Value *_this, void *value)
{
    bool isNew;
    Jsi_HashEntry *hPtr = Jsi_HashEntryNew(interp->thisTbl, _this, &isNew);
    if (!hPtr)
        return JSI_ERROR;
    Jsi_HashValueSet(hPtr, value);
    return JSI_OK;
}

Project: jsish

void* Jsi_CurrentThread(void) { return NULL; }

Project: jsish

static Jsi_RC ValueToCArray(Jsi_Interp *interp, Jsi_OptionSpec* spec, Jsi_Value *inValue, const char *inStr, void *record, Jsi_Wide flags)
{
    if (inStr)
        return JSI_ERROR;
    uchar *s = (uchar*)((char*)record) + spec->offset;
    Jsi_OptionSpec *subSpec = spec->init.OPT_CARRAY;
    int argc, i, isize, size = spec->arrSize;
    if (!subSpec || size<=0 || (isize=subSpec->size)<=0)
        goto bail;
    isize = isize/size;
    if (!Jsi_ValueIsArray(interp, inValue)) 
        return Jsi_LogError("expected array");
    argc = Jsi_ValueGetLength(interp, inValue);
    if (argc != size) 
        return Jsi_LogError("array length %d was not %d", argc, size);
    for (i = 0; i<size; i++) {
        Jsi_Value *v = Jsi_ValueArrayIndex(interp, inValue, i);
        if (Jsi_OptionsSet(interp, subSpec, (void*)s, subSpec->name, v, 0) != JSI_OK)
            return JSI_ERROR;
        s += isize;
    }
    return JSI_OK;
bail:
    return Jsi_LogError("bad config");
}

Project: jsish

void jsi_ScopeStrsFree(Jsi_Interp *interp, Jsi_ScopeStrs *ss)
{
    if (!ss) return;
    int i;
    for (i=0; i<ss->count; i++)
        if (ss->args[i].defValue)
            Jsi_DecrRefCount(interp, ss->args[i].defValue);
    if (ss->args)
        Jsi_Free(ss->args);
    Jsi_Free(ss);
}

Project: jsish

static void MutexUnlock(Jsi_Mutex* mtx) { LeaveCriticalSection(&mtx->mtx); }

Project: jsish

Jsi_OptionsFind(Jsi_Interp *interp, Jsi_OptionSpec *specs, const char *name, Jsi_Wide flags)
{
    Jsi_OptionSpec *specPtr;
    char c;      /* First character of current argument. */
    Jsi_OptionSpec *matchPtr; /* Matching spec, or NULL. */
    size_t length;
    const char *matStr = NULL;
    
    if (name == NULL) {
        Jsi_LogError("Null name for option");
        return NULL;
    }
    c = name[0];
    length = Jsi_Strlen(name);
    matchPtr = NULL;
    for (specPtr = specs; specPtr->id>=JSI_OPTION_BOOL && specPtr->id < JSI_OPTION_END && specPtr->name; specPtr++) {

        if ((specPtr->name[0] != c)
                || (Jsi_Strncmp(specPtr->name, name, length) != 0)) {
            continue;
        }
        if (specPtr->name[length] == 0) {
            return specPtr;   /* Stop on a perfect match. */
        }
        if (matchPtr != NULL) {
            Jsi_LogError("ambiguous option \"%s\" matches both \"%s\" and \"%s\"", name, matStr, specPtr->name);
            return (Jsi_OptionSpec *) NULL;
        }
        matchPtr = specPtr;
        matStr = specPtr->name;
    }

    if (matchPtr == NULL) {
        Jsi_DString dStr = {};
        Jsi_DSAppend(&dStr, "unknown option \"", name, "\" not one of: ", NULL);
    
        for (specPtr = specs; specPtr->id>=JSI_OPTION_BOOL && specPtr->id < JSI_OPTION_END && specPtr->name; specPtr++) {
            if (specPtr->name == NULL) {
                break;
            }
            if (name[0] != '?' || _JSICASTINT(specPtr->id) < 0 || specPtr->id >= JSI_OPTION_END) {
                Jsi_DSAppend(&dStr, specPtr->name, " ", NULL);
            } else {

                Jsi_DSAppend(&dStr, "?", specPtr->name, " <", jsi_OptTypeInfo[specPtr->id].idName, ">? ", NULL);
            }
        }
        assert(specPtr->id == JSI_OPTION_END);
        Jsi_LogError("%s", Jsi_DSValue(&dStr));
        Jsi_DSFree(&dStr);
        return (Jsi_OptionSpec *) NULL;
    }
    return matchPtr;
}

Project: jsish

static Jsi_TreeEntry *searchAdd(Jsi_TreeSearch *searchPtr,  Jsi_TreeEntry *hPtr)
{
    int order = (searchPtr->flags & JSI_TREE_ORDER_MASK);
    searchSpace(searchPtr, 2);
    switch (order) {
        case JSI_TREE_ORDER_LEVEL:
            if (hPtr) {
                if (hPtr->right)
                    searchPtr->Ptrs[searchPtr->top++] = hPtr->right;
                if (hPtr->left)
                    searchPtr->Ptrs[searchPtr->top++] = hPtr->left;
                return hPtr;
            }
            if (searchPtr->top<=0)
                return NULL;
            hPtr = searchPtr->Ptrs[0];
            searchPtr->top--;
            if (searchPtr->top > 0) {
                /* Not very efficient way to implement a queue, but works for now. */
                memmove(searchPtr->Ptrs, searchPtr->Ptrs+1, sizeof(Jsi_TreeEntry*)*searchPtr->top);
            }
            if (hPtr->right)
                searchPtr->Ptrs[searchPtr->top++] = hPtr->right;
            if (hPtr->left)
                searchPtr->Ptrs[searchPtr->top++] = hPtr->left;
            return hPtr;
            break;
            
        case JSI_TREE_ORDER_POST:
            if (hPtr)
                searchPtr->Ptrs[searchPtr->top++] = searchPtr->current = hPtr;
            while (searchPtr->top>0) {
                hPtr = searchPtr->Ptrs[searchPtr->top-1];
                if (hPtr->right == searchPtr->current || hPtr->left == searchPtr->current ||
                    (hPtr->left == NULL && hPtr->right == NULL)) {
                    searchPtr->top--;
                    searchPtr->current = hPtr;
                    return hPtr;
                } else {
                    searchSpace(searchPtr, 2);
                    if (hPtr->left)
                        searchPtr->Ptrs[searchPtr->top++] = hPtr->left;
                    if (hPtr->right)
                        searchPtr->Ptrs[searchPtr->top++] = hPtr->right;
                }
            }
            return NULL;
            break;
            
        case JSI_TREE_ORDER_PRE:
            if (!hPtr) {
                if (searchPtr->top<=0) return NULL;
                hPtr = searchPtr->Ptrs[--searchPtr->top];
            }
            searchPtr->Ptrs[searchPtr->top++] = hPtr;
            if (hPtr->left) searchPtr->Ptrs[searchPtr->top++] = hPtr->left;
            if (hPtr->right) searchPtr->Ptrs[searchPtr->top++] = hPtr->right;
            break;
            
        case JSI_TREE_ORDER_IN:
            while (1) {
                searchSpace(searchPtr, 2);
                if (searchPtr->current) {
                    searchPtr->Ptrs[searchPtr->top++] = searchPtr->current;
                    searchPtr->current = searchPtr->current->right;
                } else {
                    if (searchPtr->top<=0)
                        return NULL;
                    hPtr = searchPtr->Ptrs[--searchPtr->top] ;
                    searchPtr->current = hPtr->left;
                    return hPtr;
                }
            }
            break;
            
        default:
            if (hPtr) {
                Jsi_Interp *interp = hPtr->treePtr->opts.interp;
                JSI_NOTUSED(interp);
                Jsi_LogError("Invalid order: %d", order);    
            }    
    }
    return searchPtr->Ptrs[--searchPtr->top];
}

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_Wide wcount = 0;
    bool bn;
    Jsi_Number nv;
    bool isIncr = (flags & JSI_OPTS_INCR);
    const char *expType = NULL;
    char *record = (char*)rec, *ptr = record + specPtr->offset;
    Jsi_OptionCustom* cust = NULL;
    const char *emsg = NULL, *epre = "";

    if (specPtr->id<JSI_OPTION_BOOL || specPtr->id>=JSI_OPTION_END) 
        return Jsi_LogBug("unknown option id \"%d\" for \"%s\"", specPtr->id, specPtr->name);
    if (specPtr->custom  && specPtr->id == JSI_OPTION_CUSTOM) {
        cust = Jsi_OptionCustomBuiltin(specPtr->custom);
        if (cust && cust->parseProc) {
            int lastErrCnt = interp->logErrorCnt;
            Jsi_OptionSpec *oep = interp->parseMsgSpec;
            interp->parseMsgSpec = specPtr;
            Jsi_RC rc = (*cust->parseProc)(interp, specPtr, argValue, NULL, record, flags);
            if (rc != JSI_OK) {
                if (!interp->csc || lastErrCnt == interp->logErrorCnt)
                    Jsi_LogError("invalid value");
                interp->parseMsgSpec = oep;
                return JSI_ERROR;
            }
            interp->parseMsgSpec = oep;
        } else 
            return Jsi_LogBug("missing or bad custom for \"%s\"", specPtr->name);
        goto done;
    }

    switch (specPtr->id) {
    case JSI_OPTION_CUSTOM:
        if (!specPtr->custom) 
            return Jsi_LogBug("missing or custom for \"%s\"", specPtr->name);

    case JSI_OPTION_BOOL: {
        if (!argValue)
            *(char*)ptr = 0;
        else if (!Jsi_ValueIsBoolean(interp, argValue))
            goto bail;
        Jsi_GetBoolFromValue(interp, argValue, &bn);
        *(char*)ptr = bn;
        break;
    }

    case JSI_OPTION_INT:
    case JSI_OPTION_UINT:
    case JSI_OPTION_LONG:
    case JSI_OPTION_INTPTR_T:
    case JSI_OPTION_UINTPTR_T:
    case JSI_OPTION_SIZE_T:
    case JSI_OPTION_SSIZE_T:
    case JSI_OPTION_ULONG:
    case JSI_OPTION_SHORT:
    case JSI_OPTION_USHORT:
    case JSI_OPTION_UINT64:
    case JSI_OPTION_INT64:
    case JSI_OPTION_INT8:
    case JSI_OPTION_UINT8:
    case JSI_OPTION_INT16:
    case JSI_OPTION_UINT16:
    case JSI_OPTION_INT32:
    case JSI_OPTION_UINT32:
        wcount = 0;
        if (argValue) {
            if (!Jsi_ValueIsNumber(interp, argValue))
                goto bail;

            if (Jsi_GetWideFromValue(interp, argValue, &wcount) != JSI_OK) {
                return JSI_ERROR;
            }
            
        }
        switch (specPtr->id) {
#define _JSI_OPTSETTYP(typ, n, ptr) if (isIncr) n += *((typ *)ptr); \
    if (isIncr || ((Jsi_Wide)(typ)(n)) == (n)) interp->cdataIncrVal = *((typ *)ptr) = (typ)(n); else expType = #typ;
            case JSI_OPTION_INT:    _JSI_OPTSETTYP(int, wcount, ptr); break;
            case JSI_OPTION_UINT:   _JSI_OPTSETTYP(uint, wcount, ptr); break;
            case JSI_OPTION_INTPTR_T: _JSI_OPTSETTYP(intptr_t, wcount, ptr); break;
            case JSI_OPTION_UINTPTR_T:_JSI_OPTSETTYP(uintptr_t, wcount, ptr); break;
            case JSI_OPTION_SIZE_T:   _JSI_OPTSETTYP(size_t, wcount, ptr); break;
            case JSI_OPTION_SSIZE_T:  _JSI_OPTSETTYP(ssize_t, wcount, ptr); break;
            case JSI_OPTION_LONG:   _JSI_OPTSETTYP(long, wcount, ptr); break;
            case JSI_OPTION_ULONG:  _JSI_OPTSETTYP(ulong, wcount, ptr); break;
            case JSI_OPTION_SHORT:  _JSI_OPTSETTYP(short, wcount, ptr); break;
            case JSI_OPTION_USHORT: _JSI_OPTSETTYP(ushort, wcount, ptr); break;
            case JSI_OPTION_INT8:   _JSI_OPTSETTYP(int8_t, wcount, ptr); break;
            case JSI_OPTION_UINT8:  _JSI_OPTSETTYP(uint8_t, wcount, ptr) break;
            case JSI_OPTION_INT16:  _JSI_OPTSETTYP(int16_t, wcount, ptr); break;
            case JSI_OPTION_UINT16: _JSI_OPTSETTYP(uint16_t, wcount, ptr); break;
            case JSI_OPTION_INT32:  _JSI_OPTSETTYP(int32_t, wcount, ptr); break;
            case JSI_OPTION_UINT32: _JSI_OPTSETTYP(uint32_t, wcount, ptr); break;
            case JSI_OPTION_INT64:  _JSI_OPTSETTYP(int64_t, wcount, ptr); break;
            case JSI_OPTION_UINT64: _JSI_OPTSETTYP(uint64_t, wcount, ptr); break; // TODO: might loose top sign bit...
            default: break;
        }
        if (expType)
            return Jsi_LogType("not a %s", expType);
        isIncr = 0;
        break;

    case JSI_OPTION_NUMBER:
    case JSI_OPTION_DOUBLE:
    case JSI_OPTION_LDOUBLE:
    case JSI_OPTION_FLOAT:
        nv = 0;
        if (argValue) {
            if (!Jsi_ValueIsNumber(interp, argValue))
                goto bail;
            if (Jsi_GetNumberFromValue(interp, argValue, &nv) != JSI_OK) {
                return JSI_ERROR;
            }
        }
            
        switch (specPtr->id) {
#define _JSI_OPTSETNTYP(typ, n, ptr) if (!argValue) *(typ*)ptr = 0; else { if (isIncr) n += *((typ *)ptr); \
            interp->cdataIncrVal = *((typ *)ptr) = (typ)(n); \
            if (interp->strict && Jsi_NumberIsNaN((Jsi_Number)(*((typ *)ptr)))) return Jsi_LogError("not a number"); }

            case JSI_OPTION_NUMBER: _JSI_OPTSETNTYP(Jsi_Number, nv, ptr); break;
            case JSI_OPTION_LDOUBLE: _JSI_OPTSETNTYP(ldouble, nv, ptr); break;
            case JSI_OPTION_FLOAT: _JSI_OPTSETNTYP(float, nv, ptr); break;
            case JSI_OPTION_DOUBLE: _JSI_OPTSETNTYP(double, nv, ptr); break;
            default: break;
        }
        isIncr = 0;
        break;
    case JSI_OPTION_STRKEY:
    {
        if (argValue == NULL || Jsi_ValueIsNull(interp, argValue))
            *(const char**)ptr = NULL;
        else {
            const char *scp;
            if (Jsi_GetStringFromValue(interp, argValue, &scp) != JSI_OK) {
                return JSI_ERROR;
            }
            *(const char**)ptr = Jsi_KeyAdd(interp,scp);
        }
    }
    break;
    case JSI_OPTION_STRBUF:
    {
        if (argValue == NULL || Jsi_ValueIsNull(interp, argValue))
            *(char*)ptr = 0;
        else {
            int sLen;
            const char *scp = Jsi_ValueString(interp, argValue, &sLen);
            if (!scp)
                goto bail;
            if (sLen>(int)(specPtr->size-1)) {
                return Jsi_LogError("String too long");
                //sLen = specPtr->size-1;
            }
            memcpy((char*)ptr, scp, sLen);
            ((char*)ptr)[sLen] = 0;
        }
    }
    break;
    
#define _JSI_OPT_CHECKNULL(argValue) if (!argValue || Jsi_ValueIsNull(interp, argValue)) { \
         if (*((Jsi_Value **)ptr)) \
            Jsi_DecrRefCount(interp, *((Jsi_Value **)ptr)); \
        *((Jsi_Value **)ptr) = NULL; \
        break; \
    }

#define _JSI_OPT_ARGSET(argValue, ptr) \
    if (!(specPtr->flags&JSI_OPT_NO_DUPVALUE)) {\
        Jsi_IncrRefCount(interp, argValue); \
        if (*((Jsi_Value **)ptr)) Jsi_DecrRefCount(interp, *((Jsi_Value **)ptr)); \
    }\
    *((Jsi_Value **)ptr) = argValue;
    
    case JSI_OPTION_STRING:
        if (argValue == *((Jsi_Value **)ptr))
            break;
        _JSI_OPT_CHECKNULL(argValue);
        if (!Jsi_ValueIsString(interp, argValue))
            goto bail;
        _JSI_OPT_ARGSET(argValue, ptr);
        break;
        
    case JSI_OPTION_DSTRING:
        Jsi_DSInit((Jsi_DString *)ptr);
        if (argValue && !Jsi_ValueIsNull(interp, argValue))
        {
            int sLen;
            const char *scp = Jsi_ValueString(interp, argValue, &sLen);
            if (!scp)
                goto bail;
            Jsi_DSAppendLen((Jsi_DString *)ptr, scp, sLen);
        }
        break;
    
    case JSI_OPTION_TIME_D: {
       if (argValue)
        {
            Jsi_Number nv = 0;
            if (Jsi_ValueIsNumber(interp, argValue)) {
                Jsi_GetNumberFromValue(interp, argValue, &nv);
                *(double*)ptr = nv;
            } else {
                const char *scp;
                if (Jsi_GetStringFromValue(interp, argValue, &scp) != JSI_OK) {
                    return JSI_ERROR;
                }
                if (JSI_OK != Jsi_DatetimeParse(interp, scp, "", 0, &nv, false))
                    return JSI_ERROR;
                *(double*)ptr = nv;
            }
        } else {
            *(double*)ptr = 0;
        }
        break;
    }
    case JSI_OPTION_TIME_W: {
       if (argValue)
        {
            if (Jsi_ValueIsNumber(interp, argValue)) {
                Jsi_GetNumberFromValue(interp, argValue, (Jsi_Number*)ptr);
            } else {
                const char *scp;
                Jsi_Number num;
                if (Jsi_GetStringFromValue(interp, argValue, &scp) != JSI_OK) {
                    return JSI_ERROR;
                }
                if (JSI_OK != Jsi_DatetimeParse(interp, scp, "", 0, &num, false))
                    return JSI_ERROR;
                *(Jsi_Wide*)ptr = (Jsi_Wide)num;
            }
        } else {
            *(Jsi_Wide*)ptr = 0;
        }
        break;
    }
    case JSI_OPTION_TIME_T: {
       if (argValue)
        {
            if (Jsi_ValueIsNumber(interp, argValue)) {
                Jsi_Number num;
                Jsi_GetNumberFromValue(interp, argValue, &num);
                *(time_t*)ptr = (time_t)num;
            } else {
                const char *scp;
                if (Jsi_GetStringFromValue(interp, argValue, &scp) != JSI_OK) {
                    return JSI_ERROR;
                }
                Jsi_Number nval;
                if (JSI_OK != Jsi_DatetimeParse(interp, scp, "", 0, &nval, false))
                    return JSI_ERROR;
                *(time_t*)ptr = nval/1000LL;
            }
        } else {
            *(time_t*)ptr = 0;
        }
        break;
    }
    case JSI_OPTION_VAR:
        _JSI_OPT_CHECKNULL(argValue);
        if (argValue->vt != JSI_VT_NULL && argValue->vt != JSI_VT_VARIABLE) 
            goto bail;
        _JSI_OPT_ARGSET(argValue, ptr);
        break;

    case JSI_OPTION_FUNC:
        _JSI_OPT_CHECKNULL(argValue);
        if (argValue->vt != JSI_VT_OBJECT || argValue->d.obj->ot != JSI_OT_FUNCTION) 
            goto bail;
        if (specPtr->data && (interp->typeCheck.run|interp->typeCheck.all))
            if (!jsi_FuncArgCheck(interp, argValue->d.obj->d.fobj->func, (char*)specPtr->data)) 
                return Jsi_LogError("failed setting func pointer for %s", specPtr->name);

        _JSI_OPT_ARGSET(argValue, ptr);
        break;
        
    case JSI_OPTION_USEROBJ:
        _JSI_OPT_CHECKNULL(argValue);
        if (argValue->vt != JSI_VT_OBJECT || argValue->d.obj->ot != JSI_OT_USEROBJ) 
            goto bail;
        if (specPtr->data && Jsi_Strcmp((char*)specPtr->data, argValue->d.obj->d.uobj->reg->name)) 
            return Jsi_LogError("expected id %s for %s",(char*)specPtr->data,  specPtr->name);
        _JSI_OPT_ARGSET(argValue, ptr);
        break;

    case JSI_OPTION_REGEXP:
        _JSI_OPT_CHECKNULL(argValue);
        if (argValue->vt != JSI_VT_OBJECT || argValue->d.obj->ot != JSI_OT_REGEXP)
            goto bail;
        _JSI_OPT_ARGSET(argValue, ptr);
        break;

    case JSI_OPTION_OBJ:
        _JSI_OPT_CHECKNULL(argValue);
        if (argValue->vt != JSI_VT_OBJECT)
            goto bail;

    case JSI_OPTION_VALUE:
         _JSI_OPT_CHECKNULL(argValue);
         _JSI_OPT_ARGSET(argValue, ptr);
        break;
    case JSI_OPTION_ARRAY:
        _JSI_OPT_CHECKNULL(argValue);
        if (argValue->vt != JSI_VT_OBJECT || !argValue->d.obj->isarrlist)
            goto bail;
        _JSI_OPT_ARGSET(argValue, ptr);
        break;
#ifdef __cplusplus
    case JSI_OPTION_END:
#else
    default:
#endif
        Jsi_LogBug("invalid option id: %d", specPtr->id);
    }
done:
    specPtr->flags |= JSI_OPT_IS_SPECIFIED;
    if (isIncr)
        return Jsi_LogError("incr invalid for %s", specPtr->name);
    return JSI_OK;

bail:
    if (!emsg) {
        emsg = jsi_OptTypeInfo[specPtr->id].cName;
        epre = "expected ";
    }
    return Jsi_LogError("%s%s: for %s option \"%.40s\"", epre, emsg, (cust?cust->name:""), specPtr->name);
}

Project: jsish

jsonGenArray(Jsi_Interp *interp, Jsi_JsonParser *p, const char *js, uint pos, uint *endPos)
{
    if (js==NULL || !js[0])
        return NULL;
    Jsi_Value *v = Jsi_ValueNewArray(interp, 0, 0);
    Jsi_Obj *nobj = v->d.obj;
    uint i, n;
    Jsi_JsonTok *tok;
    
    tok = p->tokens+pos;
    Jsi_ValueMakeArrayObject(interp, &v, nobj);
    for (i=pos+1, n=0; i<p->toknext && n<tok->size; i++, n++) {
        if (n >= nobj->arrMaxSize) {
            if (Jsi_ObjArraySizer(interp, nobj, n+1)<=0) {
                break;
            }
        }
        nobj->arr[n] = jsonGen1Value(interp, p, js, i, &i, 1);
    }
    Jsi_ObjSetLength(interp, nobj, n);
    if (endPos)
        *endPos = i;
    return v;
}

Project: jsish

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

    int r;
    Jsi_RC result = JSI_OK;
    bool strict = 1;
    const char *cp;
    JSI_JSON_DECLARE(p, tokens, 0);
    Jsi_Value *jsv = Jsi_ValueArrayIndex(interp, args, 1);
    if (jsv && Jsi_ValueGetBoolean(interp, jsv, &strict) != JSI_OK) 
        return Jsi_LogError("Expected boolean");
    p.strict = strict;
    jsv = Jsi_ValueArrayIndex(interp, args, 0);
    cp = Jsi_ValueToString(interp, jsv, NULL);
    
    r = Jsi_JsonParse(&p, cp);
    Jsi_ValueMakeBool(interp, ret, r == JSI_JSON_ERR_NONE);
    Jsi_JsonFree(&p);
    return result;
}

Project: jsish

Jsi_Value *Jsi_ValueDup(Jsi_Interp *interp, Jsi_Value *v) {
    return ValueDup(interp, v);
}

Project: jsish

Jsi_RC Jsi_FSUnregister(Jsi_Filesystem *fsPtr) {
    FSList *fsl = jsiFSList, *flast = NULL;
    while (fsl) {
        if (fsl->fsPtr == fsPtr) {
            if (flast)
                flast->next = fsl->next;
            else
                jsiFSList = fsl->next;
            Jsi_Free(fsl);
            break;
        }
        flast = fsl;
        fsl = fsl->next;
    }
    return JSI_OK;
}

Project: jsish

static Jsi_RC jsi_ValueToBitset(Jsi_Interp *interp, Jsi_OptionSpec* spec, Jsi_Value *inValue, const char *inStr, void *record, Jsi_Wide flags)
{
    // TODO: change this to not use byte instead of int...
    int i, argc, n;
    char *s =(((char*)record) + spec->offset);
    char **argv;
    const char *cp, **list = (const char**)spec->data;
    uint64_t m = 0, im = 0;
    int fflags = ((flags|spec->flags)&JSI_OPT_CUST_NOCASE?JSI_CMP_NOCASE:0);
    if (!list) 
        return Jsi_LogError("custom enum spec did not set data: %s", spec->name);
    switch (spec->size) {
        case 1: im = *(uint8_t*)s; break;
        case 2: im = *(uint16_t*)s; break;
        case 4: im = *(uint32_t*)s; break;
        case 8: im = *(uint64_t*)s; break;
        default: 
            return Jsi_LogError("bitset size must be 1, 2, 4, or 8: %s", spec->name);
    }

#ifndef JSI_LITE_ONLY
    if (!inStr && Jsi_ValueIsString(interp, inValue))
        inStr = Jsi_ValueString(interp, inValue, NULL);
#endif
    if (inStr) {
        if (*inStr == '+') {
            inStr++;
            m = im;
        }
        if (*inStr) {
            Jsi_DString sStr;
            Jsi_DSInit(&sStr);
            Jsi_SplitStr(inStr, &argc, &argv, ",", &sStr);
            
            for (i=0; i<argc; i++) {
                int isnot = 0;
                cp = argv[i];
                if (*cp == '!') { isnot = 1; cp++; }
                if (JSI_OK != Jsi_GetIndex(interp, cp, list, "enum", fflags, &n))
                    return JSI_ERROR;
                if (n >= (int)(spec->size*8)) 
                    return Jsi_LogError("list larger than field size: %s", spec->name);
                if (isnot)
                    m &= ~(1<<n);
                else
                    m |= (1<<n);
            }
            Jsi_DSFree(&sStr);
        }
    } else {
#ifndef JSI_LITE_ONLY
        if (!inValue) {
            *s = 0;
            return JSI_OK;
        }
        if (Jsi_ValueIsObjType(interp, inValue, JSI_OT_OBJECT) && !Jsi_ValueIsArray(interp, inValue)) {
            Jsi_TreeEntry *tPtr;
            Jsi_TreeSearch search;
            Jsi_Tree *tp = Jsi_TreeFromValue(interp, inValue);
            
            m = im;
            for (tPtr = (tp?Jsi_TreeSearchFirst(tp, &search, 0, NULL):NULL);
                tPtr != NULL; tPtr = Jsi_TreeSearchNext(&search)) {
                cp =(char*) Jsi_TreeKeyGet(tPtr);
                Jsi_Value *optval = (Jsi_Value*)Jsi_TreeValueGet(tPtr);
                
                if (JSI_OK != Jsi_GetIndex(interp, cp, list, "bitset", fflags, &n)) {
                    Jsi_TreeSearchDone(&search);
                    return JSI_ERROR;
                }
                if (!Jsi_ValueIsBoolean(interp, optval)) {
                    Jsi_TreeSearchDone(&search);
                    return Jsi_LogError("object member is not a bool: %s", cp);
                }
                bool vb;
                Jsi_ValueGetBoolean(interp, optval, &vb);
                if (!vb)
                    m &= ~(1<<n);
                else
                    m |= (1<<n);
            }
            if (tp)
                Jsi_TreeSearchDone(&search);
            *s = m;
            return JSI_OK;
        }
        
        if (!Jsi_ValueIsArray(interp, inValue)) 
            return Jsi_LogError("expected array or object");
        argc = Jsi_ValueGetLength(interp, inValue);
        for (i=0; i<argc; i++) {
            int isnot = 0;
            Jsi_Value *v = Jsi_ValueArrayIndex(interp, inValue, i);
            const char *cp = (v?Jsi_ValueString(interp, v, NULL):"");
            if (!cp) 
                return Jsi_LogError("expected string");
            if (i == 0) {
                if (*cp == '+' && !cp[1]) {
                    m = im;
                    continue;
                }
            }
            if (*cp == '!') { isnot = 1; cp++; }
            if (JSI_OK != Jsi_GetIndex(interp, cp, list, "bitset", fflags, &n))
                return JSI_ERROR;
            if (isnot)
                m &= ~(1<<n);
            else
                m |= (1<<n);
        }
        *s = m;
#endif
    }
    switch (spec->size) {
        case 1: *(uint8_t*)s = (uint8_t)m; break;
        case 2: *(uint16_t*)s = (uint16_t)m; break;
        case 4: *(uint32_t*)s = (uint32_t)m; break;
        case 8: *(uint64_t*)s = (uint64_t)m; break;
    }
    return JSI_OK;
}

Project: jsish

static Jsi_RC SqliteConstructor(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                             Jsi_Value **ret, Jsi_Func *funcPtr)
{
    Jsi_RC rc = JSI_ERROR;
    /* void *cd = clientData; */
    int  flags;
    char *zErrMsg;
    const char *zFile = NULL, *vfs = 0;
    /* In normal use, each JSI interpreter runs in a single thread.  So
    ** by default, we can turn of mutexing on SQLite database connections.
    ** However, for testing purposes it is useful to have mutexes turned
    ** on.  So, by default, mutexes default off.  But if compiled with
    ** SQLITE_JSI_DEFAULT_FULLMUTEX then mutexes default on.
    */
    flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE;
#ifdef SQLITE_JSI_DEFAULT_FULLMUTEX
    flags |= SQLITE_OPEN_FULLMUTEX;
#else
    flags |= SQLITE_OPEN_NOMUTEX;
#endif

    Jsi_Value *vFile = Jsi_ValueArrayIndex(interp, args, 0);
    Jsi_Value *arg = Jsi_ValueArrayIndex(interp, args, 1);
    Jsi_DString dStr = {};
    int ismem = 0, create = 0;
    Jsi_Obj *fobj;
    Jsi_Value *toacc;
    const char *vf;
    const char *dbname = NULL;
    
    if (vFile==NULL || Jsi_ValueIsNull(interp, vFile) ||
        ((vf = Jsi_ValueString(interp, vFile, NULL)) && !Jsi_Strcmp(vf,":memory:"))) {
        zFile = ":memory:";
        ismem = 1;
    } else {
        zFile = Jsi_ValueNormalPath(interp, vFile, &dStr);
        if (zFile == NULL) 
            return Jsi_LogError("bad or missing file name");
        Jsi_StatBuf st = {};
        st.st_uid = -1;
        create = (Jsi_Lstat(interp, vFile, &st) != 0);
    }
    zErrMsg = 0;
    Jsi_Db *db = (Jsi_Db*)Jsi_Calloc(1, sizeof(*db) );
    if( db==0 ) {
        Jsi_DSFree(&dStr);
        Jsi_LogError("malloc failed");
        return JSI_ERROR;
    }
    db->sig = SQLITE_SIG_DB;
    db->_ = &dbObjCmd;
    db->_->newCnt++;
    db->_->activeCnt++;
    db->stmtCacheMax = NUM_PREPARED_STMTS;
    db->hasOpts = 1;
    if ((arg != NULL && !Jsi_ValueIsNull(interp,arg))
        && Jsi_OptionsProcess(interp, SqlOptions, db, arg, 0) < 0) {
        rc = JSI_ERROR;
        goto bail;
    }
    if (ismem == 0 &&
        (Jsi_InterpAccess(interp, vFile, (db->readonly ? JSI_INTACCESS_READ : JSI_INTACCESS_WRITE)) != JSI_OK
        || (create && Jsi_InterpAccess(interp, vFile, JSI_INTACCESS_CREATE) != JSI_OK))) {
        Jsi_LogError("Safe accces denied");
        goto bail;
    }

    if (db->stmtCacheMax<0 || db->stmtCacheMax>MAX_PREPARED_STMTS) {
        Jsi_LogError("option stmtCacheMax value %d is not in range 0..%d", db->stmtCacheMax, MAX_PREPARED_STMTS);
        goto bail;
    }
    if (!db->udata) {
        db->udata = Jsi_ValueNewObj(interp, NULL);
        Jsi_IncrRefCount(interp, db->udata);
    }
    if (db->readonly) {
        flags &= ~(SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE);
        flags |= SQLITE_OPEN_READONLY;
    } else {
        flags &= ~SQLITE_OPEN_READONLY;
        flags |= SQLITE_OPEN_READWRITE;
        if (db->noCreate) {
            flags &= ~SQLITE_OPEN_CREATE;
        }
    }
    if (db->vfs)
        vfs = Jsi_ValueToString(interp, db->vfs, NULL);
    if(db->mutex == MUTEX_NONE) {
        flags |= SQLITE_OPEN_NOMUTEX;
        flags &= ~SQLITE_OPEN_FULLMUTEX;
    } else {
        flags &= ~SQLITE_OPEN_NOMUTEX;
    }
    if(db->mutex ==MUTEX_FULL) {
        flags |= SQLITE_OPEN_FULLMUTEX;
        flags &= ~SQLITE_OPEN_NOMUTEX;
    } else {
        flags &= ~SQLITE_OPEN_FULLMUTEX;
    }
  
    if (SQLITE_OK != sqlite3_open_v2(zFile, &db->db, flags, vfs)) {
        Jsi_LogError("db open failed: %s", zFile);
        goto bail;
    }
    //Jsi_DSFree(&translatedFilename);

    if( SQLITE_OK!=sqlite3_errcode(db->db) ) {
        zErrMsg = sqlite3_mprintf("%s", sqlite3_errmsg(db->db));
        DbClose(db->db);
        db->db = 0;
    }
    if( db->db==0 ) {
        sqlite3_free(zErrMsg);
        goto bail;
    }
;
    toacc = NULL;
    if (Jsi_FunctionIsConstructor(funcPtr)) {
        toacc = _this;
    } else {
        Jsi_Obj *o = Jsi_ObjNew(interp);
        Jsi_PrototypeObjSet(interp, "Sqlite", o);
        Jsi_ValueMakeObject(interp, ret, o);
        toacc = *ret;
    }
    sqlite3_create_function(db->db, "unixtime", -1, SQLITE_UTF8, db, jsiSqlFuncUnixTime, 0, 0);
    
    fobj = Jsi_ValueGetObj(interp, toacc /* constructor obj*/);
    if ((db->objId = Jsi_UserObjNew(interp, &sqliteobject, fobj, db))<0)
        goto bail;
    db->stmtHash = Jsi_HashNew(interp, JSI_KEYS_STRING, NULL);
    db->fobj = fobj;
    //dbSys->cnt = Jsi_UserObjCreate(interp, sqliteobject.name /*dbSys*/, fobj, db);
    db->interp = interp;
    db->optPtr = &db->queryOpts;
    db->stmtCache = Jsi_ListNew((Jsi_Interp*)db, 0, dbStmtFreeProc);
    rc = JSI_OK;
    dbname = Jsi_DSValue(&db->name);
    if (dbname[0])
        sqlite3_db_config(db->db, SQLITE_DBCONFIG_MAINDBNAME, dbname);
    Jsi_JSONParseFmt(interp, &db->version, "{libVer:\"%s\", hdrVer:\"%s\", hdrNum:%d, hdrSrcId:\"%s\", pkgVer:%d}",
        (char *)sqlite3_libversion(), SQLITE_VERSION, SQLITE_VERSION_NUMBER, SQLITE_SOURCE_ID, jsi_DbPkgVersion); 
    dbSetupCallbacks(db, NULL);
    
bail:
    if (rc != JSI_OK) {
        if (db->hasOpts)
            Jsi_OptionsFree(interp, SqlOptions, db, 0);
        db->_->activeCnt--;
        Jsi_Free(db);
    }
    Jsi_DSFree(&dStr);
    Jsi_ValueMakeUndef(interp, ret);
    return rc;
}

Project: jsish

int Jsi_Scandir(Jsi_Interp *interp, Jsi_Value* path, Jsi_Dirent ***namelist,
 int (*filter)(const Jsi_Dirent *), int (*compar)(const Jsi_Dirent **, const Jsi_Dirent**)) 
 {
    void *data;
    Jsi_Filesystem *fsPtr = Jsi_FilesystemForPath(interp, path, &data);
    if (fsPtr == NULL || !fsPtr->scandirProc) return -1;
    return fsPtr->scandirProc(interp, path, namelist, filter, compar);
}

Project: jsish

char* Jsi_NormalPath(Jsi_Interp *interp, const char *path, Jsi_DString *dStr) {
    char prefix[3] = "";
    char cdbuf[PATH_MAX];
    Jsi_DSInit(dStr);
    if (!path || !path[0]) return NULL;
    if (*path == '/')
        Jsi_DSAppend(dStr, path, NULL);
#ifdef __WIN32  /* TODO: add proper handling for windows paths. */
    else if (*path && path[1] == ':') {
        Jsi_DSAppend(dStr, path, NULL);
        return Jsi_DSValue(dStr);
    }
#endif
    else if (path[0] == '~') {
        Jsi_DSAppend(dStr, jsi_GetHomeDir(interp), (path[1] == '/' ? "" : "/"), path+1, NULL);
    } else if (path[0] == '.' && path[1] == 0) {
        if (jsiIntData.pwd) {
            Jsi_DSAppend(dStr, jsiIntData.pwd, NULL);
        } else {
            Jsi_DSAppend(dStr, getcwd(cdbuf, sizeof(cdbuf)), NULL);
        }
    } else {
        if (jsiIntData.pwd) {
            Jsi_DSAppend(dStr, jsiIntData.pwd, "/", path, NULL);
        } else {
            Jsi_DSAppend(dStr, getcwd(cdbuf, sizeof(cdbuf)), "/", path, NULL);
        }
    }
    Jsi_DString sStr = {};
    char *cp = Jsi_DSValue(dStr);
#ifdef __WIN32
    if (*cp && cp[1] == ':') {
        prefix[0] = *cp;
        prefix[1] = cp[1];
        prefix[2] = 0;
        cp += 2;
    }
#endif
    int i=0, n=0, m, nmax, unclean=0, slens[PATH_MAX];
    char *sp = cp, *ss;
    char *sptrs[PATH_MAX];
    while (*cp && n<PATH_MAX) {
        while (*cp && *cp == '/') {
            cp++;
            if (*cp == '/')
                unclean = 1;
        }
        sptrs[n] = cp;
        if (cp[0] == '.' && (cp[1] == '.' || cp[1] == '/'))
            unclean = 1;
        ss = cp++;
        while (*ss && *ss != '/')
            ss++;
        slens[n++] = (ss-cp) + 1;
        cp = ss;
    }
    if (!unclean)
        return sp;
    /* Need to remove //, /./, /../ */
    nmax = n--;
    while (n>0) {
        if (slens[n]<=0) {
            n--;
            continue;
        }
        if (Jsi_Strncmp(sptrs[n],".",slens[n])==0)
            slens[n] = 0;
        else if (Jsi_Strncmp(ss,"..",slens[n])==0) {
            int cnt = 0;
            m = n-1;
            while (m>=0 && cnt<1) {
                if (slens[m])
                    cnt++;
                slens[m] = 0;
                m--;
            }
            if (cnt<1)
                return sp;  /* Can't fix it */
        }
        n--;
    }
    /* TODO: prefix for windows. */
    Jsi_DSAppend(&sStr, prefix, NULL);
    for (i=0; i<nmax; i++) {
        if (slens[i]) {
#ifdef __WIN32
            Jsi_DSAppend(&sStr, "/" /*"\\"*/, NULL);
#else
            Jsi_DSAppend(&sStr, "/", NULL);
#endif
            Jsi_DSAppendLen(&sStr, sptrs[i], slens[i]);
        }
    }
    Jsi_DSSetLength(dStr, 0);
    Jsi_DSAppend(dStr, Jsi_DSValue(&sStr), NULL);
    Jsi_DSFree(&sStr);
    return Jsi_DSValue(dStr);
}

Project: jsish

void Jsi_TreeSearchDone(Jsi_TreeSearch *searchPtr)
{
    if (searchPtr->Ptrs != searchPtr->staticPtrs)
        Jsi_Free(searchPtr->Ptrs);
    searchPtr->Ptrs = searchPtr->staticPtrs;
    searchPtr->top = 0;
}

Project: jsish

bool Jsi_NumberIsNormal(Jsi_Number a) { return (fpclassify(a) == FP_ZERO || isnormal(a)); }

Project: jsish

static Jsi_RC jsi_AliasFree(Jsi_Interp *interp, Jsi_HashEntry *hPtr, void *data) {
    /* TODO: deal with other copies of func may be floating around (refCount). */
    AliasCmd *ac = (AliasCmd *)data;
    if (!ac) return JSI_ERROR;
    SIGASSERT(ac,ALIASCMD);
    if (ac->func)
        Jsi_DecrRefCount(ac->dinterp, ac->func);
    if (ac->args)
        Jsi_DecrRefCount(ac->dinterp, ac->args);
    if (!ac->cmdVal)
        return JSI_OK;
    Jsi_Func *fobj = ac->cmdVal->d.obj->d.fobj->func;
    fobj->cmdSpec->reserved[2] = NULL;
    fobj->cmdSpec->proc = NULL;
    if (ac->intobj && ac->intobj->subinterp) {
        Jsi_CommandDelete(ac->intobj->subinterp, ac->cmdName);
        //if (Jsi_Strchr(ac->cmdName, '.'))
        //    Jsi_LogBug("alias free with X.Y dot name leaks memory: %s", ac->cmdName);
    } else
        Jsi_DecrRefCount(ac->subinterp, ac->cmdVal);
    _JSI_MEMCLEAR(ac);
    Jsi_Free(ac);
    return JSI_OK;
}

Project: jsish

const char* jsi_opcode_string(uint opCode)
{
    if (opCode >= (sizeof(jsi_op_names)/sizeof(jsi_op_names[0])))
        return "NULL";
    return jsi_op_names[opCode];
}

Project: jsish

int Jsi_Access(Jsi_Interp *interp, Jsi_Value* path, int mode) {
    void *data;
    Jsi_Filesystem *fsPtr = Jsi_FilesystemForPath(interp, path, &data);
    if (fsPtr == NULL || !fsPtr->accessProc) return -1;
    if (interp->isSafe && fsPtr && fsPtr == &jsiFilesystem) {
        int aflag = (mode&W_OK ? JSI_INTACCESS_WRITE : JSI_INTACCESS_READ);
        if (Jsi_InterpAccess(interp, path, aflag) != JSI_OK)
            return -1;
    }
    return fsPtr->accessProc(interp, path, mode);
}

Project: jsish

static Jsi_OpCodes *code_jfalse(int off) { JSI_NEW_CODES(0,OP_JFALSE, off); }

Project: jsish

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

Project: jsish

char *Jsi_ValueArrayIndexToStr(Jsi_Interp *interp, Jsi_Value *args, int index, int *lenPtr)
{
    Jsi_Value *arg = Jsi_ValueArrayIndex(interp, args, index);
    if (!arg)
        return NULL;
    char *res = Jsi_ValueString(interp, arg, lenPtr);
    if (res)
        return res;
    res = (char*)Jsi_ValueToString(interp, arg, NULL);
    if (res && lenPtr)
        *lenPtr = Jsi_Strlen(res);
    return res;
}

Project: jsish

Jsi_Db* Jsi_DbNew(const char *zFile, int inFlags /* JSI_DBI_* */)
{
    if (!jsi_dbVfsPtr) {
        printf( "Sqlite unsupported\n");
        return NULL;
    }
    return jsi_dbVfsPtr->dbNew(zFile, inFlags);
}

Project: jsish

static Jsi_RC jsiInterpDelete(Jsi_Interp* interp, void *unused)
{
    SIGASSERT(interp,INTERP);
    bool isMainInt = (interp == jsiIntData.mainInterp);
    int mainFlag = (isMainInt ? 2 : 1);
    if (isMainInt)
        DeleteAllInterps();
    if (interp->opts.initProc)
        (*interp->opts.initProc)(interp, mainFlag);
    jsiIntData.delInterp = interp;
    if (interp->gsc) jsi_ScopeChainFree(interp, interp->gsc);
    if (interp->csc) Jsi_DecrRefCount(interp, interp->csc);
    if (interp->ps) jsi_PstateFree(interp->ps);
    int i;
    for (i=0; i<interp->maxStack; i++) {
        if (interp->Stack[i]) Jsi_DecrRefCount(interp, interp->Stack[i]);
        if (interp->Obj_this[i]) Jsi_DecrRefCount(interp, interp->Obj_this[i]);
    }
    if (interp->Stack) {
        Jsi_Free(interp->Stack);
        Jsi_Free(interp->Obj_this);
    }

    if (interp->argv0)
        Jsi_DecrRefCount(interp, interp->argv0);
    if (interp->console)
        Jsi_DecrRefCount(interp, interp->console);
    if (interp->lastSubscriptFail)
        Jsi_DecrRefCount(interp, interp->lastSubscriptFail);
    if (interp->nullFuncRet)
        Jsi_DecrRefCount(interp, interp->nullFuncRet);
    Jsi_HashDelete(interp->codeTbl);
    Jsi_MapDelete(interp->cmdSpecTbl);
    Jsi_HashDelete(interp->fileTbl);
    Jsi_HashDelete(interp->funcObjTbl);
    Jsi_HashDelete(interp->funcsTbl);
    if (interp->profileCnt) { // TODO: resolve some values from dbPtr, others not.
        double endTime = jsi_GetTimestamp();
        double coverage = (int)(100.0*interp->coverHit/interp->coverAll);
        Jsi_DString dStr;
        Jsi_DSInit(&dStr);
        Jsi_DSPrintf(&dStr, "PROFILE: TOTAL: time=%.6f, func=%.6f, cmd=%.6f, #funcs=%d, #cmds=%d, cover=%2.1f%%, #values=%d, #objs=%d %s%s\n",
            endTime-interp->startTime, interp->funcSelfTime, interp->cmdSelfTime, interp->funcCallCnt, interp->cmdCallCnt,
            coverage, interp->dbPtr->valueAllocCnt,  interp->dbPtr->objAllocCnt,
            interp->parent?" ::":"", (interp->parent&&interp->name?interp->name:""));
        Jsi_Puts(interp, jsi_Stderr, Jsi_DSValue(&dStr), -1);
        Jsi_DSFree(&dStr);
    }
    if (isMainInt)
        Jsi_HashDelete(interp->lexkeyTbl);
    Jsi_HashDelete(interp->protoTbl);
    if (interp->subthread)
        jsiIntData.mainInterp->threadCnt--;
    if (interp->subthread && interp->strKeyTbl == jsiIntData.mainInterp->strKeyTbl)
        jsiIntData.mainInterp->threadShrCnt--;
    if (!jsiIntData.mainInterp->threadShrCnt)
#ifdef JSI_USE_MANY_STRKEY
        jsiIntData.mainInterp->strKeyTbl->v.tree->opts.lockTreeProc = NULL;
#else
        jsiIntData.mainInterp->strKeyTbl->v.hash->opts.lockHashProc = NULL;
#endif
    //Jsi_ValueMakeUndef(interp, &interp->ret);
    Jsi_HashDelete(interp->thisTbl);
    Jsi_HashDelete(interp->varTbl);
    Jsi_HashDelete(interp->genValueTbl);
    Jsi_HashDelete(interp->genObjTbl);
    Jsi_HashDelete(interp->aliasHash);
    if (interp->staticFuncsTbl)
        Jsi_HashDelete(interp->staticFuncsTbl);
    if (interp->breakpointHash)
        Jsi_HashDelete(interp->breakpointHash);
    if (interp->preserveTbl->numEntries!=0)
        Jsi_LogBug("Preserves unbalanced");
    Jsi_HashDelete(interp->preserveTbl);
    if (interp->curDir)
        Jsi_Free(interp->curDir);
    if (isMainInt) {
        jsi_InitFilesys(interp, mainFlag);
    }
#ifndef JSI_OMIT_VFS
    jsi_InitVfs(interp, 1);
#endif
#ifndef JSI_OMIT_CDATA
        jsi_InitCData(interp, mainFlag);
#endif
#if JSI__MYSQL==1
        Jsi_InitMySql(interp, mainFlag);
#endif
    while (interp->interpStrEvents) {
        InterpStrEvent *se = interp->interpStrEvents;
        interp->interpStrEvents = se->next;
        if (se->acfunc)
            Jsi_DecrRefCount(interp, se->acfunc);
        if (se->acdata)
            Jsi_Free(se->acdata);
        Jsi_Free(se);
    }

    if (interp->Mutex)
        Jsi_MutexDelete(interp, interp->Mutex);
    if (interp->QMutex) {
        Jsi_MutexDelete(interp, interp->QMutex);
        Jsi_DSFree(&interp->interpEvalQ);
    }
    if (interp->nullFuncArg)
        Jsi_DecrRefCount(interp, interp->nullFuncArg);
    if (interp->NullValue)
        Jsi_DecrRefCount(interp, interp->NullValue);
    if (interp->Function_prototype_prototype) {
        if (interp->Function_prototype_prototype->refCnt>1)
            Jsi_DecrRefCount(interp, interp->Function_prototype_prototype);
        Jsi_DecrRefCount(interp, interp->Function_prototype_prototype);
    }
    if (interp->Object_prototype) {
        Jsi_DecrRefCount(interp, interp->Object_prototype);
    }
    Jsi_HashDelete(interp->regexpTbl);
    Jsi_OptionsFree(interp, InterpOptions, interp, 0);
    Jsi_HashDelete(interp->userdataTbl);
    Jsi_HashDelete(interp->eventTbl);
    if (interp->inopts)
        Jsi_DecrRefCount(interp, interp->inopts);
    if (interp->safeWriteDirs)
        Jsi_DecrRefCount(interp, interp->safeWriteDirs);
    if (interp->safeReadDirs)
        Jsi_DecrRefCount(interp, interp->safeReadDirs);
    if (interp->pkgDirs)
        Jsi_DecrRefCount(interp, interp->pkgDirs);
    for (i=0; interp->cleanObjs[i]; i++) {
        interp->cleanObjs[i]->tree->opts.freeHashProc = 0;
        Jsi_ObjFree(interp, interp->cleanObjs[i]);
    }
    Jsi_HashDelete(interp->bindTbl);
    for (i = 0; i <= interp->cur_scope; i++)
        jsi_ScopeStrsFree(interp, interp->scopes[i]);
#if JSI__ZVFS==1
    Jsi_InitZvfs(interp, mainFlag);
#endif
    if (!interp->parent)
        Jsi_HashDelete(interp->loadTbl);
    if (interp->packageHash)
        Jsi_HashDelete(interp->packageHash);
    Jsi_HashDelete(interp->assocTbl);
    interp->cleanup = 1;
    jsi_AllObjOp(interp, NULL, -1);
#ifdef JSI_MEM_DEBUG
    jsi_DebugDumpValues(interp);
#endif
    if (isMainInt || interp->strKeyTbl != jsiIntData.mainInterp->strKeyTbl)
        Jsi_MapDelete(interp->strKeyTbl);

    if (isMainInt)
        jsiIntData.mainInterp = NULL;
    _JSI_MEMCLEAR(interp);
    jsiIntData.delInterp = NULL;
    Jsi_Free(interp);
    return JSI_OK;
}

Project: jsish

static Jsi_RC SqliteBackupCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                           Jsi_Value **ret, Jsi_Func *funcPtr)
{
    Jsi_Db *jdb;
    Jsi_RC rv = JSI_OK;
    int rc;
    const char *zDestFile;
    const char *zSrcDb;
    sqlite3 *pDest;
    sqlite3_backup *pBackup;

    if (!(jdb = dbGetDbHandle(interp, _this, funcPtr))) return JSI_ERROR;
    Jsi_Value *vFile = Jsi_ValueArrayIndex(interp, args, 0);
    int argc = Jsi_ValueGetLength(interp, args);
    if( argc==1 ) {
        zSrcDb = "main";
    } else {
        zSrcDb = Jsi_ValueArrayIndexToStr(interp, args, 1, NULL);
    }
    Jsi_DString dStr = {};
    if (!vFile)
        zDestFile = ":memory:";
    else {
        zDestFile = Jsi_ValueNormalPath(interp, vFile, &dStr);
        if (zDestFile == NULL) 
            return Jsi_LogError("bad or missing file name");
    }
    rc = sqlite3_open(zDestFile, &pDest);
    if( rc!=SQLITE_OK ) {
        Jsi_LogError("cannot open target database %s: %s", zDestFile, sqlite3_errmsg(pDest));
        DbClose(pDest);
        Jsi_DSFree(&dStr);
        return JSI_ERROR;
    }
    pBackup = sqlite3_backup_init(pDest, "main", jdb->db, zSrcDb);
    if( pBackup==0 ) {
        Jsi_LogError("backup failed: %s", sqlite3_errmsg(pDest));
        DbClose(pDest);
        Jsi_DSFree(&dStr);
        return JSI_ERROR;
    }
    while(  (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK ) {}
    sqlite3_backup_finish(pBackup);
    if( rc==SQLITE_DONE ) {
        rv = JSI_OK;
    } else {
        Jsi_LogError("backup failed: %s", sqlite3_errmsg(pDest));
        rv = JSI_ERROR;
    }
    Jsi_DSFree(&dStr);
    DbClose(pDest);
    return rv;
}

Project: jsish

static Jsi_RC jsi_wsFileRead(Jsi_Interp *interp, Jsi_Value *name, Jsi_DString *dStr, jsi_wsCmdObj *cmdPtr, jsi_wsPss *pss) {
    Jsi_StatBuf sb;
    Jsi_RC rc = JSI_ERROR;
    int n = Jsi_Stat(interp, name, &sb);
    if (!n && sb.st_size>0) {
        char fdir[PATH_MAX];
        const char* cr = cmdPtr->curRoot, *fpath=NULL;
        if (!Jsi_FSNative(interp, name) || ((fpath= Jsi_Realpath(interp, name, fdir))
            && cr && !Jsi_Strncmp(fpath, cr, Jsi_Strlen(cr)))) {
            rc = Jsi_FileRead(interp, name, dStr);
            if (rc == JSI_OK && cmdPtr->onModify && Jsi_FSNative(interp, name))
                jsi_wsFileAdd(interp, cmdPtr, name);
        } else
            fprintf(stderr, "Skip read file %s in %s\n", Jsi_ValueString(interp, name, NULL), (cr?cr:""));
    }
    if (cmdPtr->noWarn)
        return JSI_OK;
    return rc;
}

Project: jsish

static Jsi_OpCodes *code_not() { JSI_NEW_CODES(0,OP_NOT, 0); }

Project: jsish

Jsi_Value* Jsi_ValueMakeBlob(Jsi_Interp *interp, Jsi_Value **vPtr, unsigned char *s, int len) {
    Jsi_Value *v = (vPtr?*vPtr:NULL);
    if (!v)
        v = Jsi_ValueNew(interp);
    else
        Jsi_ValueReset(interp, vPtr);
    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;
    obj->isBlob = 1;
    return v;
}

Project: jsish

static void dbOutputQuotedString(Jsi_DString *dStr, const char *z) {
    int i;
    int nSingle = 0;
    for(i=0; z[i]; i++) {
        if( z[i]=='\'' ) nSingle++;
    }
    if( nSingle==0 ) {
        Jsi_DSAppend(dStr,"'", z, "'", NULL);
    } else {
        Jsi_DSAppend(dStr,"'", NULL);
        while( *z ) {
            for(i=0; z[i] && z[i]!='\''; i++) {}
            if( i==0 ) {
                Jsi_DSAppend(dStr,"''", NULL);
                z++;
            } else if( z[i]=='\'' ) {
                Jsi_DSAppendLen(dStr,z, i);
                Jsi_DSAppend(dStr,"''", NULL);
                z += i+1;
            } else {
                Jsi_DSAppend(dStr, z, NULL);
                break;
            }
        }
        Jsi_DSAppend(dStr,"'", NULL);
    }
}

Project: jsish

static Jsi_TreeEntry* maximum_node(Jsi_TreeEntry* n) {
    Assert (n != NULL);
    while (n->right != NULL) {
        n = n->right;
    }
    return n;
}

Project: jsish

static Jsi_RC jsi_AliasCreateCmd(Jsi_Interp* interp, const char* key, AliasCmd* ac) {
    if (Jsi_InterpGone(interp))
        return JSI_ERROR;
    key = Jsi_KeyAdd(interp, key);
    Jsi_Value *cmd = jsi_CommandCreate(interp, key, jsi_AliasInvoke, NULL, 0, 0);
    if (!cmd)
        return Jsi_LogBug("command create failure");
    ac->cmdVal = cmd;
    Jsi_Func *fobj = cmd->d.obj->d.fobj->func;
    fobj->cmdSpec->reserved[2] = ac;
    cmd->d.obj->isNoOp = (ac->func->d.obj->d.fobj->func->callback == jsi_NoOpCmd);
    return JSI_OK;
}

Project: jsish

int Jsi_Rename(Jsi_Interp *interp, Jsi_Value *src, Jsi_Value *dst) {
    void *data;
    Jsi_Filesystem *fsPtr = Jsi_FilesystemForPath(interp, src, &data);
    if (fsPtr != Jsi_FilesystemForPath(interp, src, &data)) return -1;
    if (fsPtr == NULL || !fsPtr->renameProc) return -1;
    return fsPtr->renameProc(interp, src,dst);
}

Project: jsish

static Jsi_Value* dbGetValueGet(Jsi_Interp *interp, sqlite3_value *pIn)
{
    Jsi_Value *v = Jsi_ValueNew(interp);
    switch (sqlite3_value_type(pIn)) {
    case SQLITE_BLOB: {
        int bytes;
        bytes = sqlite3_value_bytes(pIn);
        const char *zBlob = (char*) sqlite3_value_blob(pIn);
        if(!zBlob ) {
            return Jsi_ValueMakeNull(interp, &v);
        }
        unsigned char *uptr = (unsigned char*)Jsi_Malloc(bytes+1);
        memcpy(uptr, zBlob, bytes);
        uptr[bytes] = 0;
        return Jsi_ValueMakeBlob(interp, &v, uptr, bytes);
    }
    case SQLITE_INTEGER: {
        sqlite_int64 n = sqlite3_value_int64(pIn);
        if( n>=-2147483647 && n<=2147483647 ) {
            return Jsi_ValueMakeNumber(interp, &v, n);
        } else {
            return Jsi_ValueMakeNumber(interp, &v, n);
        }
    }
    case SQLITE_FLOAT: {
        return Jsi_ValueMakeNumber(interp, &v, (Jsi_Number)sqlite3_value_double(pIn));
    }
    case SQLITE_NULL: {
        return Jsi_ValueMakeNull(interp, &v);
    }
    default:
        return Jsi_ValueMakeStringDup(interp, &v, (char *)sqlite3_value_text(pIn));
    }
    return v;
}

Project: jsish

static Jsi_RC SqliteRestoreCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
                            Jsi_Value **ret, Jsi_Func *funcPtr)
{
    Jsi_Db *jdb;
    const char *zSrcFile;
    const char *zDestDb;
    sqlite3 *pSrc;
    sqlite3_backup *pBackup;
    int nTimeout = 0;
    int rc;

    if (!(jdb = dbGetDbHandle(interp, _this, funcPtr))) return JSI_ERROR;
    Jsi_Value *vFile = Jsi_ValueArrayIndex(interp, args, 0);
    int argc = Jsi_ValueGetLength(interp, args);
    if( argc==1 ) {
        zDestDb = "main";
    } else {
        zDestDb = Jsi_ValueArrayIndexToStr(interp, args, 1, NULL);
    }
    Jsi_DString dStr = {};
    if (!vFile)
        zSrcFile = ":memory:";
    else {
        zSrcFile = Jsi_ValueNormalPath(interp, vFile, &dStr);
        if (zSrcFile == NULL) 
            return Jsi_LogError("bad or missing file name");
    }
    rc = sqlite3_open_v2(zSrcFile, &pSrc, SQLITE_OPEN_READONLY, 0);

    if( rc!=SQLITE_OK ) {
        Jsi_LogError("cannot open source database: %s", sqlite3_errmsg(pSrc));
        DbClose(pSrc);
        Jsi_DSFree(&dStr);
        return JSI_ERROR;
    }
    pBackup = sqlite3_backup_init(jdb->db, zDestDb, pSrc, "main");
    if( pBackup==0 ) {
        Jsi_LogError("restore failed: %s", sqlite3_errmsg(jdb->db));
        DbClose(pSrc);
        Jsi_DSFree(&dStr);
        return JSI_ERROR;
    }
    while( (rc = sqlite3_backup_step(pBackup,100))==SQLITE_OK
            || rc==SQLITE_BUSY ) {
        if( rc==SQLITE_BUSY ) {
            if( nTimeout++ >= 3 ) break;
            sqlite3_sleep(100);
        }
    }
    sqlite3_backup_finish(pBackup);
    Jsi_RC rv;
    if( rc==SQLITE_DONE ) {
        rv = JSI_OK;
    } else if( rc==SQLITE_BUSY || rc==SQLITE_LOCKED ) {
        Jsi_LogError("restore failed: source database busy");
        rv = JSI_ERROR;
    } else {
        Jsi_LogError("restore failed: %s", sqlite3_errmsg(jdb->db));
        rv = JSI_ERROR;
    }
    Jsi_DSFree(&dStr);
    DbClose(pSrc);
    return rv;
}

Project: jsish

Jsi_HashEntryNew(Jsi_Hash *tablePtr, const void *key, bool *newPtr)
{
    if (tablePtr->opts.lockHashProc && (*tablePtr->opts.lockHashProc)(tablePtr, 1) != JSI_OK)
        return NULL;
    Jsi_HashEntry *hPtr =  (*((tablePtr)->createProc))(tablePtr, key, newPtr);
#ifdef JSI_HAS_SIG_HASHENTRY
    SIGINIT(hPtr, HASHENTRY);
#endif
    if (tablePtr->opts.lockHashProc)
        (*tablePtr->opts.lockHashProc)(tablePtr, 0);
    return hPtr;
}

Project: jsish

Jsi_RC Jsi_ValueInsert(Jsi_Interp *interp, Jsi_Value *target, const char *key, Jsi_Value *val, int flags)
{
    if (target == NULL)
        target = interp->csc;
    if (target->vt != JSI_VT_OBJECT) {
        if (interp->strict)
            Jsi_LogWarn("Target is not object");
        return JSI_ERROR;
    }
    target->f.flag |= flags;
    if (Jsi_ObjInsert(interp, target->d.obj, key, val, flags))
        return JSI_OK;
    return JSI_ERROR;
}

Project: jsish

Jsi_Value* jsi_ValueMakeBlobDup(Jsi_Interp *interp, Jsi_Value **ret, unsigned char *s, int len) {
    if (len<0) len = Jsi_Strlen((char*)s);
    uchar *dp = (uchar*)Jsi_Malloc(len+1);
    memcpy(dp, s, len);
    dp[len] = 0;
    return Jsi_ValueMakeBlob(interp, ret, dp, len);
}

Project: jsish

static void dbEvalRowInfo(
    DbEvalContext *p,               /* Evaluation context */
    int *pnCol,                     /* OUT: Number of column names */
    char ***papColName,           /* OUT: Array of column names */
    int **papColType
) {
    /* Compute column names */
    // Jsi_Interp *interp = p->jdb->interp;

    if( 0==p->apColName ) {
        sqlite3_stmt *pStmt = p->pPreStmt->pStmt;
        uint i;                        /* Iterator variable */
        uint nCol;                     /* Number of columns returned by pStmt */
        char **apColName = 0;      /* Array of column names */
        int *apColType = 0;
        const char *zColName;         /* Column name */
        int numRid = 0;               /* Number of times rowid seen. */

        p->nCol = nCol = sqlite3_column_count(pStmt);
        if( nCol>0 && (papColName || p->pArray) ) {
            uint cnLen = sizeof(char*)*nCol, cnStart = cnLen;
            for(i=0; i<nCol && cnLen<sizeof(p->staticColNames); i++)
                cnLen += Jsi_Strlen(sqlite3_column_name(pStmt,i))+1;
            if (cnLen>=sizeof(p->staticColNames)) {
                apColName = (char**)Jsi_Calloc(nCol, sizeof(char*) );
                cnStart = 0;
            } else {
                apColName = (char**)p->staticColNames;
            }
            if (papColType) {
                if (nCol < SQL_MAX_STATIC_TYPES)
                    apColType = p->staticColTypes;
                else
                    apColType = (int*)Jsi_Calloc(nCol, sizeof(int));
            }
            for(i=0; i<nCol; i++) {
                zColName = sqlite3_column_name(pStmt,i);
                if (cnStart==0)
                    apColName[i] = Jsi_Strdup(zColName);
                else {
                    apColName[i] = p->staticColNames+cnStart;
                    Jsi_Strcpy(apColName[i], zColName);
                    cnStart += Jsi_Strlen(zColName)+1;
                }
                if (apColType)
                    apColType[i] = sqlite3_column_type(pStmt,i);
                /* Check if rowid appears first, and more than once. */
                if ((i == 0 || numRid>0) &&
                        (zColName[0] == 'r' && Jsi_Strcmp(zColName,"rowid") == 0)) {
                    numRid++;
                }
            }
            /* Change first rowid to oid. */
            if (numRid > 1) {
                if (apColName != (char**)p->staticColNames) {
                    Jsi_Free(apColName[0]);
                    apColName[0] = Jsi_Strdup("oid");
                } else {
                    Jsi_Strcpy(apColName[0], "oid");
                }
            }
            p->apColName = apColName;
            p->apColType = apColType;
        }
    }
    if( papColName ) {
        *papColName = p->apColName;
    }
    if( papColType ) {
        *papColType = p->apColType;
    }
    if( pnCol ) {
        *pnCol = p->nCol;
    }
}

Project: jsish

static Jsi_OpCodes *code_scatch(jsi_Pstate *p, jsi_Pline *line, const char *var) { JSI_NEW_CODESLN(0,OP_SCATCH, var); }

Project: jsish

void Jsi_RegExpFree(Jsi_Regex* re) {
    regfree(&re->reg);
    _JSI_MEMCLEAR(re);
    Jsi_Free(re);
}

Project: jsish

static Jsi_OpCodes *code_eval(jsi_Pstate *p, jsi_Pline *line, int argc, Jsi_OpCodes *c) {
    jsi_FreeOpcodes(c); // Eliminate leak of unused opcodes.
    JSI_NEW_CODESLN(0,OP_EVAL, argc);
}

Project: jsish

int Jsi_DecrRefCount(Jsi_Interp* interp, Jsi_Value *v) {
    SIGASSERT(v,VALUE);
    if (v->refCnt<=0) {
#ifdef JSI_MEM_DEBUG
        fprintf(stderr, "Value decr with ref %d: VD.Idx=%d\n", v->refCnt, v->VD.Idx);
#endif
        return -2;
    }
    int ref;
    jsi_DebugValue(v,"Decr", jsi_DebugValueCallIdx(), interp);
    if ((ref = --(v->refCnt)) <= 0) {
        v->refCnt = -1;
        Jsi_ValueFree(interp, v);
    }
    return ref;
}

Project: jsish

static void NormalizeUnixPath(Jsi_Interp *interp, char *path) {
    char **argv; int argc, i;
    if (!Jsi_Strstr(path, "./")) return;
    Jsi_DString dStr = {}, eStr = {};
    if (path[0] != '/' && Jsi_Strstr(path, "..")) {
        char *npath = Jsi_GetCwd(interp, &eStr);
        if (npath && Jsi_Strcmp(npath,"/")) {
            Jsi_DSAppend(&eStr, "/", path, NULL);
            path = Jsi_DSValue(&eStr);
        }
    }
    Jsi_DString sStr;
    Jsi_DSInit(&sStr);
    Jsi_SplitStr(path, &argc, &argv, "/", &sStr);
    char *cp = path;
    *cp = 0;
    for (i=0; i<argc; i++) {
        if (i == 0 && argv[0][0] == 0) {
            continue;
        } else if (argv[i][0] == 0) {
            continue;
        } else if (i && !Jsi_Strcmp(argv[i],".")) {
            continue;
        } else if (!Jsi_Strcmp(argv[i],"..")) {
            char *pcp = Jsi_DSValue(&dStr), *lcp = pcp;
            pcp = Jsi_Strrchr(pcp, '/');
            if (pcp && pcp != Jsi_DSValue(&dStr)) {
                *pcp = 0;
                Jsi_DSSetLength(&dStr, Jsi_Strlen(lcp));
            }
            continue;
        } else {
            Jsi_DSAppend(&dStr, (i>0?"/":""), argv[i], NULL);
        }
    }
    Jsi_DSFree(&sStr);
    Jsi_Strcpy(path, Jsi_DSValue(&dStr));
    Jsi_DSFree(&dStr);
    Jsi_DSFree(&eStr);
}

Project: jsish

void dbTypeNameHashInit(Jsi_Db *jdb) {
    Jsi_Interp *interp = jdb->interp;
    Jsi_Hash *hPtr = jdb->typeNameHash = Jsi_HashNew(interp, JSI_KEYS_STRING, NULL);
    Jsi_HashSet(hPtr, (void*)"blob", (void*)JSI_OPTION_STRBUF);
    Jsi_HashSet(hPtr, (void*)"string", (void*)JSI_OPTION_STRING);
    Jsi_HashSet(hPtr, (void*)"double", (void*)JSI_OPTION_DOUBLE);
    Jsi_HashSet(hPtr, (void*)"integer", (void*)JSI_OPTION_INT64);
    Jsi_HashSet(hPtr, (void*)"bool", (void*)JSI_OPTION_BOOL);
    Jsi_HashSet(hPtr, (void*)"time_d", (void*)JSI_OPTION_TIME_D);
    Jsi_HashSet(hPtr, (void*)"time_w", (void*)JSI_OPTION_TIME_W);
    Jsi_HashSet(hPtr, (void*)"time_t", (void*)JSI_OPTION_TIME_T);
    Jsi_HashSet(hPtr, (void*)"date", (void*)JSI_OPTION_TIME_W);
    Jsi_HashSet(hPtr, (void*)"time", (void*)JSI_OPTION_TIME_W);
    Jsi_HashSet(hPtr, (void*)"datetime", (void*)JSI_OPTION_TIME_W);
}

Project: jsish

static int jsi_FSWriteProc(Jsi_Channel chan, const char *buf, int size) {
    return fwrite(buf, 1, size, _JSI_GETFP(chan,0));
}

Project: jsish

static Jsi_OpCodes *code_unref() { JSI_NEW_CODES(0,OP_UNREF, 0); }

Project: jsish

static Jsi_RC FilesysWriteCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_Value *_this,
    Jsi_Value **ret, Jsi_Func *funcPtr)
{
    int  sum = 0, n, m;
    UdfGet(udf, _this, funcPtr);
    char *buf = Jsi_ValueArrayIndexToStr(interp, args, 0, &m);

    if (!udf->filename) {
        goto bail;
    }
    while (m > 0 && sum < MAX_LOOP_COUNT && (n = Jsi_Write(interp, udf->chan, buf, m)) > 0) {
        /* TODO: limit max size. */
        sum += n;
        m -= n;
    }
bail:
    Jsi_ValueMakeNumber(interp, ret, (Jsi_Number)sum);
    return JSI_OK;
}

Project: jsish

static jsi_JmpPopInfo *jpinfo_new(int off, int topop)
{
    jsi_JmpPopInfo *r = (jsi_JmpPopInfo *)Jsi_Calloc(1, sizeof(*r));
    r->off = off;
    r->topop = topop;
    return r;
}