static int _object_get_callback(Jsi_Tree *tree, Jsi_TreeEntry *hPtr, void *data) { Jsi_Value *v; objwalker *ow = data; Jsi_DString *dStr = ow->dStr; int len; char *str; if ((hPtr->f.bits.dontenum)) return JSI_OK; v = Jsi_TreeValueGet(hPtr); if ((ow->quote&JSI_OUTPUT_JSON) && v && v->vt == JSI_VT_UNDEF) return JSI_OK; str = Jsi_TreeKeyGet(hPtr); char *cp = Jsi_DSValue(dStr); len = Jsi_DSLength(dStr); if (len>=2 && cp[len-2] != '{') Jsi_DSAppend(dStr, ", ", NULL); if (((ow->quote&JSI_OUTPUT_JSON) == 0 || (ow->quote&JSI_JSON_STRICT) == 0) && IsAlnum(str) && !IsKeyword(tree->interp, str)) Jsi_DSAppend(dStr, str, NULL); else /* JSON/spaces, etc requires quoting the name. */ Jsi_DSAppend(dStr, "\"", str, "\"", NULL); Jsi_DSAppend(dStr, ":", NULL); ow->depth++; jsiValueGetString(tree->interp, v, dStr, ow); ow->depth--; return JSI_OK; }
void Jsi_DString_SelfTest() { { Jsi_DString d1 = {}, d2 = {"Here is"}; Jsi_DSAppend(&d2 ," your score: ", NULL); Jsi_DSPrintf(&d2, " -> %d/%d", 99, 100); char *cp = Jsi_DSValue(&d2); puts(cp); // "Here is your score: -> 99/100" Jsi_DSAppend(&d1, cp, NULL); Jsi_DSFree(&d1); Jsi_DSFree(&d2); } { Jsi_DString d = {};; Jsi_DSPrintf(&d , "%0300d", 1); // Malloc Jsi_DSSetLength(&d, 0); Jsi_DSPrintf(&d , "%0300d", 1); // No-malloc Jsi_DSFree(&d); Jsi_DSPrintf(&d , "%0300d", 1); // Malloc Jsi_DSFree(&d); } { Jsi_DString d; Jsi_DSInit(&d); Jsi_DSAppend(&d , " some stuff: ", NULL); Jsi_DSFree(&d); } { JSI_DSTRING_VAR(dPtr,301); Jsi_DSPrintf(dPtr , "%0300d", 1); // No-malloc Jsi_DSSetLength(dPtr, 0); Jsi_DSPrintf(dPtr , "%0300d", 1); // No-malloc Jsi_DSFree(dPtr); Jsi_DSPrintf(dPtr , "%0400d", 1); // Malloc Jsi_DSFree(dPtr); } }
static int JsiCleanupChildren(Jsi_Interp *interp, int numPids, pidtype *pidPtr, fdtype errorId, Jsi_DString *dStr, Jsi_DString *cStr, int *code) { struct WaitInfoTable *table = jsi_ExecCmdData(interp); int result = JSI_OK; int i, exitCode = 256; char buf[1000]; Jsi_DString sStr; Jsi_DSInit(&sStr); for (i = 0; i < numPids; i++) { int waitStatus = 0; if (cStr) { if (i==0) Jsi_DSAppend(cStr, (Jsi_DSLength(cStr)>1?", ":""), "children: [", NULL); else Jsi_DSAppend(&sStr, ", ", NULL); } if (JsiWaitForProcess(table, pidPtr[i], &waitStatus) != JSI_BAD_PID) { // if (JsiCheckWaitStatus(interp, pidPtr[i], waitStatus, dStr?&sStr:0) != JSI_OK) //result = JSI_ERROR; // TODO: we don't error out on non-zero return code. Find way to return it. int es = WEXITSTATUS(waitStatus); if (WIFEXITED(waitStatus)) { if (i==0) exitCode = es; else if (es>exitCode) exitCode = es; } if (cStr) { pidtype pid = pidPtr[i]; if (WIFEXITED(waitStatus)) sprintf(buf, "{type:\"exit\", pid:%ld, exitCode:%d}", (long)pid, es); else sprintf(buf, "{type:\"%s\", pid:%ld, signal: %d}", (WIFSIGNALED(waitStatus) ? "killed" : "suspended"), (long)pid, WTERMSIG(waitStatus)); Jsi_DSAppend(&sStr, buf, NULL); } } } if (i>0 && cStr) { if (exitCode != 256) sprintf(buf, ", exitCode: %d", exitCode); Jsi_DSAppend(cStr, Jsi_DSValue(&sStr), "]", buf, NULL); } Jsi_DSFree(&sStr); Jsi_Free(pidPtr); /* * Read the standard error file. If there's anything there, * then add the file's contents to the result * string. */ if (errorId != JSI_BAD_FD) { JsiRewindFd(errorId); if (JsiAppendStreamToString(interp, errorId, dStr) != JSI_OK) { result = JSI_ERROR; } } *code = exitCode; return result; }
static char *get_inputline(int istty, char *prompt) { char *res; #ifdef HAVE_READLINE if (istty) { res = readline(prompt); if (res && *res) add_history(res); return res; } #endif int done = 0; char bbuf[BUFSIZ]; Jsi_DString dStr = {}; if (istty) fputs(prompt, stdout); fflush(stdout); while (!done) { /* Read a line. */ bbuf[0] = 0; if (fgets(bbuf, sizeof(bbuf), stdin) == NULL) return NULL; Jsi_DSAppend(&dStr, bbuf, NULL); if (strlen(bbuf) < (sizeof(bbuf)-1) || bbuf[sizeof(bbuf)-1] == '\n') break; } res = strdup(Jsi_DSValue(&dStr)); Jsi_DSFree(&dStr); return res; }
void jsi_UserObjToName(Jsi_Interp *interp, Jsi_UserObj *uobj, Jsi_DString *dStr) { const char * uname = "userdata"; char ubuf[50]; Jsi_UserObjReg *reg = uobj->reg; uname = reg->name; sprintf(ubuf, "%d", uobj->idx); Jsi_DSAppend(dStr, "#", uname, "_", ubuf, NULL); }
static const char *jsi_TildePath(Jsi_Interp *interp, const char* path, Jsi_DString *dStr) { if (*path != '~') return path; const char *homedir = jsi_GetHomeDir(interp); if (!homedir) return path; Jsi_DSAppend(dStr, homedir, path[1] == '/' ? "" : "/", path+1, NULL); return Jsi_DSValue(dStr); }
/* Simple implementation of exec with system(). Returns return code. * The system() call *may* do command line redirection, etc. * The standard output is not available. * Can't redirect filehandles. */ int Jsi_SysExecCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_DString *rStr) { int i, j, len, rc = 0; Jsi_DString dStr, eStr; len = Jsi_ValueGetLength(interp, args); Jsi_DSInit(rStr); if (len<=0) return 0; Jsi_DSInit(&dStr); #ifdef __WIN32 Jsi_DSAppendLen(&dStr, "\"", 1); #endif for (i=0; i<len; i++) { Jsi_Value *v = Jsi_ValueArrayIndex(interp, args, i); const char *arg = Jsi_ValueGetDString(interp, v, &eStr, 0); if (i>1) Jsi_DSAppendLen(&dStr, " ", 1); if (strpbrk(arg, "\\\" ") == NULL) { Jsi_DSAppend(&dStr, arg, NULL); } else { Jsi_DSAppendLen(&dStr, "\"", 1); for (j = 0; j < len; j++) { if (arg[j] == '\\' || arg[j] == '"') { Jsi_DSAppendLen(&dStr, "\\", 1); } Jsi_DSAppendLen(&dStr, &arg[j], 1); } Jsi_DSAppendLen(&dStr, "\"", 1); } Jsi_DSFree(&eStr); } #ifdef __WIN32 Jsi_DSAppendLen(&dStr, "\"", 1); #endif rc = system(Jsi_DSValue(&dStr)); Jsi_DSFree(&dStr); return rc; }
/* Collect and execute code from stdin. The first byte of flags are passed to Jsi_ValueGetDString(). */ int Jsi_Interactive(Jsi_Interp* interp, int flags) { int rc = 0, done = 0, len, quote = (flags & 0xff), istty = 1; char *prompt = "# ", *buf; Jsi_DString dStr; Jsi_DSInit(&dStr); #ifndef __WIN32 istty = isatty(fileno(stdin)); #else istty = _isatty(_fileno(stdin)); #endif #ifdef HAVE_READLINE Jsi_DString dHist = {}; char *hist = NULL; if(interp->noreadline == 0 && !interp->parent) { hist = Jsi_NormalPath(interp, "~/.jsish_history", &dHist); if (hist) read_history(hist); } #endif interp->level++; while (done==0 && interp->exited==0) { buf = get_inputline(istty, prompt); if (buf) { Jsi_DSAppend(&dStr, buf, NULL); free(buf); } else { done = 1; } len = Jsi_DSLength(&dStr); if (done && len == 0) break; buf = Jsi_DSValue(&dStr); if (done == 0 && (!balanced(buf))) { prompt = "> "; if (len<5) break; continue; } prompt = "# "; while ((len = Jsi_Strlen(buf))>0 && (isspace(buf[len-1]))) buf[len-1] = 0; if (buf[0] == 0) continue; /* Convenience: add semicolon to "var" statements (required by parser). */ if (strncmp(buf,"var ", 4) == 0 && strchr(buf, '\n')==NULL && strchr(buf, ';')==NULL) strcat(buf, ";"); rc = Jsi_EvalString(interp, buf, JSI_EVAL_RETURN); if (interp->exited) break; if (rc == 0) { if (interp->ret.vt != JSI_VT_UNDEF || interp->noUndef==0) { Jsi_DString eStr = {}; fputs(Jsi_ValueGetDString(interp,&interp->ret, &eStr, quote), stdout); Jsi_DSFree(&eStr); fputs("\n", stdout); } } else if (!interp->exited) { fputs("ERROR\n", stderr); } Jsi_DSSetLength(&dStr, 0); len = 0; } interp->level--; #ifdef HAVE_READLINE if (hist) { stifle_history(100); write_history(hist); } Jsi_DSFree(&dHist); #endif Jsi_DSFree(&dStr); if (interp->exited && interp->level <= 0) { rc = interp->exitCode; Jsi_InterpDelete(interp); } return rc; }
/* Format value into dStr. Toplevel caller does init/free. */ static void jsiValueGetString(Jsi_Interp *interp, Jsi_Value* v, Jsi_DString *dStr, objwalker *owPtr) { char buf[100], *str; if (owPtr->depth > interp->maxDepth) { Jsi_LogError("recursive ToString"); return; } int quote = owPtr->quote; int isjson = owPtr->quote&JSI_OUTPUT_JSON; double num; switch(v->vt) { case JSI_VT_UNDEF: Jsi_DSAppend(dStr, "undefined", NULL); return; case JSI_VT_NULL: Jsi_DSAppend(dStr, "null", NULL); return; case JSI_VT_VARIABLE: Jsi_DSAppend(dStr, "variable", NULL); return; case JSI_VT_BOOL: Jsi_DSAppend(dStr, (v->d.val ? "true":"false"), NULL); return; case JSI_VT_NUMBER: num = v->d.num; outnum: if (jsi_is_integer(num)) { sprintf(buf, "%d", (int)num); Jsi_DSAppend(dStr, buf, NULL); } else if (jsi_is_wide(num)) { sprintf(buf, "%Ld", (Jsi_Wide)num); Jsi_DSAppend(dStr, buf, NULL); } else if (jsi_ieee_isnormal(num)) { sprintf(buf, "%" JSI_NUMGFMT, num); Jsi_DSAppend(dStr, buf, NULL); } else if (jsi_ieee_isnan(num)) { Jsi_DSAppend(dStr, "NaN", NULL); } else { int s = jsi_ieee_infinity(num); if (s > 0) Jsi_DSAppend(dStr, "+Infinity", NULL); else if (s < 0) Jsi_DSAppend(dStr, "-Infinity", NULL); else Jsi_LogBug("Ieee function problem"); } return; case JSI_VT_STRING: str = v->d.s.str; outstr: if (!quote) { Jsi_DSAppend(dStr, str, NULL); return; } Jsi_DSAppend(dStr,"\"", NULL); while (*str) { if ((*str == '\'' && (!(isjson))) || *str == '\"' || (*str == '\n' && (!(owPtr->quote&JSI_OUTPUT_NEWLINES))) || *str == '\r' || *str == '\t' || *str == '\f' || *str == '\b' ) { char pcp[2]; *pcp = *str; pcp[1] = 0; Jsi_DSAppendLen(dStr,"\\", 1); switch (*str) { case '\r': *pcp = 'r'; break; case '\n': *pcp = 'n'; break; case '\t': *pcp = 't'; break; case '\f': *pcp = 'f'; break; case '\b': *pcp = 'b'; break; } Jsi_DSAppendLen(dStr,pcp, 1); } else if (!isprint(*str)) /* TODO: encode */ if (isjson) { char ubuf[10]; sprintf(ubuf, "\\u00%.02x", (unsigned char)*str); Jsi_DSAppend(dStr,ubuf, NULL); //Jsi_DSAppendLen(dStr,".", 1); } else Jsi_DSAppendLen(dStr,str, 1); else Jsi_DSAppendLen(dStr,str, 1); str++; } Jsi_DSAppend(dStr,"\"", NULL); return; case JSI_VT_OBJECT: { Jsi_Obj *o = v->d.obj; switch(o->ot) { case JSI_OT_BOOL: Jsi_DSAppend(dStr, (o->d.val ? "true":"false"), NULL); return; case JSI_OT_NUMBER: num = o->d.num; goto outnum; return; case JSI_OT_STRING: str = o->d.s.str; goto outstr; return; case JSI_OT_FUNCTION: { Jsi_FuncObjToString(interp, o, dStr); return; } case JSI_OT_REGEXP: Jsi_DSAppend(dStr, o->d.robj->pattern, NULL); return; case JSI_OT_USEROBJ: jsi_UserObjToName(interp, o->d.uobj, dStr); return; case JSI_OT_ITER: Jsi_DSAppend(dStr, "*ITER*", NULL); return; } if (o->isArray) { Jsi_Value *nv; int i, len = o->arrCnt; if (!o->arr) len = Jsi_ValueGetLength(interp, v); Jsi_DSAppend(dStr,"[ ", NULL); for (i = 0; i < len; ++i) { nv = Jsi_ValueArrayIndex(interp, v, i); if (i) Jsi_DSAppend(dStr,", ", NULL); if (nv) jsiValueGetString(interp, nv, dStr, owPtr); else Jsi_DSAppend(dStr, "undefined", NULL); } Jsi_DSAppend(dStr," ]", NULL); } else { Jsi_DSAppend(dStr,"{ ", NULL); owPtr->depth++; Jsi_TreeWalk(o->tree, _object_get_callback, owPtr, 0); owPtr->depth--; Jsi_DSAppend(dStr," }", NULL); } return; } default: Jsi_LogBug("Unexpected value type: %d\n", v->vt); } }
/* * The main exec command */ int jsi_execCmd(Jsi_Interp *interp, Jsi_Value *args, Jsi_DString *dStr, Jsi_DString *cStr, int *code) { fdtype outputId; /* File id for output pipe. -1 * means command overrode. */ fdtype errorId; /* File id for temporary file * containing error output. */ pidtype *pidPtr; int numPids, result, argc; argc = Jsi_ValueGetLength(interp, args); /* * See if the command is to be run in background; if so, create * the command, detach it, and return. */ if (argc > 1 && Jsi_Strcmp(Jsi_ValueArrayIndexToStr(interp, args, argc-1, NULL), "&") == 0) { int i; argc--; numPids = JsiCreatePipeline(interp, args, argc, &pidPtr, NULL, NULL, NULL); if (numPids < 0) { return JSI_ERROR; } if (cStr) { /* The return value is a list of the pids */ Jsi_DSAppend(cStr,(Jsi_DSLength(cStr)>1?", ":""), "pids : [", NULL); for (i = 0; i < numPids; i++) { char ibuf[100]; sprintf(ibuf, "%s%ld", (i?",":""), (long)pidPtr[i]); Jsi_DSAppend(cStr, ibuf, NULL); } Jsi_DSAppend(cStr, "]", NULL); } JsiDetachPids(interp, numPids, pidPtr); Jsi_Free(pidPtr); return JSI_OK; } /* * Create the command's pipeline. */ numPids = JsiCreatePipeline(interp, args, argc, &pidPtr, NULL, &outputId, &errorId); if (numPids < 0) { return JSI_ERROR; } /* * Read the child's output (if any) and put it into the result. */ result = JSI_OK; if (outputId != JSI_BAD_FD) { result = JsiAppendStreamToString(interp, outputId, dStr); if (result != JSI_OK && cStr) Jsi_DSAppend(cStr, (Jsi_DSLength(cStr)>1?", ":""), "errstr: \"error reading from output pipe\"", NULL); } if (JsiCleanupChildren(interp, numPids, pidPtr, errorId, dStr, cStr, code) != JSI_OK) { result = JSI_ERROR; } return result; }
int main(int argc, char **argv) { int rc = JSI_OK, jsFound = 0; Jsi_Interp* interp = Jsi_InterpCreate(NULL, argc, argv, 0); Jsi_InterpOnDelete(interp, &InterpDelete); #ifndef NO_JAZ /* Mount zip at end of executable */ Jsi_Value *v = Jsi_Executable(interp); const char *exeFile = Jsi_ValueString(interp, v, NULL); if (argc != 2 || Jsi_Strcmp(argv[1], "--nozvfs")) { rc = Jsi_ExecZip(interp, exeFile, JSI_ZVFS_DIR, &jsFound); if (rc >= 0) { if (!jsFound) { #if (!defined(JSI_OMIT_FILESYS)) && (!defined(JSI_OMIT_ZVFS)) fprintf(stderr, "warning: no main.jsi or jsiIndex.jsi\n"); #endif } if (deleted) exit(exitCode); else if (rc != JSI_OK) { fprintf(stderr, "Error\n"); exit(1); } } } #endif #ifdef USER_EXTENSION extern int USER_EXTENSION(Jsi_Interp *interp); if (USER_EXTENSION (interp) != JSI_OK) { fprintf(stderr, "extension load failed"); exit(1); } #endif Jsi_ShiftArgs(interp); if (argc == 1) { rc = Jsi_Interactive(interp, JSI_OUTPUT_QUOTE|JSI_OUTPUT_NEWLINES); } else { if (argc == 2 && (Jsi_Strcmp(argv[1], "--help")==0 || Jsi_Strcmp(argv[1], "-h" )==0)) { puts("usage: jsi ?--help | -e SCRIPT | FILE arg arg ...?"); exit(0); } if (argc == 2 && (Jsi_Strcmp(argv[1], "--version")==0 || Jsi_Strcmp(argv[1], "-v" )==0)) { char str[200] = "\n"; Jsi_Channel chan = Jsi_Open(interp, Jsi_ValueNewStringKey(interp, "/zvfs/lib/sourceid.txt"), "r"); if (chan) Jsi_Read(chan, str, sizeof(str)); printf("%d.%d.%d (%" JSI_NUMGFMT ") %s", JSI_VERSION_MAJOR, JSI_VERSION_MINOR, JSI_VERSION_RELEASE, Jsi_Version(), str); exit(0); } if (argc > 2 && (Jsi_Strcmp(argv[1], "--invoke")==0 || Jsi_Strcmp(argv[1], "-i" )==0)) { Jsi_DString dStr = {}; Jsi_DSAppend(&dStr, "jsi_invokeCmd(\"", argv[2], "\",console.args.slice(1));", NULL); rc = Jsi_EvalString(interp, Jsi_DSValue(&dStr), JSI_EVAL_NOSKIPBANG); Jsi_DSFree(&dStr); } else if (argc == 3 && (Jsi_Strcmp(argv[1], "--eval")==0 || Jsi_Strcmp(argv[1], "-e" )==0)) rc = Jsi_EvalString(interp, argv[2], JSI_EVAL_NOSKIPBANG); else { Jsi_Value *vf = NULL; const char *ext = strrchr(argv[1], '.'); /* Support running "main.jsi" from a zip file. */ if (ext && (strcmp(ext,".zip")==0 ||strcmp(ext,".jsz")==0 ) ) { rc = Jsi_ExecZip(interp, argv[1], NULL, &jsFound); if (rc<0) { fprintf(stderr, "zip mount failed\n"); exit(1); } if (!(jsFound&JSI_ZIP_MAIN)) { fprintf(stderr, "main.jsi not found\n"); exit(1); } } else { if (argc>1) vf = Jsi_ValueNewStringKey(interp, argv[1]); rc = Jsi_EvalFile(interp, vf, JSI_EVAL_ARGV0|JSI_EVAL_INDEX); } } if (deleted) exit(exitCode); if (rc == 0) { /* 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 fputs("ERROR\n", stderr); } if (!deleted) Jsi_InterpDelete(interp); return rc; }