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; }
/* Format value into dStr. Toplevel caller does init/free. */ const char* Jsi_ValueGetDString(Jsi_Interp *interp, Jsi_Value* v, Jsi_DString *dStr, int quote) { objwalker ow; ow.quote = quote; ow.depth = 0; ow.dStr = dStr; Jsi_DSInit(dStr); jsiValueGetString(interp, v, dStr, &ow); 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; }
void Jsi_Puts(Jsi_Interp* interp, Jsi_Value *v, int flags) { int quote = (flags&JSI_OUTPUT_QUOTE); int iserr = (flags&JSI_OUTPUT_STDERR); Jsi_DString dStr = {}; const char *cp = Jsi_ValueString(interp, v, 0); if (cp) { fprintf((iserr?stderr:stdout),"%s", cp); return; } Jsi_DSInit(&dStr); Jsi_ValueGetDString(interp, v, &dStr, quote); fprintf((iserr?stderr:stdout),"%s",Jsi_DSValue(&dStr)); Jsi_DSFree(&dStr); return; }
/* 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; }
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; }