// Buffer-formatting siblings of printf — kept in their own translation
// unit so the shared writeXxx helpers don't have to take a function-
// pointer sink (indirect call cost on this target) and so adding the
// formatter to libc.c can't shift vprintf's branch distances out of
// range (per the strtol.c precedent).
//
// Functions:
//   int vsnprintf(char *buf, size_t n, const char *fmt, va_list ap);
//   int snprintf (char *buf, size_t n, const char *fmt, ...);
//   int vsprintf (char *buf, const char *fmt, va_list ap);
//   int sprintf  (char *buf, const char *fmt, ...);
//
// Format support:
//   conversions   %d %i %u %x %X %o %c %s %p %f %F %e %E %g %G %n %%
//   flags         - + (space) # 0
//   width         decimal or `*` (from va_arg int)
//   precision     .N or .*
//   length        hh, h, l, ll, j, z, t
//
// Floats are soft-double (double + float promote-to-double via va_arg);
// precision capped at 9 fractional digits.  Hex-float (%a / %A) is
// fully supported: IEEE-754 double bits decoded into 4 u16 words (no
// i64 shift libcalls), emitted as `0x1.{13-hex}p{signed-decimal}` with
// glibc-style trailing-zero stripping when precision is unspecified.
// Subnormals canonicalize as `0x0.{mantissa}p-1022`.  Inf/NaN parity
// across %f / %F / %g / %G / %e / %E / %a / %A.  Multibyte / wide-char
// specifiers (%lc, %ls) fall through and emit `%lc` literally.
//
// Return value: number of characters that would have been written had
// the buffer been unbounded (C99 vsnprintf semantics), not just the
// number actually written.  This lets callers detect truncation.
//
// Sink state lives in file-static globals (gCur/gEnd/gTotal) rather
// than a per-call context.  Single-threaded use only, but that matches
// the rest of this runtime.

typedef unsigned long size_t;
typedef __builtin_va_list  va_list;
#define va_start(ap, last) __builtin_va_start(ap, last)
#define va_arg(ap, ty)     __builtin_va_arg(ap, ty)
#define va_end(ap)         __builtin_va_end(ap)

// Unbounded sink sentinel used by sprintf/vsprintf.  Setting gEnd to
// `buf + 0xFFFE` looks innocuous but clang lowers the +0xFFFE to a
// `dec a; dec a` peephole (0xFFFE is -2 in 16-bit), giving gEnd =
// buf - 2 -- the `cur < end` bounds test then always fails.  Use the
// absolute top-of-bank sentinel instead.
#define SPRINTF_END_SENTINEL ((char *)0xFFFF)


static char   *gCur;
static char   *gEnd;
static size_t  gTotal;


static void emit(char c) {
    if (gCur < gEnd) {
        *gCur++ = c;
    }
    gTotal++;
}


static void emitStr(const char *p) {
    if (!p) {
        p = "(null)";
    }
    while (*p) {
        emit(*p++);
    }
}


// ---- Number-to-buffer helpers (reverse order, returns digit count) -----

// uint64 -> decimal digits in reverse order.  buf must be >= 20 bytes
// (UINT64_MAX = 18446744073709551615 = 20 digits).
static int u64ToDec(unsigned long long n, char *buf) {
    int i = 0;
    if (n == 0) {
        buf[i++] = '0';
        return i;
    }
    while (n > 0) {
        buf[i++] = (char)('0' + (n % 10ull));
        n /= 10ull;
    }
    return i;
}


// uint64 -> hex digits in reverse order.  Returns digit count.  Buf
// must be >= 16 bytes (UINT64_MAX = 16 hex digits).
static int u64ToHex(unsigned long long n, int upper, char *buf) {
    const char *digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
    int i = 0;
    if (n == 0) {
        buf[i++] = '0';
        return i;
    }
    while (n > 0) {
        buf[i++] = digits[n & 0xFull];
        n >>= 4;
    }
    return i;
}


// uint64 -> octal digits in reverse order.  Returns digit count.
static int u64ToOct(unsigned long long n, char *buf) {
    int i = 0;
    if (n == 0) {
        buf[i++] = '0';
        return i;
    }
    while (n > 0) {
        buf[i++] = (char)('0' + (n & 7ull));
        n >>= 3;
    }
    return i;
}


// Emit n copies of c (used for width / precision padding).
static void emitPad(int n, char c) {
    while (n-- > 0) emit(c);
}


// Emit a reversed buffer in forward order.
static void emitRev(const char *buf, int n) {
    while (n-- > 0) emit(buf[n]);
}


// ---- Integer formatting with full flag/width/prec/length surface ----

typedef struct {
    int leftAlign;   // '-'
    int signPlus;    // '+'
    int signSpace;   // ' '
    int altForm;     // '#'
    int zeroPad;     // '0'
    int width;
    int prec;        // -1 if unset
} Spec;


// Emit an integer with all the conversion flags.  `value` is the
// magnitude (always non-negative); `isNeg` says whether to prefix '-'.
// `base` is 8 / 10 / 16.  `upper` selects A-F vs a-f for base 16.
static void emitNumber(unsigned long long value,
                       int isSigned, int isNeg,
                       int base, int upper,
                       const Spec *s) {
    char buf[20];
    int len;
    if (base == 16) {
        len = u64ToHex(value, upper, buf);
    } else if (base == 8) {
        len = u64ToOct(value, buf);
    } else {
        len = u64ToDec(value, buf);
    }
    // Sign / alt-form prefix.
    char  prefix1 = 0;        // '-', '+', or ' '
    char  prefix2 = 0;        // 'x' / 'X' / '0' for # alt-form
    char  prefix0 = 0;        // '0' before the 'x' for hex alt
    if (isSigned) {
        if (isNeg)             prefix1 = '-';
        else if (s->signPlus)  prefix1 = '+';
        else if (s->signSpace) prefix1 = ' ';
    }
    if (s->altForm && base == 16 && value != 0ull) {
        prefix0 = '0';
        prefix2 = upper ? 'X' : 'x';
    } else if (s->altForm && base == 8 && (s->prec < 0 || s->prec < len + 1)) {
        // Octal alt-form: ensure leading 0.  Easiest: bump precision.
        if (buf[len - 1] != '0') {
            buf[len++] = '0';
        }
    }
    // Precision (min number of digits — left-pad with '0').
    int digitPad = 0;
    if (s->prec >= 0 && len < s->prec) {
        digitPad = s->prec - len;
    }
    int prefixLen = (prefix1 ? 1 : 0) + (prefix0 ? 1 : 0) + (prefix2 ? 1 : 0);
    int contentLen = prefixLen + digitPad + len;
    int fieldPad = s->width > contentLen ? s->width - contentLen : 0;
    // When zero-padding is requested AND no precision, the zero pad
    // counts toward digitPad (so '+' / sign goes first, then zeros,
    // then digits).  Precision specified disables zero pad per C99.
    if (s->zeroPad && !s->leftAlign && s->prec < 0) {
        digitPad += fieldPad;
        fieldPad = 0;
    }
    if (!s->leftAlign) {
        emitPad(fieldPad, ' ');
    }
    if (prefix1) emit(prefix1);
    if (prefix0) emit(prefix0);
    if (prefix2) emit(prefix2);
    emitPad(digitPad, '0');
    emitRev(buf, len);
    if (s->leftAlign) {
        emitPad(fieldPad, ' ');
    }
}


// Emit a string with width + precision honored (precision = max chars).
static void emitStrField(const char *p, const Spec *s) {
    if (!p) p = "(null)";
    int len = 0;
    while (p[len] && (s->prec < 0 || len < s->prec)) len++;
    int fieldPad = s->width > len ? s->width - len : 0;
    if (!s->leftAlign) emitPad(fieldPad, ' ');
    for (int i = 0; i < len; i++) emit(p[i]);
    if (s->leftAlign) emitPad(fieldPad, ' ');
}


// IEEE-754 double decoded into a sign bit + 11-bit exponent + four
// 16-bit mantissa words.  Mantissa is laid out LSB-first: m[0] is
// bits[15:0], m[1] bits[31:16], m[2] bits[47:32], m[3] bits[51:48]
// (only the low 4 bits of m[3] are used).  Reading the bits as 4 u16
// words avoids the >>52 / 12-bit-mask paths that drag i64 libcalls in.
#ifndef LLVM816_NO_FLOAT_PRINTF
typedef struct {
    unsigned short m[4];       // mantissa: low-to-high, m[3] only 4 LSBs
    unsigned short exp;        // 11-bit biased exponent (0..0x7FF)
    unsigned char  sign;       // 0 / 1
} DblBits;


static void decodeDouble(double v, DblBits *d) {
    unsigned short w[4];
    __builtin_memcpy(w, &v, 8);
    // Little-endian byte order: w[0] = bytes 0-1 (mantissa LSB),
    // w[3] = bytes 6-7 (sign + exp + mantissa MSB-nibble).
    d->m[0] = w[0];
    d->m[1] = w[1];
    d->m[2] = w[2];
    d->m[3] = (unsigned short)(w[3] & 0x000F);
    d->exp  = (unsigned short)((w[3] >> 4) & 0x07FF);
    d->sign = (unsigned char)((w[3] >> 15) & 1);
}


// If v is +/-Inf or NaN, emit the canonical glibc-style spelling and
// return 1.  Otherwise return 0 (caller continues with finite path).
// `upper` selects "INF"/"NAN" vs "inf"/"nan".  Width/left-align/space/
// '+' flags are honored exactly like glibc.
static int emitInfNan(const DblBits *d, int upper, const Spec *s) {
    if (d->exp != 0x7FF) {
        return 0;
    }
    int isNan = (d->m[0] | d->m[1] | d->m[2] | d->m[3]) != 0;
    const char *body = isNan ? (upper ? "NAN" : "nan")
                             : (upper ? "INF" : "inf");
    char prefix = 0;
    if (!isNan) {
        if (d->sign)          prefix = '-';
        else if (s->signPlus) prefix = '+';
        else if (s->signSpace)prefix = ' ';
    }
    int bodyLen   = 3;
    int total     = bodyLen + (prefix ? 1 : 0);
    int fieldPad  = s->width > total ? s->width - total : 0;
    // C99: zero-padding is undefined / ignored for Inf/NaN; glibc uses
    // spaces.  We follow glibc.
    if (!s->leftAlign) {
        emitPad(fieldPad, ' ');
    }
    if (prefix) {
        emit(prefix);
    }
    emitStr(body);
    if (s->leftAlign) {
        emitPad(fieldPad, ' ');
    }
    return 1;
}


// Emit %a / %A hex-float.  Local width/leftAlign/zeroPad handling --
// emitNumber's monolithic numeric body can only honor one prefix at a
// time, and hex-float needs prefix = sign + "0x" + content.  We do use
// emitNumber for the exponent tail (sign + decimal digits, no prefix).
//
// Format: [-]0x{H}.{F}p{SE} where H is 0 or 1, F is up to 13 hex digits
// (52 mantissa bits / 4), SE is signed decimal exponent.  Subnormals
// canonicalize as 0x0.{F}p-1022 (matching glibc).  Trailing-zero
// stripping for the fractional part fires when precision is unspecified.
static void emitHexFloat(double v, char spec, const Spec *s) {
    DblBits d;
    decodeDouble(v, &d);
    int upper = (spec == 'A');
    if (emitInfNan(&d, upper, s)) {
        return;
    }
    // Pull the 13 fractional hex nibbles of the mantissa (high-to-low).
    // The 52-bit mantissa = 13 hex digits.  All of n[0..12] are
    // FRACTIONAL nibbles; the integral digit (0 or 1) is implicit
    // (set by the exp == 0 subnormal-vs-zero split below).
    // n[0] is the most significant nibble (m[3] LSBs); n[12] is the
    // least significant nibble (m[0] LSBs).
    unsigned char n[13];
    n[0]  = (unsigned char)(d.m[3] & 0x0F);
    n[1]  = (unsigned char)((d.m[2] >> 12) & 0x0F);
    n[2]  = (unsigned char)((d.m[2] >>  8) & 0x0F);
    n[3]  = (unsigned char)((d.m[2] >>  4) & 0x0F);
    n[4]  = (unsigned char)( d.m[2]        & 0x0F);
    n[5]  = (unsigned char)((d.m[1] >> 12) & 0x0F);
    n[6]  = (unsigned char)((d.m[1] >>  8) & 0x0F);
    n[7]  = (unsigned char)((d.m[1] >>  4) & 0x0F);
    n[8]  = (unsigned char)( d.m[1]        & 0x0F);
    n[9]  = (unsigned char)((d.m[0] >> 12) & 0x0F);
    n[10] = (unsigned char)((d.m[0] >>  8) & 0x0F);
    n[11] = (unsigned char)((d.m[0] >>  4) & 0x0F);
    n[12] = (unsigned char)( d.m[0]        & 0x0F);
    // Determine integral hex digit + biased-to-unbiased exponent.
    // C99 canonical: normal -> 1.fp{e-1023}, subnormal -> 0.fp-1022,
    // zero -> 0x0p+0 (glibc prints with prec digits if requested).
    char  integral;            // '0' or '1'
    int   expVal;              // exponent of 2 (already accounting for the
                               // implicit-1 / subnormal split)
    int   zero = (d.exp == 0)
              && (d.m[0] | d.m[1] | d.m[2] | d.m[3]) == 0;
    if (d.exp == 0) {
        integral = '0';
        expVal   = zero ? 0 : -1022;   // subnormals all share -1022
    } else {
        integral = '1';
        expVal   = (int)d.exp - 1023;
    }
    // Decide how many fractional hex digits to emit.  fracLen is the
    // count of nibbles to emit from n[0..fracLen-1].  When prec is
    // unspecified (s->prec < 0): emit exact representation, strip
    // trailing zeros (glibc style).  Otherwise: emit `prec` digits
    // (zero-pad or round if needed).
    int fracLen;
    if (s->prec < 0) {
        // Trailing-zero strip: find the largest index < 13 with a
        // non-zero nibble; fracLen = (idx + 1).  If all zero,
        // fracLen = 0.
        fracLen = 13;
        while (fracLen > 0 && n[fracLen - 1] == 0) {
            fracLen--;
        }
    } else if (s->prec > 13) {
        fracLen = 13;  // We have at most 13 nibbles of real data;
                       // pad below with '0' up to s->prec.
    } else {
        fracLen = s->prec;
        // Round-half-even at fracLen.  When fracLen < 13, the first
        // discarded nibble is n[fracLen].  Half = 8.  Round up if >8;
        // round to even on exactly 8 with no remainder; round down if <8.
        if (fracLen < 13) {
            int round = 0;
            unsigned char first = n[fracLen];
            if (first > 8) {
                round = 1;
            } else if (first == 8) {
                // Any remaining non-zero nibble after first -> round up.
                int sticky = 0;
                for (int i = fracLen + 1; i < 13; i++) {
                    if (n[i] != 0) { sticky = 1; break; }
                }
                if (sticky) {
                    round = 1;
                } else {
                    // Half: round to even (last kept nibble even -> down).
                    unsigned char last = (fracLen > 0) ? n[fracLen - 1]
                                                      : (unsigned char)(integral - '0');
                    round = (last & 1);
                }
            }
            if (round) {
                int i = fracLen - 1;
                while (i >= 0) {
                    n[i] = (unsigned char)((n[i] + 1) & 0x0F);
                    if (n[i] != 0) break;
                    i--;
                }
                if (i < 0) {
                    // Carry propagated into the integral digit.  glibc
                    // does NOT re-normalize on overflow here: `%.0a` of
                    // 1.5 (0x1.8p+0) emits `0x2p+0`, not `0x1p+1`.  We
                    // match that.  Subnormal rounding up to 0x1 keeps
                    // the -1022 exponent (subnormal-to-smallest-normal).
                    unsigned char ih = (unsigned char)(integral - '0');
                    ih = (unsigned char)(ih + 1);
                    integral = (char)('0' + ih);
                }
            }
        }
    }
    // Build the body in a local buffer so we can apply width padding
    // without reusing emitNumber's prefix logic.  Body layout:
    //   [sign] 0x H . F p SE
    // Worst case: sign(1) + "0x"(2) + integral(1) + "."(1) +
    //             13 hex digits + "p"(1) + sign(1) + 5 decimal = 25.
    // We allow up to 32 to give the prec>13 padding case headroom.
    char body[40];
    int  bi = 0;
    if      (d.sign)        body[bi++] = '-';
    else if (s->signPlus)   body[bi++] = '+';
    else if (s->signSpace)  body[bi++] = ' ';
    body[bi++] = '0';
    body[bi++] = upper ? 'X' : 'x';
    body[bi++] = integral;
    // The '.' is emitted IFF we will emit at least one fractional digit
    // OR alt-form is set (# forces the radix point).
    int emitDot = (fracLen > 0) || (s->prec > 0) || s->altForm;
    if (emitDot) {
        body[bi++] = '.';
    }
    {
        const char *digits = upper ? "0123456789ABCDEF"
                                   : "0123456789abcdef";
        int written = 0;
        for (int i = 0; i < fracLen && i < 13; i++) {
            body[bi++] = digits[n[i]];
            written++;
        }
        // Zero-pad up to s->prec when prec exceeds available nibbles.
        if (s->prec > written) {
            int pad = s->prec - written;
            while (pad-- > 0) {
                body[bi++] = '0';
            }
        }
    }
    body[bi++] = upper ? 'P' : 'p';
    // Exponent: ALWAYS prints a sign ('+' or '-') and at least one digit.
    int  eAbs = expVal < 0 ? -expVal : expVal;
    char ebuf[8];                                  // up to 4-5 digits
    int  elen = u64ToDec((unsigned long long)eAbs, ebuf);
    body[bi++] = (expVal < 0) ? '-' : '+';
    while (elen-- > 0) {
        body[bi++] = ebuf[elen];
    }
    // Field-width + zero-pad logic (local, NOT via emitNumber).
    int contentLen = bi;
    int fieldPad   = s->width > contentLen ? s->width - contentLen : 0;
    if (s->zeroPad && !s->leftAlign) {
        // Zero pad goes BETWEEN the "0x" prefix (incl. any sign) and
        // the integral digit, matching glibc / C99 for %a.
        int prefixEnd = 0;
        if (body[0] == '-' || body[0] == '+' || body[0] == ' ') {
            prefixEnd = 3;  // sign + 0x
        } else {
            prefixEnd = 2;  // 0x
        }
        // Emit the leading prefix, then the zeros, then the rest.
        for (int i = 0; i < prefixEnd; i++) emit(body[i]);
        emitPad(fieldPad, '0');
        for (int i = prefixEnd; i < bi; i++) emit(body[i]);
        return;
    }
    if (!s->leftAlign) {
        emitPad(fieldPad, ' ');
    }
    for (int i = 0; i < bi; i++) emit(body[i]);
    if (s->leftAlign) {
        emitPad(fieldPad, ' ');
    }
}


static void emitDouble(double v, int prec, char spec, const Spec *s) {
    // For %g / %G, "precision" is total significant digits.  Real glibc
    // would compute exponent and choose between %e and %f styles, but
    // we keep things simple and just emit `X.YYY` with trailing zeros
    // stripped at the end.  For %f / %e, prec is decimal places.
    int isG = (spec == 'g' || spec == 'G');
    // Inf/NaN parity with %a (must precede prec clamp and sign strip
    // since those don't make sense on non-finite values).  `upper` for
    // %F/%E/%G follows the same caps convention as %A.
    {
        DblBits d;
        decodeDouble(v, &d);
        int upper = (spec == 'F' || spec == 'E' || spec == 'G');
        if (emitInfNan(&d, upper, s)) {
            return;
        }
    }
    if (prec < 0) {
        prec = 6;
    }
    if (prec > 9) {
        prec = 9;
    }
    // Avoid `if (v < 0)` (which calls __ltdf2) — the W65816 codegen
    // for that comparison passes its double arg with a missing word,
    // and the test silently returns false for negatives.  Read the
    // IEEE-754 sign bit and clear it inline instead.
    unsigned long long bits;
    __builtin_memcpy(&bits, &v, 8);
    if (bits & ((unsigned long long)1 << 63)) {
        emit('-');
        bits &= ~((unsigned long long)1 << 63);
        __builtin_memcpy(&v, &bits, 8);
    }
    // Split int part first, then scale only the fractional part.  The
    // earlier "multiply v by 10^prec then split via integer divide"
    // approach silently overflowed long for v*10^prec > 2^31 (e.g. any
    // value ≥ 2.15 with prec=9 in `%.12g`).  We've since reworked the
    // libcall ABI, so the previously-buggy `v - (double)ipart` chain
    // works now — smoke catches a regression of either bug.
    unsigned long intPart = (unsigned long)(long)v;
    double frac = v - (double)intPart;
    unsigned long mul = 1;
    for (int i = 0; i < prec; i++) {
        frac = frac * 10.0;
        mul *= 10;
    }
    // Round-half-up before truncation: 0.314 * 100 = 31.3999... in
    // soft-double, but `%.2f` of 3.14 should print "3.14".  Adding 0.5
    // then truncating is round-half-up for the non-negative frac here.
    frac = frac + 0.5;
    unsigned long frcPart = (unsigned long)(long)frac;
    // Carry-up if rounding pushed frac to a full integer (e.g. 0.9995
    // → 0.9995*1000+0.5 = 1000 = mul; the "0.9995" wanted to become
    // "1.000", not "0.1000").
    if (frcPart >= mul) {
        intPart += 1;
        frcPart = 0;
    }
    {
        char ibuf[20];
        int  ilen = u64ToDec(intPart, ibuf);
        emitRev(ibuf, ilen);
    }
    if (prec == 0) {
        return;
    }
    // Build fractional digits into a local buffer (reverse order to
    // forward) so we can trim trailing zeros for %g before emitting.
    char buf[10];
    for (int i = prec - 1; i >= 0; i--) {
        buf[i] = (char)('0' + (frcPart % 10));
        frcPart /= 10;
    }
    int emitCount = prec;
    if (isG) {
        // Strip trailing zeros.  If the whole fractional part is
        // zeros, skip the '.' too.
        while (emitCount > 0 && buf[emitCount - 1] == '0') {
            emitCount -= 1;
        }
    }
    if (emitCount == 0) {
        return;  // No fractional digits to emit → no '.' either.
    }
    emit('.');
    for (int i = 0; i < emitCount; i++) {
        emit(buf[i]);
    }
}
#endif // LLVM816_NO_FLOAT_PRINTF


// Length modifiers — encoded as small ints to keep the dispatch flat.
enum {
    LEN_NONE = 0,
    LEN_HH,         // hh: char-promoted-to-int
    LEN_H,          // h:  short-promoted-to-int
    LEN_L,          // l:  long
    LEN_LL,         // ll: long long
    LEN_J,          // j:  intmax_t (= long long)
    LEN_Z,          // z:  size_t (= unsigned long)
    LEN_T           // t:  ptrdiff_t (= int)
};


// fmt is arg0 (A register); see banner comment for why the order matters.
static int format(const char *fmt, va_list ap) {
    while (*fmt) {
        char c = *fmt++;
        if (c != '%') {
            emit(c);
            continue;
        }
        Spec s;
        s.leftAlign = 0;
        s.signPlus  = 0;
        s.signSpace = 0;
        s.altForm   = 0;
        s.zeroPad   = 0;
        s.width     = 0;
        s.prec      = -1;
        // Flags (any subset, any order).
        for (;;) {
            char f = *fmt;
            if      (f == '-') s.leftAlign = 1;
            else if (f == '+') s.signPlus  = 1;
            else if (f == ' ') s.signSpace = 1;
            else if (f == '#') s.altForm   = 1;
            else if (f == '0') s.zeroPad   = 1;
            else break;
            fmt++;
        }
        // Width: decimal or `*`.
        if (*fmt == '*') {
            int w = va_arg(ap, int);
            if (w < 0) { s.leftAlign = 1; w = -w; }
            s.width = w;
            fmt++;
        } else {
            while (*fmt >= '0' && *fmt <= '9') {
                s.width = s.width * 10 + (*fmt - '0');
                fmt++;
            }
        }
        // Precision: `.N` or `.*` (presence enables, default 0 if no digits).
        if (*fmt == '.') {
            fmt++;
            if (*fmt == '*') {
                s.prec = va_arg(ap, int);
                fmt++;
            } else {
                s.prec = 0;
                while (*fmt >= '0' && *fmt <= '9') {
                    s.prec = s.prec * 10 + (*fmt - '0');
                    fmt++;
                }
            }
        }
        // Length modifier (one of hh / h / l / ll / j / z / t).
        int len = LEN_NONE;
        if (*fmt == 'h') {
            fmt++;
            if (*fmt == 'h') { fmt++; len = LEN_HH; }
            else len = LEN_H;
        } else if (*fmt == 'l') {
            fmt++;
            if (*fmt == 'l') { fmt++; len = LEN_LL; }
            else len = LEN_L;
        } else if (*fmt == 'j') { fmt++; len = LEN_J; }
        else if (*fmt == 'z')   { fmt++; len = LEN_Z; }
        else if (*fmt == 't')   { fmt++; len = LEN_T; }
        char spec = *fmt++;
        // Signed integers.
        if (spec == 'd' || spec == 'i') {
            long long v;
            switch (len) {
                case LEN_HH: v = (signed char)va_arg(ap, int);      break;
                case LEN_H:  v = (short)va_arg(ap, int);            break;
                case LEN_L:  v = (long)va_arg(ap, long);            break;
                case LEN_LL:
                case LEN_J:  v = va_arg(ap, long long);             break;
                case LEN_Z:  v = (long)va_arg(ap, unsigned long);   break;
                case LEN_T:  v = va_arg(ap, int);                   break;
                default:     v = va_arg(ap, int);                   break;
            }
            int isNeg = v < 0;
            unsigned long long mag = isNeg ? (0ull - (unsigned long long)v)
                                            : (unsigned long long)v;
            emitNumber(mag, /*isSigned=*/1, isNeg, 10, 0, &s);
        }
        // Unsigned bases: %u %x %X %o.
        else if (spec == 'u' || spec == 'x' || spec == 'X' || spec == 'o') {
            unsigned long long v;
            switch (len) {
                case LEN_HH: v = (unsigned char)va_arg(ap, unsigned int);  break;
                case LEN_H:  v = (unsigned short)va_arg(ap, unsigned int); break;
                case LEN_L:  v = va_arg(ap, unsigned long);                break;
                case LEN_LL:
                case LEN_J:  v = va_arg(ap, unsigned long long);           break;
                case LEN_Z:  v = va_arg(ap, unsigned long);                break;
                case LEN_T:  v = (unsigned int)va_arg(ap, int);            break;
                default:     v = va_arg(ap, unsigned int);                 break;
            }
            int base  = (spec == 'u') ? 10 : (spec == 'o') ? 8 : 16;
            int upper = (spec == 'X');
            emitNumber(v, /*isSigned=*/0, 0, base, upper, &s);
        }
        else if (spec == 'c') {
            char ch = (char)va_arg(ap, int);
            int fieldPad = s.width > 1 ? s.width - 1 : 0;
            if (!s.leftAlign) emitPad(fieldPad, ' ');
            emit(ch);
            if (s.leftAlign) emitPad(fieldPad, ' ');
        }
        else if (spec == 's') {
            emitStrField(va_arg(ap, const char *), &s);
        }
#ifndef LLVM816_NO_FLOAT_PRINTF
        else if (spec == 'f' || spec == 'F' ||
                 spec == 'g' || spec == 'G' ||
                 spec == 'e' || spec == 'E') {
            emitDouble(va_arg(ap, double), s.prec, spec, &s);
        }
        else if (spec == 'a' || spec == 'A') {
            emitHexFloat(va_arg(ap, double), spec, &s);
        }
#endif
        else if (spec == 'p') {
            // ptr32 — print as "0xBBBBOOOO" (8 hex digits, bank + offset).
            unsigned long pp = (unsigned long)(unsigned long)va_arg(ap, void *);
            emit('0');
            emit('x');
            Spec p = s;
            p.prec  = 8;
            p.width = 0;
            emitNumber(pp, 0, 0, 16, 0, &p);
        }
        else if (spec == 'n') {
            // Store the number of chars emitted so far through the
            // pointer arg.  Length modifier picks the integer width.
            int count = (int)gTotal;
            switch (len) {
                case LEN_HH: *va_arg(ap, signed char *) = (signed char)count; break;
                case LEN_H:  *va_arg(ap, short *)       = (short)count;       break;
                case LEN_L:  *va_arg(ap, long *)        = (long)count;        break;
                case LEN_LL:
                case LEN_J:  *va_arg(ap, long long *)   = (long long)count;   break;
                case LEN_Z:  *va_arg(ap, unsigned long *) = (unsigned long)count; break;
                case LEN_T:  *va_arg(ap, int *)         = count;              break;
                default:     *va_arg(ap, int *)         = count;              break;
            }
        }
        else if (spec == '%') {
            emit('%');
        }
        else {
            // Unknown conversion — echo `%spec` literally.
            emit('%');
            emit(spec);
        }
    }
    if (gCur < gEnd) {
        *gCur = '\0';
    } else if (gEnd > (char *)0) {
        // Truncated, but n > 0: overwrite the last byte with NUL so
        // the result is a valid C string.  snprintf with n=0 sets
        // gEnd = NULL up front so this branch correctly skips —
        // previously it wrote `gEnd[-1]` to `buf[-1]`, clobbering
        // memory before the buffer.
        gEnd[-1] = '\0';
    }
    return (int)gTotal;
}



int snprintf(char *buf, size_t n, const char *fmt, ...) {
    gCur   = buf;
    // n == 0 must NOT touch the buffer (C99 7.19.6.5).  Setting
    // gEnd = NULL here makes both `gCur < gEnd` and `gEnd > 0`
    // false, so no NUL terminator gets written.
    gEnd   = n ? buf + n : (char *)0;
    gTotal = 0;
    va_list ap;
    va_start(ap, fmt);
    int r = format(fmt, ap);
    va_end(ap);
    return r;
}


int sprintf(char *buf, const char *fmt, ...) {
    gCur   = buf;
    // sprintf is unbounded; see SPRINTF_END_SENTINEL above for the
    // reason we don't use buf + 0xFFFE.
    gEnd   = SPRINTF_END_SENTINEL;
    gTotal = 0;
    va_list ap;
    va_start(ap, fmt);
    int r = format(fmt, ap);
    va_end(ap);
    return r;
}


int vsnprintf(char *buf, size_t n, const char *fmt, va_list ap) {
    gCur   = buf;
    gEnd   = n ? buf + n : (char *)0;
    gTotal = 0;
    return format(fmt, ap);
}


int vsprintf(char *buf, const char *fmt, va_list ap) {
    gCur   = buf;
    gEnd   = SPRINTF_END_SENTINEL;
    gTotal = 0;
    return format(fmt, ap);
}
