// strtol / strtoul — kept in their own translation unit because adding
// them to libc.c bumped the libc.s layout enough that vprintf's branch
// distances exceeded our BranchExpand threshold (caught by printf("%d
// ok") segfaulting).  Their own .o keeps them out of the way.

extern int isspace(int);

static int charDigit(char c, int base) {
    int d = -1;
    if (c >= '0' && c <= '9') d = c - '0';
    else if (c >= 'a' && c <= 'z') d = 10 + c - 'a';
    else if (c >= 'A' && c <= 'Z') d = 10 + c - 'A';
    if (d < 0 || d >= base) return -1;
    return d;
}

unsigned long strtoul(const char *nptr, char **endptr, int base) {
    const char *s = nptr;
    while (isspace(*s)) s++;
    int neg = 0;
    if (*s == '-') { neg = 1; s++; }
    else if (*s == '+') s++;
    if ((base == 0 || base == 16) && s[0] == '0' &&
        (s[1] == 'x' || s[1] == 'X') && charDigit(s[2], 16) >= 0) {
        base = 16;
        s += 2;
    } else if (base == 0 && *s == '0') {
        base = 8;
        s++;
    } else if (base == 0) {
        base = 10;
    }
    unsigned long n = 0;
    int saw_digit = 0;
    for (;;) {
        int d = charDigit(*s, base);
        if (d < 0) break;
        n = n * (unsigned long)base + (unsigned long)d;
        saw_digit = 1;
        s++;
    }
    if (endptr) *endptr = (char *)(saw_digit ? s : nptr);
    // Negate in unsigned arithmetic to avoid signed-overflow UB.
    return neg ? (0ul - n) : n;
}

long strtol(const char *nptr, char **endptr, int base) {
    const char *s = nptr;
    while (isspace(*s)) s++;
    int neg = (*s == '-');
    if (*s == '+' || *s == '-') s++;
    char *ep = 0;
    unsigned long n = strtoul(s, &ep, base);
    if (ep == s) {
        if (endptr) *endptr = (char *)nptr;
        return 0;
    }
    if (endptr) *endptr = ep;
    // Negate as unsigned to avoid signed-overflow UB on LONG_MIN
    // ("-2147483648" — the magnitude doesn't fit in long).
    return neg ? (long)(0ul - n) : (long)n;
}


// ---- Long-long (64-bit) variants ------------------------------------

unsigned long long strtoull(const char *nptr, char **endptr, int base) {
    const char *s = nptr;
    while (isspace(*s)) s++;
    int neg = 0;
    if (*s == '-') { neg = 1; s++; }
    else if (*s == '+') s++;
    if ((base == 0 || base == 16) && s[0] == '0' &&
        (s[1] == 'x' || s[1] == 'X') && charDigit(s[2], 16) >= 0) {
        base = 16;
        s += 2;
    } else if (base == 0 && *s == '0') {
        base = 8;
        s++;
    } else if (base == 0) {
        base = 10;
    }
    unsigned long long n = 0;
    int saw_digit = 0;
    for (;;) {
        int d = charDigit(*s, base);
        if (d < 0) break;
        n = n * (unsigned long long)base + (unsigned long long)d;
        saw_digit = 1;
        s++;
    }
    if (endptr) *endptr = (char *)(saw_digit ? s : nptr);
    return neg ? (0ull - n) : n;
}


long long strtoll(const char *nptr, char **endptr, int base) {
    const char *s = nptr;
    while (isspace(*s)) s++;
    int neg = (*s == '-');
    if (*s == '+' || *s == '-') s++;
    char *ep = 0;
    unsigned long long n = strtoull(s, &ep, base);
    if (ep == s) {
        if (endptr) *endptr = (char *)nptr;
        return 0;
    }
    if (endptr) *endptr = ep;
    return neg ? (long long)(0ull - n) : (long long)n;
}


long long atoll(const char *s) {
    return strtoll(s, 0, 10);
}


// ---- Float parsing (atof / strtod / strtof) -------------------------

extern double pow(double, double);   // for 10^N scaling


// strtod parses [whitespace][+|-]<digits>[.digits][e|E[+|-]<digits>].
// Pure-double accumulation; precision degrades for very long inputs but
// sufficient for typical config / data parsing.
double strtod(const char *nptr, char **endptr) {
    const char *s = nptr;
    while (isspace(*s)) s++;
    int neg = 0;
    if (*s == '-') { neg = 1; s++; }
    else if (*s == '+') s++;

    double whole = 0.0;
    int saw_digit = 0;
    while (*s >= '0' && *s <= '9') {
        whole = whole * 10.0 + (double)(*s - '0');
        s++;
        saw_digit = 1;
    }
    double frac = 0.0;
    double scale = 1.0;
    if (*s == '.') {
        s++;
        while (*s >= '0' && *s <= '9') {
            frac = frac * 10.0 + (double)(*s - '0');
            scale *= 10.0;
            s++;
            saw_digit = 1;
        }
    }
    if (!saw_digit) {
        if (endptr) *endptr = (char *)nptr;
        return 0.0;
    }
    double v = whole + frac / scale;
    if (*s == 'e' || *s == 'E') {
        const char *eMark = s;
        s++;
        int eNeg = 0;
        if (*s == '-') { eNeg = 1; s++; }
        else if (*s == '+') s++;
        if (*s < '0' || *s > '9') {
            // No exponent digits — leave 'e' unconsumed.
            s = eMark;
        } else {
            int e = 0;
            while (*s >= '0' && *s <= '9') {
                e = e * 10 + (*s - '0');
                s++;
            }
            v *= pow(10.0, eNeg ? -e : e);
        }
    }
    if (endptr) *endptr = (char *)s;
    return neg ? -v : v;
}


float strtof(const char *nptr, char **endptr) {
    return (float)strtod(nptr, endptr);
}


double atof(const char *s) {
    return strtod(s, 0);
}
