// strtok / strtok_r — own TU so the noinline on strtok_r doesn't
// affect other functions' inlining decisions.  Kept separate from
// extras.c by historical convention.

static char *gStrtokSave;


// noinline: at -O2 the strtok() wrapper otherwise inlines all of
// strtok_r, growing the .o by ~70%.  The runtime's bank-0 budget
// is tight enough that the duplicated code pushes rodata past
// 0xC000 (IIgs IO window), corrupting string literals at runtime.
char *strtok_r(char *str, const char *delim, char **saveptr) {
    unsigned char *s;
    if (str != (char *)0) {
        s = (unsigned char *)str;
    } else {
        s = (unsigned char *)(*saveptr);
    }
    if (s == (unsigned char *)0) {
        return (char *)0;
    }
    const unsigned char *du = (const unsigned char *)delim;
    // Skip leading delimiters.
    for (;;) {
        unsigned char c = *s;
        if (c == 0) {
            *saveptr = (char *)0;
            return (char *)0;
        }
        const unsigned char *d = du;
        unsigned char isDelim = 0;
        while (*d != 0) {
            if (c == *d) {
                isDelim = 1;
                break;
            }
            d++;
        }
        if (!isDelim) {
            break;
        }
        s++;
    }
    unsigned char *tok = s;
    // Find next delimiter or NUL.
    for (;;) {
        unsigned char c = *s;
        if (c == 0) {
            *saveptr = (char *)0;
            return (char *)tok;
        }
        const unsigned char *d = du;
        unsigned char isDelim = 0;
        while (*d != 0) {
            if (c == *d) {
                isDelim = 1;
                break;
            }
            d++;
        }
        if (isDelim) {
            *s = 0;
            *saveptr = (char *)(s + 1);
            return (char *)tok;
        }
        s++;
    }
}


char *strtok(char *str, const char *delim) {
    return strtok_r(str, delim, &gStrtokSave);
}
