static int _pdc_vsscanf()

in Utilities/cmpdcurses/pdcurses/scanw.c [167:580]


static int _pdc_vsscanf(const char *buf, const char *fmt, va_list arg_ptr)
{
    int count, chars, c, width, radix, d, i;
    int *int_ptr;
    long *long_ptr;
    short *short_ptr;
    char *char_ptr;
    unsigned char f;
    char neg, assign, ok, size;
    long n;
    char map[256], end;
    double dx, dd, *dbl_ptr;
    float *flt_ptr;
    int exp;
    char eneg;

    count = 0;
    chars = 0;
    c = 0;
    while ((f = *fmt) != 0)
    {
        if (WHITE(f))
        {
            do
            {
                ++fmt;
                f = *fmt;
            }
            while (WHITE(f));
            do
            {
                c = *buf++;
                if (!c)
                {
                    if (!f || count)
                        return count;
                    else
                        return EOF;
                } else
                    ++chars;
            }
            while (WHITE(c));
            UNGETC();
        } else if (f != '%')
        {
            NEXT(c);
            if (c != f)
                return count;
            ++fmt;
        } else
        {
            assign = TRUE;
            width = INT_MAX;
            char_ptr = NULL;
            ++fmt;
            if (*fmt == '*')
            {
                assign = FALSE;
                ++fmt;
            }
            if (isdigit(*fmt))
            {
                width = 0;
                while (isdigit(*fmt))
                    width = width * 10 + (*fmt++ - '0');
                if (!width)
                    width = INT_MAX;
            }
            size = 0;
            if (*fmt == 'h' || *fmt == 'l')
                size = *fmt++;
            f = *fmt;
            switch (f)
            {
            case 'c':
                if (width == INT_MAX)
                    width = 1;
                if (assign)
                    char_ptr = va_arg(arg_ptr, char *);
                while (width > 0)
                {
                    --width;
                    NEXT(c);
                    if (assign)
                    {
                        *char_ptr++ = (char) c;
                        ++count;
                    }
                }
                break;
            case '[':
                memset(map, 0, 256);
                end = 0;
                ++fmt;
                if (*fmt == '^')
                {
                    ++fmt;
                    end = 1;
                }
                i = 0;
                for (;;)
                {
                    f = (unsigned char) *fmt;
                    switch (f)
                    {
                    case 0:
                        /* avoid skipping past 0 */
                        --fmt;
                        NEXT(c);
                        goto string;
                    case ']':
                        if (i > 0)
                        {
                            NEXT(c);
                            goto string;
                        }
                        /* no break */
                    default:
                        if (fmt[1] == '-' && fmt[2]
                            && f < (unsigned char)fmt[2])
                        {
                            memset(map + f, 1, (unsigned char)fmt[2] - f);
                            fmt += 2;
                        }
                        else
                            map[f] = 1;
                        break;
                    }
                    ++fmt;
                    ++i;
                }
            case 's':
                memset(map, 0, 256);
                map[' '] = 1;
                map['\n'] = 1;
                map['\r'] = 1;
                map['\t'] = 1;
                end = 1;
                do
                {
                    NEXT(c);
                }
                while (WHITE(c));
            string:
                if (assign)
                    char_ptr = va_arg(arg_ptr, char *);
                while (width > 0 && map[(unsigned char) c] != end)
                {
                    --width;
                    if (assign)
                        *char_ptr++ = (char) c;
                    c = *buf++;
                    if (!c)
                        break;
                    else
                        ++chars;
                }
                if (assign)
                {
                    *char_ptr = 0;
                    ++count;
                }
                if (!c)
                    return count;
                else
                    UNGETC();
                break;
            case 'f':
            case 'e':
            case 'E':
            case 'g':
            case 'G':
                neg = ok = FALSE;
                dx = 0.0;
                do
                {
                    NEXT(c);
                }
                while (WHITE(c));
                if (c == '+')
                {
                    NEXT(c);
                    --width;
                } else if (c == '-')
                {
                    neg = TRUE;
                    NEXT(c);
                    --width;
                }
                while (width > 0 && isdigit(c))
                {
                    --width;
                    dx = dx * 10.0 + (double) (c - '0');
                    ok = TRUE;
                    c = *buf++;
                    if (!c)
                        break;
                    else
                        ++chars;
                }
                if (width > 0 && c == '.')
                {
                    --width;
                    dd = 10.0;
                    NEXT(c);
                    while (width > 0 && isdigit(c))
                    {
                        --width;
                        dx += (double) (c - '0') / dd;
                        dd *= 10.0;
                        ok = TRUE;
                        c = *buf++;
                        if (!c)
                            break;
                        else
                            ++chars;
                    }
                }
                if (!ok)
                    return count;
                if (width > 0 && (c == 'e' || c == 'E'))
                {
                    eneg = FALSE;
                    exp = 0;
                    NEXT(c);
                    --width;
                    if (width > 0 && c == '+')
                    {
                        NEXT(c);
                        --width;
                    } else if (width > 0 && c == '-')
                    {
                        eneg = TRUE;
                        NEXT(c);
                        --width;
                    }
                    if (!(width > 0 && isdigit(c)))
                    {
                        UNGETC();
                        return count;
                    }
                    while (width > 0 && isdigit(c))
                    {
                        --width;
                        exp = exp * 10 + (c - '0');
                        c = *buf++;
                        if (!c)
                            break;
                        else
                            ++chars;
                    }
                    if (eneg)
                        exp = -exp;
                    while (exp > 0)
                    {
                        dx *= 10.0;
                        --exp;
                    }
                    while (exp < 0)
                    {
                        dx /= 10.0;
                        ++exp;
                    }
                }
                if (assign)
                {
                    if (neg)
                        dx = -dx;
                    if (size == 'l')
                    {
                        dbl_ptr = va_arg(arg_ptr, double *);
                        *dbl_ptr = dx;
                    }
                    else
                    {
                        flt_ptr = va_arg(arg_ptr, float *);
                        *flt_ptr = (float)dx;
                    }
                    ++count;
                }
                if (!c)
                    return count;
                else
                    UNGETC();
                break;
            case 'i':
                neg = FALSE;
                radix = 10;
                do
                {
                    NEXT(c);
                }
                while (WHITE(c));
                if (!(width > 0 && c == '0'))
                    goto scan_complete_number;
                NEXT(c);
                --width;
                if (width > 0 && (c == 'x' || c == 'X'))
                {
                    NEXT(c);
                    radix = 16;
                    --width;
                }
                else if (width > 0 && (c >= '0' && c <= '7'))
                    radix = 8;
                goto scan_unsigned_number;
            case 'd':
            case 'u':
            case 'o':
            case 'x':
            case 'X':
                do
                {
                    NEXT(c);
                }
                while (WHITE(c));
                switch (f)
                {
                case 'o':
                    radix = 8;
                    break;
                case 'x':
                case 'X':
                    radix = 16;
                    break;
                default:
                    radix = 10;
                    break;
                }
            scan_complete_number:
                neg = FALSE;
                if (width > 0 && c == '+')
                {
                    NEXT(c);
                    --width;
                }
                else if (width > 0 && c == '-' && radix == 10)
                {
                    neg = TRUE;
                    NEXT(c);
                    --width;
                }
            scan_unsigned_number:
                n = 0;
                ok = FALSE;
                while (width > 0)
                {
                    --width;
                    if (isdigit(c))
                        d = c - '0';
                    else if (isupper(c))
                        d = c - 'A' + 10;
                    else if (islower(c))
                        d = c - 'a' + 10;
                    else
                        break;
                    if (d < 0 || d >= radix)
                        break;
                    ok = TRUE;
                    n = n * radix + d;
                    c = *buf++;
                    if (!c)
                        break;
                    else
                        ++chars;
                }
                if (!ok)
                    return count;
                if (assign)
                {
                    if (neg)
                        n = -n;
                    switch (size)
                    {
                    case 'h':
                        short_ptr = va_arg(arg_ptr, short *);
                        *short_ptr = (short) n;
                        break;
                    case 'l':
                        long_ptr = va_arg(arg_ptr, long *);
                        *long_ptr = (long) n;
                        break;
                    default:
                        int_ptr = va_arg(arg_ptr, int *);
                        *int_ptr = (int) n;
                    }
                    ++count;
                }
                if (!c)
                    return count;
                else
                    UNGETC();
                break;
            case 'n':
                if (assign)
                {
                    int_ptr = va_arg(arg_ptr, int *);
                    *int_ptr = chars;
                    ++count;
                }
                break;
            default:
                if (!f) /* % at end of string */
                    return count;
                NEXT(c);
                if (c != f)
                    return count;
                break;
            }
            ++fmt;
        }
    }
    return count;
}