Ejemplo n.º 1
0
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);
}
Ejemplo n.º 2
0
char *
Jsi_DSSet(Jsi_DString *dsPtr, const char *str)
{
    Jsi_DSSetLength(dsPtr, 0);
    if (str)
        Jsi_DSAppendLen(dsPtr, str, -1);
    return Jsi_DSValue(dsPtr);
}
Ejemplo n.º 3
0
/* 
 * 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;
}
Ejemplo n.º 4
0
/* 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);
}
Ejemplo n.º 5
0
/*
 * 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;
}
Ejemplo n.º 6
0
/**
 * 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;
}
Ejemplo n.º 7
0
/* 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;
}
Ejemplo n.º 8
0
/* 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);
    }
}
Ejemplo n.º 9
0
/* 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;
}
Ejemplo n.º 10
0
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);
}
Ejemplo n.º 11
0
/**
 * 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;
}