Jsi_Obj *jsi_UserObjFromName(Jsi_Interp *interp, const char *name) { if (*name != '#') return NULL; char *cp = strrchr(name, '_'); if (cp==0 || !*cp) return NULL; int id = atoi(cp+1); Jsi_DString dStr = {}; Jsi_DSAppendLen(&dStr, name+1, cp-name-1); Jsi_HashEntry *hPtr = Jsi_HashEntryFind(interp->userdataTbl, Jsi_DSValue(&dStr)); Jsi_DSFree(&dStr); UserObjReg *rdata = Jsi_HashValueGet(hPtr); if (!rdata) return NULL; Jsi_Hash *tPtr = rdata->hashPtr; if (tPtr==0) return NULL; hPtr = Jsi_HashEntryFind(tPtr, (void*)id); if (!hPtr) return NULL; return Jsi_HashValueGet(hPtr); }
char * Jsi_DSSet(Jsi_DString *dsPtr, const char *str) { Jsi_DSSetLength(dsPtr, 0); if (str) Jsi_DSAppendLen(dsPtr, str, -1); return Jsi_DSValue(dsPtr); }
/* * Calls Jsi_DSAppendLen for each string value argument, passing in -1 for the length. * Each string is assumed to be null terminated and the final argument must be a NULL. * RETURNS: The string starting at the first appended character. */ char * Jsi_DSAppend(Jsi_DString *dsPtr, const char *str, ...) { va_list argList; char *elem; if (DSNotInit(dsPtr)) InitStr(dsPtr); int len = dsPtr->len; if (!str) return dsPtr->str; Jsi_DSAppendLen(dsPtr, str, -1); va_start(argList, str); while ((elem = va_arg(argList, char *)) != NULL) { Jsi_DSAppendLen(dsPtr, elem, -1); } va_end(argList); return dsPtr->str+len; }
/* Initialize an uninitialized string. */ static void InitStr(Jsi_DString *dsPtr) { char *str = dsPtr->str; dsPtr->len = 0; if (dsPtr->staticSize<=0) dsPtr->staticSize = JSI_DSTRING_STATIC_SIZE; dsPtr->spaceAvl = dsPtr->staticSize; dsPtr->staticSpace[0] = 0; dsPtr->str = dsPtr->staticSpace; if (str) Jsi_DSAppendLen(dsPtr, str, -1); }
/* * Format output and append to the end of dsPtr. * RETURNS: The string starting at the first appended character. */ char * Jsi_DSPrintf(Jsi_DString *dsPtr, const char *fmt, ...) { va_list argList; #ifndef JSI_PRINTF_BUFSIZ #define JSI_PRINTF_BUFSIZ BUFSIZ #endif char buf[JSI_PRINTF_BUFSIZ], *bPtr = buf; int n, bsiz = sizeof(buf), needAppend = 1; if (DSNotInit(dsPtr)) InitStr(dsPtr); int len = dsPtr->len; char *send = dsPtr->str + len; uint avail = (dsPtr->spaceAvl - len); if (avail >= sizeof(buf)) { /* Format directly into string. */ bPtr = dsPtr->str+len; bsiz = avail; needAppend = 0; } va_start(argList, fmt); n = vsnprintf(bPtr, bsiz, fmt, argList); if (n<0 || (n+len)>JSI_MAX_ALLOC_BUF) { LogError("vsnprintf error: rc=%d, len=%d", n, len); va_end(argList); return send; } if (n >= bsiz) { int m = len+n+1; if (Jsi_DSSetLength(dsPtr, m) < m) { va_start(argList, fmt); return send; } m = vsnprintf(dsPtr->str+len, len+1, fmt, argList); if (m != n) { LogError("len mismatch: %d != %d", m, n); va_end(argList); return send; } } else if (needAppend) { Jsi_DSAppendLen(dsPtr, buf, n); } va_end(argList); return send; }
/** * Read from 'fd', append the data to strObj and close 'fd'. * Returns JSI_OK if OK, or JSI_ERROR on error. */ static int JsiAppendStreamToString(Jsi_Interp *interp, fdtype fd, Jsi_DString *dStr) { char buf[256]; FILE *fh = JsiFdOpenForRead(fd); if (fh == NULL) { return JSI_ERROR; } while (1) { int retval = fread(buf, 1, sizeof(buf), fh); if (retval > 0) { Jsi_DSAppendLen(dStr, buf, retval); } if (retval != sizeof(buf)) { break; } } Jsi_RemoveTrailingNewline(dStr); fclose(fh); return JSI_OK; }
/* 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; }
/* 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); } }
/* Lookup a name, eg. "a[b].c a.b.c a[b][c] a.b[c] a["b"].c a[1].c */ Jsi_Value *Jsi_NameLookup(Jsi_Interp *interp, const char *name) { int cnt = 0, len, isq; char *nam = (char*)name, *cp, *cp2, *ocp, *kstr; Jsi_Value *v = NULL, tv = VALINIT, nv = VALINIT, key = VALINIT; Jsi_DString dStr = {}; cp2 = strchr(nam,'['); cp = strchr(nam, '.'); if (cp2 && (cp==0 || cp2<cp)) cp = cp2; if (!cp) return Jsi_VarLookup(interp, nam); Jsi_DSSetLength(&dStr, 0); Jsi_DSAppendLen(&dStr, nam, cp-nam); v = Jsi_VarLookup(interp, Jsi_DSValue(&dStr)); if (!v) goto bail; while (cnt++ < 1000) { ocp = cp; nam = cp+1; isq = 0; if (*cp == '[') { cp = FindEndB(cp+1); /* handle [] in strings. */ if (!cp) goto bail; len = cp-nam; cp++; if (len>=2 && ((nam[0] == '\"' && nam[len-1] == '\"') || (nam[0] == '\'' && nam[len-1] == '\''))) { nam += 1; len -= 2; isq = 1; } } else if (*cp == '.') { cp2 = strchr(nam,'['); cp = strchr(nam, '.'); if (cp2 && (cp==0 || cp2<cp)) cp = cp2; len = (cp ? cp-nam : strlen(nam)); } else { goto bail; } Jsi_DSSetLength(&dStr, 0); Jsi_DSAppendLen(&dStr, nam, len); kstr = Jsi_DSValue(&dStr); if (*ocp == '[' && isq == 0 && isdigit(kstr[0]) && Jsi_ValueIsArray(interp, v)) { int nn; if (Jsi_GetInt(interp, kstr, &nn, 0) != JSI_OK) goto bail; v = Jsi_ValueArrayIndex(interp, v, nn); if (!v) goto bail; } else if (*ocp == '[' && isq == 0) { Jsi_Value *kv = Jsi_VarLookup(interp, kstr); if (!kv) goto bail; jsi_ValueSubscriptLen(interp, v, kv, &nv, 1); goto keyon; } else { Jsi_ValueMakeStringKey(interp, &key, kstr); jsi_ValueSubscriptLen(interp, v, &key, &nv, 1); keyon: if (nv.vt == JSI_VT_UNDEF) goto bail; else { tv = nv; v = &tv; } } if (cp == 0 || *cp == 0) break; } //Jsi_ValueReset(interp, &ret); Jsi_DSFree(&dStr); if (v && v == &tv) { v = Jsi_ValueNew(interp); *v = tv; } return v; bail: Jsi_DSFree(&dStr); return NULL; }
char *JsiWinBuildCommandLine(Jsi_Interp *interp, char **argv, Jsi_DString *dStr) { char *start, *special; int quote, i; Jsi_DSInit(dStr); for (i = 0; argv[i]; i++) { if (i > 0) { Jsi_DSAppendLen(dStr, " ", 1); } if (argv[i][0] == '\0') { quote = JSI_OUTPUT_QUOTE; } else { quote = 0; for (start = argv[i]; *start != '\0'; start++) { if (isspace(UCHAR(*start))) { quote = JSI_OUTPUT_QUOTE; break; } } } if (quote) { Jsi_DSAppendLen(dStr, "\"" , 1); } start = argv[i]; for (special = argv[i]; ; ) { if ((*special == '\\') && (special[1] == '\\' || special[1] == '"' || (quote && special[1] == '\0'))) { Jsi_DSAppendLen(dStr, start, special - start); start = special; while (1) { special++; if (*special == '"' || (quote && *special == '\0')) { /* * N backslashes followed a quote -> insert * N * 2 + 1 backslashes then a quote. */ Jsi_DSAppendLen(dStr, start, special - start); break; } if (*special != '\\') { break; } } Jsi_DSAppendLen(dStr, start, special - start); start = special; } if (*special == '"') { if (special == start) { Jsi_DSAppendLen(dStr, "\"", 1); } else { Jsi_DSAppendLen(dStr, start, special - start); } Jsi_DSAppendLen(dStr, "\\\"", 2); start = special + 1; } if (*special == '\0') { break; } special++; } Jsi_DSAppendLen(dStr, start, special - start); if (quote) { Jsi_DSAppendLen(dStr, "\"", 1); } } return Jsi_DSValue(dStr); }
/** * Apply the printf-like format in fmtObjPtr with the given arguments. * * Returns a new object with zero reference count if OK, or NULL on error. */ int Jsi_FormatString(Jsi_Interp *interp, Jsi_Value *args, Jsi_DString *dStr) { const char *span, *format, *formatEnd, *msg; int numBytes = 0, argIndex = 1, gotXpg = 0, gotSequential = 0, argCnt; static const char * const mixedXPG = "cannot mix \"%\" and \"%n$\" conversion specifiers"; static const char * const badIndex[2] = { "not enough arguments for all format specifiers", "\"%n$\" argument index out of range" }; int formatLen; Jsi_Value *v; /* A single buffer is used to store numeric fields (with sprintf()) * This buffer is allocated/reallocated as necessary */ char stat_buf[100], *num_buffer = stat_buf; int num_buffer_size = sizeof(stat_buf); argCnt = Jsi_ValueGetLength(interp, args); if (argCnt<1) { msg = "missing format"; goto errorMsg; } format = Jsi_ValueArrayIndexToStr(interp, args,0, &formatLen); span = format; formatEnd = format + formatLen; Jsi_DSInit(dStr); while (format != formatEnd) { char *end; int gotMinus, sawFlag; int gotPrecision, useShort, useLong; long width, precision; int newXpg; int ch; int step; int doubleType; char pad = ' '; char spec[2*JSI_INTEGER_SPACE + 12]; char *p; int formatted_chars; int formatted_bytes; const char *formatted_buf = NULL; step = jsi_utf8_tounicode(format, &ch); format += step; if (ch != '%') { numBytes += step; continue; } if (numBytes) { Jsi_DSAppendLen(dStr, span, numBytes); numBytes = 0; } /* * Saw a % : process the format specifier. * * Step 0. Handle special case of escaped format marker (i.e., %%). */ step = jsi_utf8_tounicode(format, &ch); if (ch == '%') { span = format; numBytes = step; format += step; continue; } /* * Step 1. XPG3 position specifier */ newXpg = 0; if (isdigit(ch)) { int position = strtoul(format, &end, 10); if (*end == '$') { newXpg = 1; argIndex = position - 1; format = end + 1; step = jsi_utf8_tounicode(format, &ch); } } if (newXpg) { if (gotSequential) { msg = mixedXPG; goto errorMsg; } gotXpg = 1; } else { if (gotXpg) { msg = mixedXPG; goto errorMsg; } gotSequential = 1; } if ((argIndex < 0) || (argIndex >= argCnt)) { msg = badIndex[gotXpg]; goto errorMsg; } /* * Step 2. Set of flags. Also build up the sprintf spec. */ p = spec; *p++ = '%'; gotMinus = 0; sawFlag = 1; do { switch (ch) { case '-': gotMinus = 1; break; case '0': pad = ch; break; case ' ': case '+': case '#': break; default: sawFlag = 0; continue; } *p++ = ch; format += step; step = jsi_utf8_tounicode(format, &ch); } while (sawFlag); /* * Step 3. Minimum field width. */ width = 0; if (isdigit(ch)) { width = strtoul(format, &end, 10); format = end; step = jsi_utf8_tounicode(format, &ch); } else if (ch == '*') { if (argIndex >= argCnt - 1) { msg = badIndex[gotXpg]; goto errorMsg; } v = Jsi_ValueArrayIndex(interp, args, argIndex); if (Jsi_GetLongFromValue(interp, v, &width) != JSI_OK) { goto error; } if (width < 0) { width = -width; if (!gotMinus) { *p++ = '-'; gotMinus = 1; } } argIndex++; format += step; step = jsi_utf8_tounicode(format, &ch); } /* * Step 4. Precision. */ gotPrecision = precision = 0; if (ch == '.') { gotPrecision = 1; format += step; step = jsi_utf8_tounicode(format, &ch); } if (isdigit(ch)) { precision = strtoul(format, &end, 10); format = end; step = jsi_utf8_tounicode(format, &ch); } else if (ch == '*') { if (argIndex >= argCnt - 1) { msg = badIndex[gotXpg]; goto errorMsg; } v = Jsi_ValueArrayIndex(interp, args, argIndex); if (Jsi_GetLongFromValue(interp, v, &precision) != JSI_OK) { goto error; } /* * TODO: Check this truncation logic. */ if (precision < 0) { precision = 0; } argIndex++; format += step; step = jsi_utf8_tounicode(format, &ch); } /* * Step 5. Length modifier. */ useShort = 0; useLong = 0; if (ch == 'h') { useShort = 1; format += step; step = jsi_utf8_tounicode(format, &ch); } else if (ch == 'l') { useLong = 1; format += step; step = jsi_utf8_tounicode(format, &ch); if (ch == 'l') { format += step; step = jsi_utf8_tounicode(format, &ch); } } format += step; span = format; /* * Step 6. The actual conversion character. */ if (ch == 'i') { ch = 'd'; } doubleType = 0; /* Each valid conversion will set: * formatted_buf - the result to be added * formatted_chars - the length of formatted_buf in characters * formatted_bytes - the length of formatted_buf in bytes */ switch (ch) { case '\0': msg = "format string ended in middle of field specifier"; goto errorMsg; case 's': { v = Jsi_ValueArrayIndex(interp, args, argIndex); if (Jsi_GetStringFromValue(interp, v, &formatted_buf) != JSI_OK) goto error; formatted_bytes = formatted_chars = Jsi_Strlen(formatted_buf); if (gotPrecision && (precision < formatted_chars)) { /* Need to build a (null terminated) truncated string */ formatted_chars = precision; formatted_bytes = jsi_utf8_index(formatted_buf, precision); } break; } case 'c': { Jsi_Wide code; v = Jsi_ValueArrayIndex(interp, args, argIndex); if (Jsi_GetWideFromValue(interp, v, &code) != JSI_OK) { goto error; } /* Just store the value in the 'spec' buffer */ formatted_bytes = jsi_utf8_fromunicode(spec, code); formatted_buf = spec; formatted_chars = 1; break; } case 'e': case 'E': case 'f': case 'g': case 'G': doubleType = 1; /* fall through */ case 'd': case 'u': case 'o': case 'x': case 'X': { Jsi_Wide w; Jsi_Number d; int length; /* Fill in the width and precision */ if (width) { p += sprintf(p, "%ld", width); } if (gotPrecision) { p += sprintf(p, ".%ld", precision); } /* Now the modifier, and get the actual value here */ if (doubleType) { v = Jsi_ValueArrayIndex(interp, args, argIndex); if (Jsi_GetDoubleFromValue(interp, v, &d) != JSI_OK) { goto error; } length = MAX_FLOAT_WIDTH; } else { v = Jsi_ValueArrayIndex(interp, args, argIndex); if (Jsi_GetWideFromValue(interp, v, &w) != JSI_OK) { goto error; } length = JSI_INTEGER_SPACE; if (useShort) { *p++ = 'h'; if (ch == 'd') { w = (short)w; } else { w = (unsigned short)w; } } else { *p++ = 'l'; #ifdef HAVE_LONG_LONG if (useLong && sizeof(long long) == sizeof(Jsi_Wide)) { *p++ = 'l'; } #endif } } *p++ = (char) ch; *p = '\0'; /* Adjust length for width and precision */ if (width > length) { length = width; } if (gotPrecision) { length += precision; } /* Increase the size of the buffer if needed */ if (num_buffer_size < length + 1) { num_buffer_size = length + 1; num_buffer = Jsi_Realloc((num_buffer==stat_buf?NULL:num_buffer), num_buffer_size); } if (doubleType) { snprintf(num_buffer, length + 1, spec, d); } else { formatted_bytes = snprintf(num_buffer, length + 1, spec, w); } formatted_chars = formatted_bytes = strlen(num_buffer); formatted_buf = num_buffer; break; } default: { /* Just reuse the 'spec' buffer */ spec[0] = ch; spec[1] = '\0'; Jsi_LogError("bad field specifier \"%s\"", spec); goto error; } } if (!gotMinus) { while (formatted_chars < width) { Jsi_DSAppendLen(dStr, &pad, 1); formatted_chars++; } } Jsi_DSAppendLen(dStr, formatted_buf, formatted_bytes); while (formatted_chars < width) { Jsi_DSAppendLen(dStr, &pad, 1); formatted_chars++; } argIndex += gotSequential; } if (numBytes) { Jsi_DSAppendLen(dStr, span, numBytes); } if (num_buffer!=stat_buf) Jsi_Free(num_buffer); return JSI_OK; errorMsg: Jsi_LogError("%s", msg); error: Jsi_DSFree(dStr); if (num_buffer!=stat_buf) Jsi_Free(num_buffer); return JSI_ERROR; }