nsprpub/pr/src/io/prscanf.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/nsprpub/pr/src/io/prscanf.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,634 @@
     1.4 +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +/*
    1.10 + * Scan functions for NSPR types
    1.11 + *
    1.12 + * Author: Wan-Teh Chang
    1.13 + *
    1.14 + * Acknowledgment: The implementation is inspired by the source code
    1.15 + * in P.J. Plauger's "The Standard C Library," Prentice-Hall, 1992.
    1.16 + */
    1.17 +
    1.18 +#include <limits.h>
    1.19 +#include <ctype.h>
    1.20 +#include <string.h>
    1.21 +#include <stdlib.h>
    1.22 +#include "prprf.h"
    1.23 +#include "prdtoa.h"
    1.24 +#include "prlog.h"
    1.25 +#include "prerror.h"
    1.26 +
    1.27 +/*
    1.28 + * A function that reads a character from 'stream'.
    1.29 + * Returns the character read, or EOF if end of stream is reached.
    1.30 + */
    1.31 +typedef int (*_PRGetCharFN)(void *stream);
    1.32 +
    1.33 +/*
    1.34 + * A function that pushes the character 'ch' back to 'stream'.
    1.35 + */
    1.36 +typedef void (*_PRUngetCharFN)(void *stream, int ch); 
    1.37 +
    1.38 +/*
    1.39 + * The size specifier for the integer and floating point number
    1.40 + * conversions in format control strings.
    1.41 + */
    1.42 +typedef enum {
    1.43 +    _PR_size_none,  /* No size specifier is given */
    1.44 +    _PR_size_h,     /* The 'h' specifier, suggesting "short" */
    1.45 +    _PR_size_l,     /* The 'l' specifier, suggesting "long" */
    1.46 +    _PR_size_L,     /* The 'L' specifier, meaning a 'long double' */
    1.47 +    _PR_size_ll     /* The 'll' specifier, suggesting "long long" */
    1.48 +} _PRSizeSpec;
    1.49 +
    1.50 +/*
    1.51 + * The collection of data that is passed between the scan function
    1.52 + * and its subordinate functions.  The fields of this structure
    1.53 + * serve as the input or output arguments for these functions.
    1.54 + */
    1.55 +typedef struct {
    1.56 +    _PRGetCharFN get;        /* get a character from input stream */
    1.57 +    _PRUngetCharFN unget;    /* unget (push back) a character */
    1.58 +    void *stream;            /* argument for get and unget */
    1.59 +    va_list ap;              /* the variable argument list */
    1.60 +    int nChar;               /* number of characters read from 'stream' */
    1.61 +
    1.62 +    PRBool assign;           /* assign, or suppress assignment? */
    1.63 +    int width;               /* field width */
    1.64 +    _PRSizeSpec sizeSpec;    /* 'h', 'l', 'L', or 'll' */
    1.65 +
    1.66 +    PRBool converted;        /* is the value actually converted? */
    1.67 +} ScanfState;
    1.68 +
    1.69 +#define GET(state) ((state)->nChar++, (state)->get((state)->stream))
    1.70 +#define UNGET(state, ch) \
    1.71 +        ((state)->nChar--, (state)->unget((state)->stream, ch))
    1.72 +
    1.73 +/*
    1.74 + * The following two macros, GET_IF_WITHIN_WIDTH and WITHIN_WIDTH,
    1.75 + * are always used together.
    1.76 + *
    1.77 + * GET_IF_WITHIN_WIDTH calls the GET macro and assigns its return
    1.78 + * value to 'ch' only if we have not exceeded the field width of
    1.79 + * 'state'.  Therefore, after GET_IF_WITHIN_WIDTH, the value of
    1.80 + * 'ch' is valid only if the macro WITHIN_WIDTH evaluates to true.
    1.81 + */
    1.82 +
    1.83 +#define GET_IF_WITHIN_WIDTH(state, ch) \
    1.84 +        if (--(state)->width >= 0) { \
    1.85 +            (ch) = GET(state); \
    1.86 +        }
    1.87 +#define WITHIN_WIDTH(state) ((state)->width >= 0)
    1.88 +
    1.89 +/*
    1.90 + * _pr_strtoull:
    1.91 + *     Convert a string to an unsigned 64-bit integer.  The string
    1.92 + *     'str' is assumed to be a representation of the integer in
    1.93 + *     base 'base'.
    1.94 + *
    1.95 + * Warning: 
    1.96 + *     - Only handle base 8, 10, and 16.
    1.97 + *     - No overflow checking.
    1.98 + */
    1.99 +
   1.100 +static PRUint64
   1.101 +_pr_strtoull(const char *str, char **endptr, int base)
   1.102 +{
   1.103 +    static const int BASE_MAX = 16;
   1.104 +    static const char digits[] = "0123456789abcdef";
   1.105 +    char *digitPtr;
   1.106 +    PRUint64 x;    /* return value */
   1.107 +    PRInt64 base64;
   1.108 +    const char *cPtr;
   1.109 +    PRBool negative;
   1.110 +    const char *digitStart;
   1.111 +
   1.112 +    PR_ASSERT(base == 0 || base == 8 || base == 10 || base == 16);
   1.113 +    if (base < 0 || base == 1 || base > BASE_MAX) {
   1.114 +        if (endptr) {
   1.115 +            *endptr = (char *) str;
   1.116 +            return LL_ZERO;
   1.117 +        }
   1.118 +    }
   1.119 +
   1.120 +    cPtr = str;
   1.121 +    while (isspace(*cPtr)) {
   1.122 +        ++cPtr;
   1.123 +    }
   1.124 +
   1.125 +    negative = PR_FALSE;
   1.126 +    if (*cPtr == '-') {
   1.127 +        negative = PR_TRUE;
   1.128 +        cPtr++;
   1.129 +    } else if (*cPtr == '+') {
   1.130 +        cPtr++;
   1.131 +    }
   1.132 +
   1.133 +    if (base == 16) {
   1.134 +        if (*cPtr == '0' && (cPtr[1] == 'x' || cPtr[1] == 'X')) {
   1.135 +            cPtr += 2;
   1.136 +        }
   1.137 +    } else if (base == 0) {
   1.138 +        if (*cPtr != '0') {
   1.139 +            base = 10;
   1.140 +        } else if (cPtr[1] == 'x' || cPtr[1] == 'X') {
   1.141 +            base = 16;
   1.142 +            cPtr += 2;
   1.143 +        } else {
   1.144 +            base = 8;
   1.145 +        } 
   1.146 +    }
   1.147 +    PR_ASSERT(base != 0);
   1.148 +    LL_I2L(base64, base);
   1.149 +    digitStart = cPtr;
   1.150 +
   1.151 +    /* Skip leading zeros */
   1.152 +    while (*cPtr == '0') {
   1.153 +        cPtr++;
   1.154 +    }
   1.155 +
   1.156 +    LL_I2L(x, 0);
   1.157 +    while ((digitPtr = (char*)memchr(digits, tolower(*cPtr), base)) != NULL) {
   1.158 +        PRUint64 d;
   1.159 +
   1.160 +        LL_I2L(d, (digitPtr - digits));
   1.161 +        LL_MUL(x, x, base64);
   1.162 +        LL_ADD(x, x, d);
   1.163 +        cPtr++;
   1.164 +    }
   1.165 +
   1.166 +    if (cPtr == digitStart) {
   1.167 +        if (endptr) {
   1.168 +            *endptr = (char *) str;
   1.169 +        }
   1.170 +        return LL_ZERO;
   1.171 +    }
   1.172 +
   1.173 +    if (negative) {
   1.174 +#ifdef HAVE_LONG_LONG
   1.175 +        /* The cast to a signed type is to avoid a compiler warning */
   1.176 +        x = -(PRInt64)x;
   1.177 +#else
   1.178 +        LL_NEG(x, x);
   1.179 +#endif
   1.180 +    }
   1.181 +
   1.182 +    if (endptr) {
   1.183 +        *endptr = (char *) cPtr;
   1.184 +    }
   1.185 +    return x;
   1.186 +}
   1.187 +
   1.188 +/*
   1.189 + * The maximum field width (in number of characters) that is enough
   1.190 + * (may be more than necessary) to represent a 64-bit integer or
   1.191 + * floating point number.
   1.192 + */
   1.193 +#define FMAX 31
   1.194 +#define DECIMAL_POINT '.'
   1.195 +
   1.196 +static PRStatus
   1.197 +GetInt(ScanfState *state, int code)
   1.198 +{
   1.199 +    char buf[FMAX + 1], *p;
   1.200 +    int ch;
   1.201 +    static const char digits[] = "0123456789abcdefABCDEF";
   1.202 +    PRBool seenDigit = PR_FALSE;
   1.203 +    int base;
   1.204 +    int dlen;
   1.205 +
   1.206 +    switch (code) {
   1.207 +        case 'd': case 'u':
   1.208 +            base = 10;
   1.209 +            break;
   1.210 +        case 'i':
   1.211 +            base = 0;
   1.212 +            break;
   1.213 +        case 'x': case 'X': case 'p':
   1.214 +            base = 16;
   1.215 +            break;
   1.216 +        case 'o':
   1.217 +            base = 8;
   1.218 +            break;
   1.219 +        default:
   1.220 +            return PR_FAILURE;
   1.221 +    }
   1.222 +    if (state->width == 0 || state->width > FMAX) {
   1.223 +        state->width = FMAX;
   1.224 +    }
   1.225 +    p = buf;
   1.226 +    GET_IF_WITHIN_WIDTH(state, ch);
   1.227 +    if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
   1.228 +        *p++ = ch;
   1.229 +        GET_IF_WITHIN_WIDTH(state, ch);
   1.230 +    }
   1.231 +    if (WITHIN_WIDTH(state) && ch == '0') {
   1.232 +        seenDigit = PR_TRUE;
   1.233 +        *p++ = ch;
   1.234 +        GET_IF_WITHIN_WIDTH(state, ch);
   1.235 +        if (WITHIN_WIDTH(state)
   1.236 +                && (ch == 'x' || ch == 'X')
   1.237 +                && (base == 0 || base == 16)) {
   1.238 +            base = 16;
   1.239 +            *p++ = ch;
   1.240 +            GET_IF_WITHIN_WIDTH(state, ch);
   1.241 +        } else if (base == 0) {
   1.242 +            base = 8;
   1.243 +        }
   1.244 +    }
   1.245 +    if (base == 0 || base == 10) {
   1.246 +        dlen = 10;
   1.247 +    } else if (base == 8) {
   1.248 +        dlen = 8;
   1.249 +    } else {
   1.250 +        PR_ASSERT(base == 16);
   1.251 +        dlen = 16 + 6; /* 16 digits, plus 6 in uppercase */
   1.252 +    }
   1.253 +    while (WITHIN_WIDTH(state) && memchr(digits, ch, dlen)) {
   1.254 +        *p++ = ch;
   1.255 +        GET_IF_WITHIN_WIDTH(state, ch);
   1.256 +        seenDigit = PR_TRUE;
   1.257 +    }
   1.258 +    if (WITHIN_WIDTH(state)) {
   1.259 +        UNGET(state, ch);
   1.260 +    }
   1.261 +    if (!seenDigit) {
   1.262 +        return PR_FAILURE;
   1.263 +    }
   1.264 +    *p = '\0';
   1.265 +    if (state->assign) {
   1.266 +        if (code == 'd' || code == 'i') {
   1.267 +            if (state->sizeSpec == _PR_size_ll) {
   1.268 +                PRInt64 llval = _pr_strtoull(buf, NULL, base);
   1.269 +                *va_arg(state->ap, PRInt64 *) = llval;
   1.270 +            } else {
   1.271 +                long lval = strtol(buf, NULL, base);
   1.272 +
   1.273 +                if (state->sizeSpec == _PR_size_none) {
   1.274 +                    *va_arg(state->ap, PRIntn *) = lval;
   1.275 +                } else if (state->sizeSpec == _PR_size_h) {
   1.276 +                    *va_arg(state->ap, PRInt16 *) = (PRInt16)lval;
   1.277 +                } else if (state->sizeSpec == _PR_size_l) {
   1.278 +                    *va_arg(state->ap, PRInt32 *) = lval;
   1.279 +                } else {
   1.280 +                    return PR_FAILURE;
   1.281 +                }
   1.282 +            }
   1.283 +        } else {
   1.284 +            if (state->sizeSpec == _PR_size_ll) {
   1.285 +                PRUint64 llval = _pr_strtoull(buf, NULL, base);
   1.286 +                *va_arg(state->ap, PRUint64 *) = llval;
   1.287 +            } else {
   1.288 +                unsigned long lval = strtoul(buf, NULL, base);
   1.289 +
   1.290 +                if (state->sizeSpec == _PR_size_none) {
   1.291 +                    *va_arg(state->ap, PRUintn *) = lval;
   1.292 +                } else if (state->sizeSpec == _PR_size_h) {
   1.293 +                    *va_arg(state->ap, PRUint16 *) = (PRUint16)lval;
   1.294 +                } else if (state->sizeSpec == _PR_size_l) {
   1.295 +                    *va_arg(state->ap, PRUint32 *) = lval;
   1.296 +                } else {
   1.297 +                    return PR_FAILURE;
   1.298 +                }
   1.299 +            }
   1.300 +        }
   1.301 +        state->converted = PR_TRUE;
   1.302 +    }
   1.303 +    return PR_SUCCESS;
   1.304 +}
   1.305 +
   1.306 +static PRStatus
   1.307 +GetFloat(ScanfState *state)
   1.308 +{
   1.309 +    char buf[FMAX + 1], *p;
   1.310 +    int ch;
   1.311 +    PRBool seenDigit = PR_FALSE;
   1.312 +
   1.313 +    if (state->width == 0 || state->width > FMAX) {
   1.314 +        state->width = FMAX;
   1.315 +    }
   1.316 +    p = buf;
   1.317 +    GET_IF_WITHIN_WIDTH(state, ch);
   1.318 +    if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
   1.319 +        *p++ = ch;
   1.320 +        GET_IF_WITHIN_WIDTH(state, ch);
   1.321 +    }
   1.322 +    while (WITHIN_WIDTH(state) && isdigit(ch)) {
   1.323 +        *p++ = ch;
   1.324 +        GET_IF_WITHIN_WIDTH(state, ch);
   1.325 +        seenDigit = PR_TRUE;
   1.326 +    }
   1.327 +    if (WITHIN_WIDTH(state) && ch == DECIMAL_POINT) {
   1.328 +        *p++ = ch;
   1.329 +        GET_IF_WITHIN_WIDTH(state, ch);
   1.330 +        while (WITHIN_WIDTH(state) && isdigit(ch)) {
   1.331 +            *p++ = ch;
   1.332 +            GET_IF_WITHIN_WIDTH(state, ch);
   1.333 +            seenDigit = PR_TRUE;
   1.334 +        }
   1.335 +    }
   1.336 +
   1.337 +    /*
   1.338 +     * This is not robust.  For example, "1.2e+" would confuse
   1.339 +     * the code below to read 'e' and '+', only to realize that
   1.340 +     * it should have stopped at "1.2".  But we can't push back
   1.341 +     * more than one character, so there is nothing I can do.
   1.342 +     */
   1.343 +
   1.344 +    /* Parse exponent */
   1.345 +    if (WITHIN_WIDTH(state) && (ch == 'e' || ch == 'E') && seenDigit) {
   1.346 +        *p++ = ch;
   1.347 +        GET_IF_WITHIN_WIDTH(state, ch);
   1.348 +        if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
   1.349 +            *p++ = ch;
   1.350 +            GET_IF_WITHIN_WIDTH(state, ch);
   1.351 +        }
   1.352 +        while (WITHIN_WIDTH(state) && isdigit(ch)) {
   1.353 +            *p++ = ch;
   1.354 +            GET_IF_WITHIN_WIDTH(state, ch);
   1.355 +        }
   1.356 +    }
   1.357 +    if (WITHIN_WIDTH(state)) {
   1.358 +        UNGET(state, ch);
   1.359 +    }
   1.360 +    if (!seenDigit) {
   1.361 +        return PR_FAILURE;
   1.362 +    }
   1.363 +    *p = '\0';
   1.364 +    if (state->assign) {
   1.365 +        PRFloat64 dval = PR_strtod(buf, NULL);
   1.366 +
   1.367 +        state->converted = PR_TRUE;
   1.368 +        if (state->sizeSpec == _PR_size_l) {
   1.369 +            *va_arg(state->ap, PRFloat64 *) = dval;
   1.370 +        } else if (state->sizeSpec == _PR_size_L) {
   1.371 +#if defined(OSF1) || defined(IRIX)
   1.372 +            *va_arg(state->ap, double *) = dval;
   1.373 +#else
   1.374 +            *va_arg(state->ap, long double *) = dval;
   1.375 +#endif
   1.376 +        } else {
   1.377 +            *va_arg(state->ap, float *) = (float) dval;
   1.378 +        }
   1.379 +    }
   1.380 +    return PR_SUCCESS;
   1.381 +}
   1.382 +
   1.383 +/*
   1.384 + * Convert, and return the end of the conversion spec.
   1.385 + * Return NULL on error.
   1.386 + */
   1.387 +
   1.388 +static const char *
   1.389 +Convert(ScanfState *state, const char *fmt)
   1.390 +{
   1.391 +    const char *cPtr;
   1.392 +    int ch;
   1.393 +    char *cArg = NULL;
   1.394 +
   1.395 +    state->converted = PR_FALSE;
   1.396 +    cPtr = fmt;
   1.397 +    if (*cPtr != 'c' && *cPtr != 'n' && *cPtr != '[') {
   1.398 +        do {
   1.399 +            ch = GET(state);
   1.400 +        } while (isspace(ch));
   1.401 +        UNGET(state, ch);
   1.402 +    }
   1.403 +    switch (*cPtr) {
   1.404 +        case 'c':
   1.405 +            if (state->assign) {
   1.406 +                cArg = va_arg(state->ap, char *);
   1.407 +            }
   1.408 +            if (state->width == 0) {
   1.409 +                state->width = 1;
   1.410 +            }
   1.411 +            for (; state->width > 0; state->width--) {
   1.412 +                ch = GET(state);
   1.413 +                if (ch == EOF) {
   1.414 +                    return NULL;
   1.415 +                } else if (state->assign) {
   1.416 +                    *cArg++ = ch;
   1.417 +                }
   1.418 +            }
   1.419 +            if (state->assign) {
   1.420 +                state->converted = PR_TRUE;
   1.421 +            }
   1.422 +            break;
   1.423 +        case 'p':
   1.424 +        case 'd': case 'i': case 'o':
   1.425 +        case 'u': case 'x': case 'X':
   1.426 +            if (GetInt(state, *cPtr) == PR_FAILURE) {
   1.427 +                return NULL;
   1.428 +            }
   1.429 +            break;
   1.430 +        case 'e': case 'E': case 'f':
   1.431 +        case 'g': case 'G':
   1.432 +            if (GetFloat(state) == PR_FAILURE) {
   1.433 +                return NULL;
   1.434 +            }
   1.435 +            break;
   1.436 +        case 'n':
   1.437 +            /* do not consume any input */
   1.438 +            if (state->assign) {
   1.439 +                switch (state->sizeSpec) {
   1.440 +                    case _PR_size_none:
   1.441 +                        *va_arg(state->ap, PRIntn *) = state->nChar;
   1.442 +                        break;
   1.443 +                    case _PR_size_h:
   1.444 +                        *va_arg(state->ap, PRInt16 *) = state->nChar;
   1.445 +                        break;
   1.446 +                    case _PR_size_l:
   1.447 +                        *va_arg(state->ap, PRInt32 *) = state->nChar;
   1.448 +                        break;
   1.449 +                    case _PR_size_ll:
   1.450 +                        LL_I2L(*va_arg(state->ap, PRInt64 *), state->nChar);
   1.451 +                        break;
   1.452 +                    default:
   1.453 +                        PR_ASSERT(0);
   1.454 +                }
   1.455 +            }
   1.456 +            break;
   1.457 +        case 's':
   1.458 +            if (state->width == 0) {
   1.459 +                state->width = INT_MAX;
   1.460 +            }
   1.461 +            if (state->assign) {
   1.462 +                cArg = va_arg(state->ap, char *);
   1.463 +            }
   1.464 +            for (; state->width > 0; state->width--) {
   1.465 +                ch = GET(state);
   1.466 +                if ((ch == EOF) || isspace(ch)) {
   1.467 +                    UNGET(state, ch);
   1.468 +                    break;
   1.469 +                }
   1.470 +                if (state->assign) {
   1.471 +                    *cArg++ = ch;
   1.472 +                }
   1.473 +            }
   1.474 +            if (state->assign) {
   1.475 +                *cArg = '\0';
   1.476 +                state->converted = PR_TRUE;
   1.477 +            }
   1.478 +            break;
   1.479 +        case '%':
   1.480 +            ch = GET(state);
   1.481 +            if (ch != '%') {
   1.482 +                UNGET(state, ch);
   1.483 +                return NULL;
   1.484 +            }
   1.485 +            break;
   1.486 +        case '[':
   1.487 +            {
   1.488 +                PRBool complement = PR_FALSE;
   1.489 +                const char *closeBracket;
   1.490 +                size_t n;
   1.491 +
   1.492 +                if (*++cPtr == '^') {
   1.493 +                    complement = PR_TRUE;
   1.494 +                    cPtr++;
   1.495 +                }
   1.496 +                closeBracket = strchr(*cPtr == ']' ? cPtr + 1 : cPtr, ']');
   1.497 +                if (closeBracket == NULL) {
   1.498 +                    return NULL;
   1.499 +                }
   1.500 +                n = closeBracket - cPtr;
   1.501 +                if (state->width == 0) {
   1.502 +                    state->width = INT_MAX;
   1.503 +                }
   1.504 +                if (state->assign) {
   1.505 +                    cArg = va_arg(state->ap, char *);
   1.506 +                }
   1.507 +                for (; state->width > 0; state->width--) {
   1.508 +                    ch = GET(state);
   1.509 +                    if ((ch == EOF) 
   1.510 +                            || (!complement && !memchr(cPtr, ch, n))
   1.511 +                            || (complement && memchr(cPtr, ch, n))) {
   1.512 +                        UNGET(state, ch);
   1.513 +                        break;
   1.514 +                    }
   1.515 +                    if (state->assign) {
   1.516 +                        *cArg++ = ch;
   1.517 +                    }
   1.518 +                }
   1.519 +                if (state->assign) {
   1.520 +                    *cArg = '\0';
   1.521 +                    state->converted = PR_TRUE;
   1.522 +                }
   1.523 +                cPtr = closeBracket;
   1.524 +            }
   1.525 +            break;
   1.526 +        default:
   1.527 +            return NULL;
   1.528 +    }
   1.529 +    return cPtr;
   1.530 +}
   1.531 +
   1.532 +static PRInt32
   1.533 +DoScanf(ScanfState *state, const char *fmt)
   1.534 +{
   1.535 +    PRInt32 nConverted = 0;
   1.536 +    const char *cPtr;
   1.537 +    int ch;
   1.538 +
   1.539 +    state->nChar = 0;
   1.540 +    cPtr = fmt;
   1.541 +    while (1) {
   1.542 +        if (isspace(*cPtr)) {
   1.543 +            /* white space: skip */
   1.544 +            do {
   1.545 +                cPtr++;
   1.546 +            } while (isspace(*cPtr));
   1.547 +            do {
   1.548 +                ch = GET(state);
   1.549 +            } while (isspace(ch));
   1.550 +            UNGET(state, ch);
   1.551 +        } else if (*cPtr == '%') {
   1.552 +            /* format spec: convert */
   1.553 +            cPtr++;
   1.554 +            state->assign = PR_TRUE;
   1.555 +            if (*cPtr == '*') {
   1.556 +                cPtr++;
   1.557 +                state->assign = PR_FALSE;
   1.558 +            }
   1.559 +            for (state->width = 0; isdigit(*cPtr); cPtr++) {
   1.560 +                state->width = state->width * 10 + *cPtr - '0';
   1.561 +            }
   1.562 +            state->sizeSpec = _PR_size_none;
   1.563 +            if (*cPtr == 'h') {
   1.564 +                cPtr++;
   1.565 +                state->sizeSpec = _PR_size_h;
   1.566 +            } else if (*cPtr == 'l') {
   1.567 +                cPtr++;
   1.568 +                if (*cPtr == 'l') {
   1.569 +                    cPtr++;
   1.570 +                    state->sizeSpec = _PR_size_ll;
   1.571 +                } else {
   1.572 +                    state->sizeSpec = _PR_size_l;
   1.573 +                }
   1.574 +            } else if (*cPtr == 'L') {
   1.575 +                cPtr++;
   1.576 +                state->sizeSpec = _PR_size_L;
   1.577 +            }
   1.578 +            cPtr = Convert(state, cPtr);
   1.579 +            if (cPtr == NULL) {
   1.580 +                return (nConverted > 0 ? nConverted : EOF);
   1.581 +            }
   1.582 +            if (state->converted) {
   1.583 +                nConverted++;
   1.584 +            }
   1.585 +            cPtr++;
   1.586 +        } else {
   1.587 +            /* others: must match */
   1.588 +            if (*cPtr == '\0') {
   1.589 +                return nConverted;
   1.590 +            }
   1.591 +            ch = GET(state);
   1.592 +            if (ch != *cPtr) {
   1.593 +                UNGET(state, ch);
   1.594 +                return nConverted;
   1.595 +            }
   1.596 +            cPtr++;
   1.597 +        }
   1.598 +    }
   1.599 +}
   1.600 +
   1.601 +static int
   1.602 +StringGetChar(void *stream)
   1.603 +{
   1.604 +    char *cPtr = *((char **) stream);
   1.605 +
   1.606 +    if (*cPtr == '\0') {
   1.607 +        return EOF;
   1.608 +    } else {
   1.609 +        *((char **) stream) = cPtr + 1;
   1.610 +        return (unsigned char) *cPtr;
   1.611 +    }
   1.612 +}
   1.613 +
   1.614 +static void
   1.615 +StringUngetChar(void *stream, int ch)
   1.616 +{
   1.617 +    char *cPtr = *((char **) stream);
   1.618 +
   1.619 +    if (ch != EOF) {
   1.620 +        *((char **) stream) = cPtr - 1;
   1.621 +    }
   1.622 +}
   1.623 +
   1.624 +PR_IMPLEMENT(PRInt32)
   1.625 +PR_sscanf(const char *buf, const char *fmt, ...)
   1.626 +{
   1.627 +    PRInt32 rv;
   1.628 +    ScanfState state;
   1.629 +
   1.630 +    state.get = &StringGetChar;
   1.631 +    state.unget = &StringUngetChar;
   1.632 +    state.stream = (void *) &buf;
   1.633 +    va_start(state.ap, fmt);
   1.634 +    rv = DoScanf(&state, fmt);
   1.635 +    va_end(state.ap);
   1.636 +    return rv;
   1.637 +}

mercurial