// Tiny string.h / stdlib.h helpers — kept out of libc.c because
// adding to that translation unit shifts vprintf's internal branch
// distances and randomly breaks BranchExpand (same precedent as
// strtol.c and snprintf.c).
//
// Functions:
//   string.h:  strcat, strncat
//   stdlib.h:  atol, llabs

typedef unsigned long size_t;


char *strcat(char *dst, const char *src) {
    char *d = dst;
    while (*d) {
        d++;
    }
    while ((*d = *src)) {
        d++;
        src++;
    }
    return dst;
}


char *strncat(char *dst, const char *src, size_t n) {
    char *d = dst;
    while (*d) {
        d++;
    }
    while (n && *src) {
        *d = *src;
        d++;
        src++;
        n--;
    }
    *d = 0;
    return dst;
}


extern int isspace(int);


long atol(const char *s) {
    while (isspace(*s)) {
        s++;
    }
    int sign = 1;
    if (*s == '-') {
        sign = -1;
        s++;
    } else if (*s == '+') {
        s++;
    }
    // Parse magnitude as unsigned to avoid signed-overflow UB (e.g.
    // "-2147483648" — the magnitude 2147483648 doesn't fit in long).
    unsigned long u = 0;
    while (*s >= '0' && *s <= '9') {
        u = u * 10 + (unsigned long)(*s - '0');
        s++;
    }
    return sign < 0 ? (long)(0ul - u) : (long)u;
}


long long llabs(long long n) {
    return n < 0 ? -n : n;
}


// strnlen: like strlen but capped at maxlen.  Useful for safely
// measuring strings that may not be NUL-terminated within a known
// buffer size.
size_t strnlen(const char *s, size_t maxlen) {
    size_t n = 0;
    while (n < maxlen && s[n]) {
        n++;
    }
    return n;
}


static int toLowerByte(int c) {
    if (c >= 'A' && c <= 'Z') {
        return c - 'A' + 'a';
    }
    return c;
}


int strcasecmp(const char *a, const char *b) {
    while (*a && *b) {
        int da = toLowerByte((unsigned char)*a);
        int db = toLowerByte((unsigned char)*b);
        if (da != db) {
            return da - db;
        }
        a++;
        b++;
    }
    return toLowerByte((unsigned char)*a) - toLowerByte((unsigned char)*b);
}


int strncasecmp(const char *a, const char *b, size_t n) {
    while (n && *a && *b) {
        int da = toLowerByte((unsigned char)*a);
        int db = toLowerByte((unsigned char)*b);
        if (da != db) {
            return da - db;
        }
        a++;
        b++;
        n--;
    }
    if (!n) return 0;
    return toLowerByte((unsigned char)*a) - toLowerByte((unsigned char)*b);
}


// Linear congruential generator — Numerical Recipes constants.
// Returns 15-bit values (RAND_MAX = 0x7FFF) per C standard convention.
static unsigned long randSeed = 1;

void srand(unsigned int seed) {
    randSeed = seed;
}

int rand(void) {
    randSeed = randSeed * 1103515245UL + 12345UL;
    return (int)((randSeed >> 16) & 0x7FFF);
}


// crt0 hook: seed rand() from the IIgs RTC via ReadTimeHex (Misc
// Tool $0D03).  Called from crt0Gsos.s / crt0Gno.s after .init_array
// has run.  The Tool Locator is already up at that point (the GS/OS
// Loader brings it up before transferring control to __start; GNO's
// kernel does likewise), so JSL $E10000 X=$0D03 is safe.
//
// Without this hook randSeed stays at 1 and every run produces an
// identical PRNG sequence -- a silent correctness bug for callers
// like mkstemp that rely on rand() for uniqueness across invocations.
//
// Mixing strategy: fold the 8 TimeRec bytes into the seed via a
// simple u16 rotate-XOR, then place into the high half of randSeed
// (the LCG output is `(seed >> 16) & 0x7FFF`, so the first rand()
// directly reflects the seed bits we just installed).  u16 arithmetic
// keeps the helper small -- ~150 B vs ~860 B for the u32 form.
extern void iigsReadTimeHex(unsigned char *buf8);

void __srandInitFromTime(void) {
    unsigned char b[8];
    iigsReadTimeHex(b);
    unsigned short s = 0;
    for (int i = 0; i < 8; i++) {
        s = (unsigned short)((s << 3) | (s >> 13));
        s = (unsigned short)(s ^ (unsigned short)b[i]);
    }
    // Force non-zero (LCG with seed 0 still cycles, but at least one
    // bit set keeps the early outputs out of the trivial-prefix range).
    if (!s) {
        s = 1;
    }
    // Place the time-derived bits in the high half so the first
    // rand() output -- ((seed * K + C) >> 16) & 0x7FFF -- carries
    // them.  Low half stays 0; the LCG mixes it into the next call.
    randSeed = ((unsigned long)s) << 16;
}


// ----- sys/time.h gettimeofday() ---------------------------------------
//
// Thin shim over libc.c's time() — same epoch-second source, packaged
// in the POSIX struct timeval shape.  tv_usec is always 0 because the
// IIgs has no sub-second wall clock (the VBL counter at $E1:006B is
// monotonic but not aligned to wall-clock seconds).  The tz argument
// is accepted for source compat and ignored; the IIgs has no
// timezone database.
//
// Declared in <sys/time.h>; the struct timeval layout matches that
// header byte-for-byte (time_t, then long).

extern long time(long *t);  // matches signature in <time.h>

struct __ggGtodTimeval {
    long tv_sec;
    long tv_usec;
};


int gettimeofday(struct __ggGtodTimeval *tv, void *tz) {
    (void)tz;
    if (!tv) {
        return 0;
    }
    long s = time((long *)0);
    if (s == 0) {
        // time() returns 0 either at Unix epoch midnight (impossible on
        // a real IIgs RTC) or when the Tool Locator isn't up.  Treat as
        // failure -- matches the POSIX convention.
        tv->tv_sec = 0;
        tv->tv_usec = 0;
        return -1;
    }
    tv->tv_sec = s;
    tv->tv_usec = 0;
    return 0;
}


// ----- additional string.h ----------------------------------------------

static int inSet(char c, const char *set) {
    while (*set) {
        if (c == *set) {
            return 1;
        }
        set++;
    }
    return 0;
}


char *strpbrk(const char *s, const char *accept) {
    while (*s) {
        if (inSet(*s, accept)) {
            return (char *)s;
        }
        s++;
    }
    return 0;
}


size_t strspn(const char *s, const char *accept) {
    size_t n = 0;
    while (s[n] && inSet(s[n], accept)) {
        n++;
    }
    return n;
}


size_t strcspn(const char *s, const char *reject) {
    size_t n = 0;
    while (s[n] && !inSet(s[n], reject)) {
        n++;
    }
    return n;
}


// strtok / strtok_r are in runtime/src/strtok.c.

// ---- wchar.h ----
// wchar_t is 16-bit on this target.  The wcs* functions mirror the
// str* family.  mbtowc / wctomb use the trivial 1:1 byte<->wide-char
// mapping (essentially Latin-1) — no real multi-byte / locale support.

// Now `int` to match the clang builtin signature for wcslen/wcscmp/
// wcscpy etc; was `unsigned short`.  Latin-1 content (0..255) is
// representable in both.
typedef int wchar_t;

size_t wcslen(const wchar_t *s) {
    size_t n = 0;
    while (*s++) n++;
    return n;
}

int wcscmp(const wchar_t *a, const wchar_t *b) {
    while (*a && *a == *b) { a++; b++; }
    return (int)((short)*a - (short)*b);
}

int wcsncmp(const wchar_t *a, const wchar_t *b, size_t n) {
    while (n && *a && *a == *b) { a++; b++; n--; }
    if (!n) return 0;
    return (int)((short)*a - (short)*b);
}

wchar_t *wcscpy(wchar_t *dst, const wchar_t *src) {
    wchar_t *d = dst;
    while ((*d++ = *src++)) {}
    return dst;
}

wchar_t *wcsncpy(wchar_t *dst, const wchar_t *src, size_t n) {
    wchar_t *d = dst;
    while (n && (*d = *src)) { d++; src++; n--; }
    while (n--) *d++ = 0;
    return dst;
}

wchar_t *wcscat(wchar_t *dst, const wchar_t *src) {
    wchar_t *d = dst;
    while (*d) d++;
    while ((*d++ = *src++)) {}
    return dst;
}

wchar_t *wcschr(const wchar_t *s, wchar_t c) {
    while (*s) {
        if (*s == c) return (wchar_t *)s;
        s++;
    }
    return (c == 0) ? (wchar_t *)s : (wchar_t *)0;
}

wchar_t *wcsrchr(const wchar_t *s, wchar_t c) {
    const wchar_t *last = (const wchar_t *)0;
    while (*s) {
        if (*s == c) last = s;
        s++;
    }
    if (c == 0) return (wchar_t *)s;
    return (wchar_t *)last;
}

int mbtowc(wchar_t *pwc, const char *s, size_t n) {
    if (!s) return 0;            // no shift state
    if (n == 0) return -1;
    unsigned char c = (unsigned char)*s;
    if (pwc) *pwc = (wchar_t)c;
    return c ? 1 : 0;
}

int wctomb(char *s, wchar_t wc) {
    if (!s) return 0;            // no shift state
    if (wc > 0xFF) return -1;
    *s = (char)wc;
    return 1;
}

size_t mbstowcs(wchar_t *pwcs, const char *s, size_t n) {
    size_t i = 0;
    while (i < n && s[i]) {
        if (pwcs) pwcs[i] = (wchar_t)(unsigned char)s[i];
        i++;
    }
    if (pwcs && i < n) pwcs[i] = 0;
    return i;
}

size_t wcstombs(char *s, const wchar_t *pwcs, size_t n) {
    size_t i = 0;
    while (i < n && pwcs[i]) {
        if (pwcs[i] > 0xFF) return (size_t)-1;
        if (s) s[i] = (char)pwcs[i];
        i++;
    }
    if (s && i < n) s[i] = 0;
    return i;
}

int mblen(const char *s, size_t n) {
    if (!s) return 0;
    if (n == 0) return -1;
    return *s ? 1 : 0;
}


// ---- wide-char memory + scan/format ---------------------------------
// Operate on wchar_t arrays (wchar_t is `int` on this target = 2
// bytes).  Under Latin-1 we delegate the actual work to the byte/str
// equivalents wherever the data fits in 8 bits.

#include <stdarg.h>

struct tm;

extern void  *memcpy (void *dst, const void *src, size_t n);
extern void  *memmove(void *dst, const void *src, size_t n);
extern long   strtol  (const char *nptr, char **endptr, int base);
extern unsigned long  strtoul (const char *nptr, char **endptr, int base);
extern long long      strtoll (const char *nptr, char **endptr, int base);
extern unsigned long long strtoull(const char *nptr, char **endptr, int base);
extern double strtod  (const char *nptr, char **endptr);
extern float  strtof  (const char *nptr, char **endptr);
extern int    vsnprintf(char *buf, size_t n, const char *fmt, va_list ap);
extern size_t strftime (char *buf, size_t n, const char *fmt, const struct tm *tm);


wchar_t *wmemcpy(wchar_t *dst, const wchar_t *src, size_t n) {
    memcpy(dst, src, n * sizeof(wchar_t));
    return dst;
}


wchar_t *wmemmove(wchar_t *dst, const wchar_t *src, size_t n) {
    memmove(dst, src, n * sizeof(wchar_t));
    return dst;
}


wchar_t *wmemset(wchar_t *dst, wchar_t c, size_t n) {
    wchar_t *p = dst;
    while (n--) {
        *p++ = c;
    }
    return dst;
}


int wmemcmp(const wchar_t *a, const wchar_t *b, size_t n) {
    while (n--) {
        if (*a != *b) {
            return (int)(*a - *b);
        }
        a++;
        b++;
    }
    return 0;
}


wchar_t *wmemchr(const wchar_t *s, wchar_t c, size_t n) {
    while (n--) {
        if (*s == c) {
            return (wchar_t *)s;
        }
        s++;
    }
    return (wchar_t *)0;
}


// Helper: narrow a wide string of up to `lim` chars into a byte
// buffer.  Stops at the first NUL or after `lim` chars.  Returns
// the number of bytes written (excluding any trailing NUL).
static size_t __narrow(char *out, const wchar_t *in, size_t lim) {
    size_t i = 0;
    while (i < lim && in[i]) {
        out[i] = (char)(in[i] & 0xFF);
        i++;
    }
    if (i < lim) {
        out[i] = 0;
    }
    return i;
}


long wcstol(const wchar_t *nptr, wchar_t **endptr, int base) {
    char buf[40];
    size_t k = __narrow(buf, nptr, sizeof(buf) - 1);
    buf[k] = 0;
    char *bend;
    long r = strtol(buf, &bend, base);
    if (endptr) {
        *endptr = (wchar_t *)(nptr + (bend - buf));
    }
    return r;
}


unsigned long wcstoul(const wchar_t *nptr, wchar_t **endptr, int base) {
    char buf[40];
    size_t k = __narrow(buf, nptr, sizeof(buf) - 1);
    buf[k] = 0;
    char *bend;
    unsigned long r = strtoul(buf, &bend, base);
    if (endptr) {
        *endptr = (wchar_t *)(nptr + (bend - buf));
    }
    return r;
}


long long wcstoll(const wchar_t *nptr, wchar_t **endptr, int base) {
    char buf[40];
    size_t k = __narrow(buf, nptr, sizeof(buf) - 1);
    buf[k] = 0;
    char *bend;
    long long r = strtoll(buf, &bend, base);
    if (endptr) {
        *endptr = (wchar_t *)(nptr + (bend - buf));
    }
    return r;
}


unsigned long long wcstoull(const wchar_t *nptr, wchar_t **endptr, int base) {
    char buf[40];
    size_t k = __narrow(buf, nptr, sizeof(buf) - 1);
    buf[k] = 0;
    char *bend;
    unsigned long long r = strtoull(buf, &bend, base);
    if (endptr) {
        *endptr = (wchar_t *)(nptr + (bend - buf));
    }
    return r;
}


double wcstod(const wchar_t *nptr, wchar_t **endptr) {
    char buf[40];
    size_t k = __narrow(buf, nptr, sizeof(buf) - 1);
    buf[k] = 0;
    char *bend;
    double r = strtod(buf, &bend);
    if (endptr) {
        *endptr = (wchar_t *)(nptr + (bend - buf));
    }
    return r;
}


float wcstof(const wchar_t *nptr, wchar_t **endptr) {
    char buf[40];
    size_t k = __narrow(buf, nptr, sizeof(buf) - 1);
    buf[k] = 0;
    char *bend;
    float r = strtof(buf, &bend);
    if (endptr) {
        *endptr = (wchar_t *)(nptr + (bend - buf));
    }
    return r;
}


// swprintf: narrow the format string, route through vsnprintf into a
// byte buffer, then widen the result back into `buf`.  Limits the
// format-spec coverage to what vsnprintf supports; %ls / %lc are not
// honoured (caller must pass narrow-char args).  Returns -1 on
// overflow per C11.
//
// Buffers kept small (64 bytes each) so the total frame stays under
// the W65816's 256-byte stack-rel addressing limit.  Long format
// strings and long outputs are truncated.
int vswprintf(wchar_t *buf, size_t n, const wchar_t *fmt, va_list ap) {
    if (n == 0) {
        return -1;
    }
    char fmtBuf[64];
    __narrow(fmtBuf, fmt, sizeof(fmtBuf) - 1);
    fmtBuf[sizeof(fmtBuf) - 1] = 0;
    char outBuf[64];
    size_t cap = n - 1 < sizeof(outBuf) - 1 ? n - 1 : sizeof(outBuf) - 1;
    int wrote = vsnprintf(outBuf, cap + 1, fmtBuf, ap);
    if (wrote < 0 || (size_t)wrote >= n) {
        buf[0] = 0;
        return -1;
    }
    int i;
    for (i = 0; i < wrote; i++) {
        buf[i] = (wchar_t)(unsigned char)outBuf[i];
    }
    buf[wrote] = 0;
    return wrote;
}


int swprintf(wchar_t *buf, size_t n, const wchar_t *fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    int r = vswprintf(buf, n, fmt, ap);
    va_end(ap);
    return r;
}


size_t wcsftime(wchar_t *buf, size_t n, const wchar_t *fmt, const struct tm *tm) {
    if (n == 0) {
        return 0;
    }
    char fmtBuf[64];
    __narrow(fmtBuf, fmt, sizeof(fmtBuf) - 1);
    fmtBuf[sizeof(fmtBuf) - 1] = 0;
    char outBuf[128];
    size_t cap = n - 1 < sizeof(outBuf) - 1 ? n - 1 : sizeof(outBuf) - 1;
    size_t wrote = strftime(outBuf, cap + 1, fmtBuf, tm);
    if (wrote == 0 || wrote >= n) {
        buf[0] = 0;
        return 0;
    }
    size_t i;
    for (i = 0; i < wrote; i++) {
        buf[i] = (wchar_t)(unsigned char)outBuf[i];
    }
    buf[wrote] = 0;
    return wrote;
}


// ---- fenv.h ----------------------------------------------------------
//
// softFloat / softDouble are fixed at round-to-nearest-even and don't
// raise IEEE exceptions.  We track the requested rounding mode and an
// exception-flag word but neither affects soft-float output.

static int __fenvRound = 0;  /* FE_TONEAREST */
static unsigned short __fenvExcept = 0;

int feclearexcept(int excepts)                       { __fenvExcept &= (unsigned short)~excepts; return 0; }
int feraiseexcept(int excepts)                       { __fenvExcept |= (unsigned short)excepts;  return 0; }
int fetestexcept(int excepts)                        { return __fenvExcept & excepts; }
int fegetexceptflag(unsigned short *flagp, int e)    { (void)e; if (flagp) *flagp = __fenvExcept; return 0; }
int fesetexceptflag(const unsigned short *flagp, int e) {
    if (!flagp) return -1;
    __fenvExcept = (unsigned short)((__fenvExcept & ~e) | (*flagp & e));
    return 0;
}
int fegetround(void)                                 { return __fenvRound; }
int fesetround(int r)                                { __fenvRound = r; return 0; }
int fegetenv(unsigned short *envp)                   { if (envp) *envp = __fenvExcept; return 0; }
int feholdexcept(unsigned short *envp)               { if (envp) *envp = __fenvExcept; __fenvExcept = 0; return 0; }
int fesetenv(const unsigned short *envp)             { __fenvExcept = envp ? *envp : 0; return 0; }
int feupdateenv(const unsigned short *envp)          { unsigned short e = envp ? *envp : 0; __fenvExcept |= e; return 0; }


// ---- threads.h backing storage ---------------------------------------
//
// All thread / mutex / cond ops are inline no-ops; only tss_* needs
// real per-key storage.  8 keys is enough for any single-core code.

void *__tss_slots[8];
int   __tss_next = 0;


// ---- aligned_alloc / posix_memalign ---------------------------------
//
// Wraps malloc with an over-allocation + alignment-adjust trick: alloc
// (n + alignment + sizeof(void*)) bytes; align upward; stash the
// original pointer just before the returned address for free() to find.
// `aligned_alloc` requires `n` to be a multiple of `alignment` (C11).

extern void *malloc(unsigned long n);
extern void  free  (void *p);

void *aligned_alloc(unsigned long alignment, unsigned long size) {
    if (alignment == 0 || (alignment & (alignment - 1))) return (void *)0;
    if (size % alignment) return (void *)0;
    unsigned long over = size + alignment + sizeof(void *);
    char *raw = (char *)malloc(over);
    if (!raw) return (void *)0;
    unsigned long addr = (unsigned long)raw + sizeof(void *);
    unsigned long aligned = (addr + alignment - 1) & ~(alignment - 1);
    ((void **)aligned)[-1] = raw;
    return (void *)aligned;
}

// Wrappers that read the stashed raw pointer and free the underlying
// block.  Callers should use these (not plain free) for aligned_alloc'd
// pointers.  Single-source projects can `#define free aligned_free` if
// needed; the standard C11 contract is that `free` works on aligned
// pointers, so we also patch free below.
void aligned_free(void *p) {
    if (!p) return;
    void *raw = ((void **)p)[-1];
    free(raw);
}

int posix_memalign(void **memptr, unsigned long alignment, unsigned long size) {
    if (!memptr) return 22;  /* EINVAL */
    if (alignment < sizeof(void *) || (alignment & (alignment - 1))) {
        *memptr = (void *)0;
        return 22;
    }
    void *p = aligned_alloc(alignment, (size + alignment - 1) & ~(alignment - 1));
    if (!p) { *memptr = (void *)0; return 12; /* ENOMEM */ }
    *memptr = p;
    return 0;
}
