Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | /* |
michael@0 | 7 | * Scan functions for NSPR types |
michael@0 | 8 | * |
michael@0 | 9 | * Author: Wan-Teh Chang |
michael@0 | 10 | * |
michael@0 | 11 | * Acknowledgment: The implementation is inspired by the source code |
michael@0 | 12 | * in P.J. Plauger's "The Standard C Library," Prentice-Hall, 1992. |
michael@0 | 13 | */ |
michael@0 | 14 | |
michael@0 | 15 | #include <limits.h> |
michael@0 | 16 | #include <ctype.h> |
michael@0 | 17 | #include <string.h> |
michael@0 | 18 | #include <stdlib.h> |
michael@0 | 19 | #include "prprf.h" |
michael@0 | 20 | #include "prdtoa.h" |
michael@0 | 21 | #include "prlog.h" |
michael@0 | 22 | #include "prerror.h" |
michael@0 | 23 | |
michael@0 | 24 | /* |
michael@0 | 25 | * A function that reads a character from 'stream'. |
michael@0 | 26 | * Returns the character read, or EOF if end of stream is reached. |
michael@0 | 27 | */ |
michael@0 | 28 | typedef int (*_PRGetCharFN)(void *stream); |
michael@0 | 29 | |
michael@0 | 30 | /* |
michael@0 | 31 | * A function that pushes the character 'ch' back to 'stream'. |
michael@0 | 32 | */ |
michael@0 | 33 | typedef void (*_PRUngetCharFN)(void *stream, int ch); |
michael@0 | 34 | |
michael@0 | 35 | /* |
michael@0 | 36 | * The size specifier for the integer and floating point number |
michael@0 | 37 | * conversions in format control strings. |
michael@0 | 38 | */ |
michael@0 | 39 | typedef enum { |
michael@0 | 40 | _PR_size_none, /* No size specifier is given */ |
michael@0 | 41 | _PR_size_h, /* The 'h' specifier, suggesting "short" */ |
michael@0 | 42 | _PR_size_l, /* The 'l' specifier, suggesting "long" */ |
michael@0 | 43 | _PR_size_L, /* The 'L' specifier, meaning a 'long double' */ |
michael@0 | 44 | _PR_size_ll /* The 'll' specifier, suggesting "long long" */ |
michael@0 | 45 | } _PRSizeSpec; |
michael@0 | 46 | |
michael@0 | 47 | /* |
michael@0 | 48 | * The collection of data that is passed between the scan function |
michael@0 | 49 | * and its subordinate functions. The fields of this structure |
michael@0 | 50 | * serve as the input or output arguments for these functions. |
michael@0 | 51 | */ |
michael@0 | 52 | typedef struct { |
michael@0 | 53 | _PRGetCharFN get; /* get a character from input stream */ |
michael@0 | 54 | _PRUngetCharFN unget; /* unget (push back) a character */ |
michael@0 | 55 | void *stream; /* argument for get and unget */ |
michael@0 | 56 | va_list ap; /* the variable argument list */ |
michael@0 | 57 | int nChar; /* number of characters read from 'stream' */ |
michael@0 | 58 | |
michael@0 | 59 | PRBool assign; /* assign, or suppress assignment? */ |
michael@0 | 60 | int width; /* field width */ |
michael@0 | 61 | _PRSizeSpec sizeSpec; /* 'h', 'l', 'L', or 'll' */ |
michael@0 | 62 | |
michael@0 | 63 | PRBool converted; /* is the value actually converted? */ |
michael@0 | 64 | } ScanfState; |
michael@0 | 65 | |
michael@0 | 66 | #define GET(state) ((state)->nChar++, (state)->get((state)->stream)) |
michael@0 | 67 | #define UNGET(state, ch) \ |
michael@0 | 68 | ((state)->nChar--, (state)->unget((state)->stream, ch)) |
michael@0 | 69 | |
michael@0 | 70 | /* |
michael@0 | 71 | * The following two macros, GET_IF_WITHIN_WIDTH and WITHIN_WIDTH, |
michael@0 | 72 | * are always used together. |
michael@0 | 73 | * |
michael@0 | 74 | * GET_IF_WITHIN_WIDTH calls the GET macro and assigns its return |
michael@0 | 75 | * value to 'ch' only if we have not exceeded the field width of |
michael@0 | 76 | * 'state'. Therefore, after GET_IF_WITHIN_WIDTH, the value of |
michael@0 | 77 | * 'ch' is valid only if the macro WITHIN_WIDTH evaluates to true. |
michael@0 | 78 | */ |
michael@0 | 79 | |
michael@0 | 80 | #define GET_IF_WITHIN_WIDTH(state, ch) \ |
michael@0 | 81 | if (--(state)->width >= 0) { \ |
michael@0 | 82 | (ch) = GET(state); \ |
michael@0 | 83 | } |
michael@0 | 84 | #define WITHIN_WIDTH(state) ((state)->width >= 0) |
michael@0 | 85 | |
michael@0 | 86 | /* |
michael@0 | 87 | * _pr_strtoull: |
michael@0 | 88 | * Convert a string to an unsigned 64-bit integer. The string |
michael@0 | 89 | * 'str' is assumed to be a representation of the integer in |
michael@0 | 90 | * base 'base'. |
michael@0 | 91 | * |
michael@0 | 92 | * Warning: |
michael@0 | 93 | * - Only handle base 8, 10, and 16. |
michael@0 | 94 | * - No overflow checking. |
michael@0 | 95 | */ |
michael@0 | 96 | |
michael@0 | 97 | static PRUint64 |
michael@0 | 98 | _pr_strtoull(const char *str, char **endptr, int base) |
michael@0 | 99 | { |
michael@0 | 100 | static const int BASE_MAX = 16; |
michael@0 | 101 | static const char digits[] = "0123456789abcdef"; |
michael@0 | 102 | char *digitPtr; |
michael@0 | 103 | PRUint64 x; /* return value */ |
michael@0 | 104 | PRInt64 base64; |
michael@0 | 105 | const char *cPtr; |
michael@0 | 106 | PRBool negative; |
michael@0 | 107 | const char *digitStart; |
michael@0 | 108 | |
michael@0 | 109 | PR_ASSERT(base == 0 || base == 8 || base == 10 || base == 16); |
michael@0 | 110 | if (base < 0 || base == 1 || base > BASE_MAX) { |
michael@0 | 111 | if (endptr) { |
michael@0 | 112 | *endptr = (char *) str; |
michael@0 | 113 | return LL_ZERO; |
michael@0 | 114 | } |
michael@0 | 115 | } |
michael@0 | 116 | |
michael@0 | 117 | cPtr = str; |
michael@0 | 118 | while (isspace(*cPtr)) { |
michael@0 | 119 | ++cPtr; |
michael@0 | 120 | } |
michael@0 | 121 | |
michael@0 | 122 | negative = PR_FALSE; |
michael@0 | 123 | if (*cPtr == '-') { |
michael@0 | 124 | negative = PR_TRUE; |
michael@0 | 125 | cPtr++; |
michael@0 | 126 | } else if (*cPtr == '+') { |
michael@0 | 127 | cPtr++; |
michael@0 | 128 | } |
michael@0 | 129 | |
michael@0 | 130 | if (base == 16) { |
michael@0 | 131 | if (*cPtr == '0' && (cPtr[1] == 'x' || cPtr[1] == 'X')) { |
michael@0 | 132 | cPtr += 2; |
michael@0 | 133 | } |
michael@0 | 134 | } else if (base == 0) { |
michael@0 | 135 | if (*cPtr != '0') { |
michael@0 | 136 | base = 10; |
michael@0 | 137 | } else if (cPtr[1] == 'x' || cPtr[1] == 'X') { |
michael@0 | 138 | base = 16; |
michael@0 | 139 | cPtr += 2; |
michael@0 | 140 | } else { |
michael@0 | 141 | base = 8; |
michael@0 | 142 | } |
michael@0 | 143 | } |
michael@0 | 144 | PR_ASSERT(base != 0); |
michael@0 | 145 | LL_I2L(base64, base); |
michael@0 | 146 | digitStart = cPtr; |
michael@0 | 147 | |
michael@0 | 148 | /* Skip leading zeros */ |
michael@0 | 149 | while (*cPtr == '0') { |
michael@0 | 150 | cPtr++; |
michael@0 | 151 | } |
michael@0 | 152 | |
michael@0 | 153 | LL_I2L(x, 0); |
michael@0 | 154 | while ((digitPtr = (char*)memchr(digits, tolower(*cPtr), base)) != NULL) { |
michael@0 | 155 | PRUint64 d; |
michael@0 | 156 | |
michael@0 | 157 | LL_I2L(d, (digitPtr - digits)); |
michael@0 | 158 | LL_MUL(x, x, base64); |
michael@0 | 159 | LL_ADD(x, x, d); |
michael@0 | 160 | cPtr++; |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | if (cPtr == digitStart) { |
michael@0 | 164 | if (endptr) { |
michael@0 | 165 | *endptr = (char *) str; |
michael@0 | 166 | } |
michael@0 | 167 | return LL_ZERO; |
michael@0 | 168 | } |
michael@0 | 169 | |
michael@0 | 170 | if (negative) { |
michael@0 | 171 | #ifdef HAVE_LONG_LONG |
michael@0 | 172 | /* The cast to a signed type is to avoid a compiler warning */ |
michael@0 | 173 | x = -(PRInt64)x; |
michael@0 | 174 | #else |
michael@0 | 175 | LL_NEG(x, x); |
michael@0 | 176 | #endif |
michael@0 | 177 | } |
michael@0 | 178 | |
michael@0 | 179 | if (endptr) { |
michael@0 | 180 | *endptr = (char *) cPtr; |
michael@0 | 181 | } |
michael@0 | 182 | return x; |
michael@0 | 183 | } |
michael@0 | 184 | |
michael@0 | 185 | /* |
michael@0 | 186 | * The maximum field width (in number of characters) that is enough |
michael@0 | 187 | * (may be more than necessary) to represent a 64-bit integer or |
michael@0 | 188 | * floating point number. |
michael@0 | 189 | */ |
michael@0 | 190 | #define FMAX 31 |
michael@0 | 191 | #define DECIMAL_POINT '.' |
michael@0 | 192 | |
michael@0 | 193 | static PRStatus |
michael@0 | 194 | GetInt(ScanfState *state, int code) |
michael@0 | 195 | { |
michael@0 | 196 | char buf[FMAX + 1], *p; |
michael@0 | 197 | int ch; |
michael@0 | 198 | static const char digits[] = "0123456789abcdefABCDEF"; |
michael@0 | 199 | PRBool seenDigit = PR_FALSE; |
michael@0 | 200 | int base; |
michael@0 | 201 | int dlen; |
michael@0 | 202 | |
michael@0 | 203 | switch (code) { |
michael@0 | 204 | case 'd': case 'u': |
michael@0 | 205 | base = 10; |
michael@0 | 206 | break; |
michael@0 | 207 | case 'i': |
michael@0 | 208 | base = 0; |
michael@0 | 209 | break; |
michael@0 | 210 | case 'x': case 'X': case 'p': |
michael@0 | 211 | base = 16; |
michael@0 | 212 | break; |
michael@0 | 213 | case 'o': |
michael@0 | 214 | base = 8; |
michael@0 | 215 | break; |
michael@0 | 216 | default: |
michael@0 | 217 | return PR_FAILURE; |
michael@0 | 218 | } |
michael@0 | 219 | if (state->width == 0 || state->width > FMAX) { |
michael@0 | 220 | state->width = FMAX; |
michael@0 | 221 | } |
michael@0 | 222 | p = buf; |
michael@0 | 223 | GET_IF_WITHIN_WIDTH(state, ch); |
michael@0 | 224 | if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) { |
michael@0 | 225 | *p++ = ch; |
michael@0 | 226 | GET_IF_WITHIN_WIDTH(state, ch); |
michael@0 | 227 | } |
michael@0 | 228 | if (WITHIN_WIDTH(state) && ch == '0') { |
michael@0 | 229 | seenDigit = PR_TRUE; |
michael@0 | 230 | *p++ = ch; |
michael@0 | 231 | GET_IF_WITHIN_WIDTH(state, ch); |
michael@0 | 232 | if (WITHIN_WIDTH(state) |
michael@0 | 233 | && (ch == 'x' || ch == 'X') |
michael@0 | 234 | && (base == 0 || base == 16)) { |
michael@0 | 235 | base = 16; |
michael@0 | 236 | *p++ = ch; |
michael@0 | 237 | GET_IF_WITHIN_WIDTH(state, ch); |
michael@0 | 238 | } else if (base == 0) { |
michael@0 | 239 | base = 8; |
michael@0 | 240 | } |
michael@0 | 241 | } |
michael@0 | 242 | if (base == 0 || base == 10) { |
michael@0 | 243 | dlen = 10; |
michael@0 | 244 | } else if (base == 8) { |
michael@0 | 245 | dlen = 8; |
michael@0 | 246 | } else { |
michael@0 | 247 | PR_ASSERT(base == 16); |
michael@0 | 248 | dlen = 16 + 6; /* 16 digits, plus 6 in uppercase */ |
michael@0 | 249 | } |
michael@0 | 250 | while (WITHIN_WIDTH(state) && memchr(digits, ch, dlen)) { |
michael@0 | 251 | *p++ = ch; |
michael@0 | 252 | GET_IF_WITHIN_WIDTH(state, ch); |
michael@0 | 253 | seenDigit = PR_TRUE; |
michael@0 | 254 | } |
michael@0 | 255 | if (WITHIN_WIDTH(state)) { |
michael@0 | 256 | UNGET(state, ch); |
michael@0 | 257 | } |
michael@0 | 258 | if (!seenDigit) { |
michael@0 | 259 | return PR_FAILURE; |
michael@0 | 260 | } |
michael@0 | 261 | *p = '\0'; |
michael@0 | 262 | if (state->assign) { |
michael@0 | 263 | if (code == 'd' || code == 'i') { |
michael@0 | 264 | if (state->sizeSpec == _PR_size_ll) { |
michael@0 | 265 | PRInt64 llval = _pr_strtoull(buf, NULL, base); |
michael@0 | 266 | *va_arg(state->ap, PRInt64 *) = llval; |
michael@0 | 267 | } else { |
michael@0 | 268 | long lval = strtol(buf, NULL, base); |
michael@0 | 269 | |
michael@0 | 270 | if (state->sizeSpec == _PR_size_none) { |
michael@0 | 271 | *va_arg(state->ap, PRIntn *) = lval; |
michael@0 | 272 | } else if (state->sizeSpec == _PR_size_h) { |
michael@0 | 273 | *va_arg(state->ap, PRInt16 *) = (PRInt16)lval; |
michael@0 | 274 | } else if (state->sizeSpec == _PR_size_l) { |
michael@0 | 275 | *va_arg(state->ap, PRInt32 *) = lval; |
michael@0 | 276 | } else { |
michael@0 | 277 | return PR_FAILURE; |
michael@0 | 278 | } |
michael@0 | 279 | } |
michael@0 | 280 | } else { |
michael@0 | 281 | if (state->sizeSpec == _PR_size_ll) { |
michael@0 | 282 | PRUint64 llval = _pr_strtoull(buf, NULL, base); |
michael@0 | 283 | *va_arg(state->ap, PRUint64 *) = llval; |
michael@0 | 284 | } else { |
michael@0 | 285 | unsigned long lval = strtoul(buf, NULL, base); |
michael@0 | 286 | |
michael@0 | 287 | if (state->sizeSpec == _PR_size_none) { |
michael@0 | 288 | *va_arg(state->ap, PRUintn *) = lval; |
michael@0 | 289 | } else if (state->sizeSpec == _PR_size_h) { |
michael@0 | 290 | *va_arg(state->ap, PRUint16 *) = (PRUint16)lval; |
michael@0 | 291 | } else if (state->sizeSpec == _PR_size_l) { |
michael@0 | 292 | *va_arg(state->ap, PRUint32 *) = lval; |
michael@0 | 293 | } else { |
michael@0 | 294 | return PR_FAILURE; |
michael@0 | 295 | } |
michael@0 | 296 | } |
michael@0 | 297 | } |
michael@0 | 298 | state->converted = PR_TRUE; |
michael@0 | 299 | } |
michael@0 | 300 | return PR_SUCCESS; |
michael@0 | 301 | } |
michael@0 | 302 | |
michael@0 | 303 | static PRStatus |
michael@0 | 304 | GetFloat(ScanfState *state) |
michael@0 | 305 | { |
michael@0 | 306 | char buf[FMAX + 1], *p; |
michael@0 | 307 | int ch; |
michael@0 | 308 | PRBool seenDigit = PR_FALSE; |
michael@0 | 309 | |
michael@0 | 310 | if (state->width == 0 || state->width > FMAX) { |
michael@0 | 311 | state->width = FMAX; |
michael@0 | 312 | } |
michael@0 | 313 | p = buf; |
michael@0 | 314 | GET_IF_WITHIN_WIDTH(state, ch); |
michael@0 | 315 | if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) { |
michael@0 | 316 | *p++ = ch; |
michael@0 | 317 | GET_IF_WITHIN_WIDTH(state, ch); |
michael@0 | 318 | } |
michael@0 | 319 | while (WITHIN_WIDTH(state) && isdigit(ch)) { |
michael@0 | 320 | *p++ = ch; |
michael@0 | 321 | GET_IF_WITHIN_WIDTH(state, ch); |
michael@0 | 322 | seenDigit = PR_TRUE; |
michael@0 | 323 | } |
michael@0 | 324 | if (WITHIN_WIDTH(state) && ch == DECIMAL_POINT) { |
michael@0 | 325 | *p++ = ch; |
michael@0 | 326 | GET_IF_WITHIN_WIDTH(state, ch); |
michael@0 | 327 | while (WITHIN_WIDTH(state) && isdigit(ch)) { |
michael@0 | 328 | *p++ = ch; |
michael@0 | 329 | GET_IF_WITHIN_WIDTH(state, ch); |
michael@0 | 330 | seenDigit = PR_TRUE; |
michael@0 | 331 | } |
michael@0 | 332 | } |
michael@0 | 333 | |
michael@0 | 334 | /* |
michael@0 | 335 | * This is not robust. For example, "1.2e+" would confuse |
michael@0 | 336 | * the code below to read 'e' and '+', only to realize that |
michael@0 | 337 | * it should have stopped at "1.2". But we can't push back |
michael@0 | 338 | * more than one character, so there is nothing I can do. |
michael@0 | 339 | */ |
michael@0 | 340 | |
michael@0 | 341 | /* Parse exponent */ |
michael@0 | 342 | if (WITHIN_WIDTH(state) && (ch == 'e' || ch == 'E') && seenDigit) { |
michael@0 | 343 | *p++ = ch; |
michael@0 | 344 | GET_IF_WITHIN_WIDTH(state, ch); |
michael@0 | 345 | if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) { |
michael@0 | 346 | *p++ = ch; |
michael@0 | 347 | GET_IF_WITHIN_WIDTH(state, ch); |
michael@0 | 348 | } |
michael@0 | 349 | while (WITHIN_WIDTH(state) && isdigit(ch)) { |
michael@0 | 350 | *p++ = ch; |
michael@0 | 351 | GET_IF_WITHIN_WIDTH(state, ch); |
michael@0 | 352 | } |
michael@0 | 353 | } |
michael@0 | 354 | if (WITHIN_WIDTH(state)) { |
michael@0 | 355 | UNGET(state, ch); |
michael@0 | 356 | } |
michael@0 | 357 | if (!seenDigit) { |
michael@0 | 358 | return PR_FAILURE; |
michael@0 | 359 | } |
michael@0 | 360 | *p = '\0'; |
michael@0 | 361 | if (state->assign) { |
michael@0 | 362 | PRFloat64 dval = PR_strtod(buf, NULL); |
michael@0 | 363 | |
michael@0 | 364 | state->converted = PR_TRUE; |
michael@0 | 365 | if (state->sizeSpec == _PR_size_l) { |
michael@0 | 366 | *va_arg(state->ap, PRFloat64 *) = dval; |
michael@0 | 367 | } else if (state->sizeSpec == _PR_size_L) { |
michael@0 | 368 | #if defined(OSF1) || defined(IRIX) |
michael@0 | 369 | *va_arg(state->ap, double *) = dval; |
michael@0 | 370 | #else |
michael@0 | 371 | *va_arg(state->ap, long double *) = dval; |
michael@0 | 372 | #endif |
michael@0 | 373 | } else { |
michael@0 | 374 | *va_arg(state->ap, float *) = (float) dval; |
michael@0 | 375 | } |
michael@0 | 376 | } |
michael@0 | 377 | return PR_SUCCESS; |
michael@0 | 378 | } |
michael@0 | 379 | |
michael@0 | 380 | /* |
michael@0 | 381 | * Convert, and return the end of the conversion spec. |
michael@0 | 382 | * Return NULL on error. |
michael@0 | 383 | */ |
michael@0 | 384 | |
michael@0 | 385 | static const char * |
michael@0 | 386 | Convert(ScanfState *state, const char *fmt) |
michael@0 | 387 | { |
michael@0 | 388 | const char *cPtr; |
michael@0 | 389 | int ch; |
michael@0 | 390 | char *cArg = NULL; |
michael@0 | 391 | |
michael@0 | 392 | state->converted = PR_FALSE; |
michael@0 | 393 | cPtr = fmt; |
michael@0 | 394 | if (*cPtr != 'c' && *cPtr != 'n' && *cPtr != '[') { |
michael@0 | 395 | do { |
michael@0 | 396 | ch = GET(state); |
michael@0 | 397 | } while (isspace(ch)); |
michael@0 | 398 | UNGET(state, ch); |
michael@0 | 399 | } |
michael@0 | 400 | switch (*cPtr) { |
michael@0 | 401 | case 'c': |
michael@0 | 402 | if (state->assign) { |
michael@0 | 403 | cArg = va_arg(state->ap, char *); |
michael@0 | 404 | } |
michael@0 | 405 | if (state->width == 0) { |
michael@0 | 406 | state->width = 1; |
michael@0 | 407 | } |
michael@0 | 408 | for (; state->width > 0; state->width--) { |
michael@0 | 409 | ch = GET(state); |
michael@0 | 410 | if (ch == EOF) { |
michael@0 | 411 | return NULL; |
michael@0 | 412 | } else if (state->assign) { |
michael@0 | 413 | *cArg++ = ch; |
michael@0 | 414 | } |
michael@0 | 415 | } |
michael@0 | 416 | if (state->assign) { |
michael@0 | 417 | state->converted = PR_TRUE; |
michael@0 | 418 | } |
michael@0 | 419 | break; |
michael@0 | 420 | case 'p': |
michael@0 | 421 | case 'd': case 'i': case 'o': |
michael@0 | 422 | case 'u': case 'x': case 'X': |
michael@0 | 423 | if (GetInt(state, *cPtr) == PR_FAILURE) { |
michael@0 | 424 | return NULL; |
michael@0 | 425 | } |
michael@0 | 426 | break; |
michael@0 | 427 | case 'e': case 'E': case 'f': |
michael@0 | 428 | case 'g': case 'G': |
michael@0 | 429 | if (GetFloat(state) == PR_FAILURE) { |
michael@0 | 430 | return NULL; |
michael@0 | 431 | } |
michael@0 | 432 | break; |
michael@0 | 433 | case 'n': |
michael@0 | 434 | /* do not consume any input */ |
michael@0 | 435 | if (state->assign) { |
michael@0 | 436 | switch (state->sizeSpec) { |
michael@0 | 437 | case _PR_size_none: |
michael@0 | 438 | *va_arg(state->ap, PRIntn *) = state->nChar; |
michael@0 | 439 | break; |
michael@0 | 440 | case _PR_size_h: |
michael@0 | 441 | *va_arg(state->ap, PRInt16 *) = state->nChar; |
michael@0 | 442 | break; |
michael@0 | 443 | case _PR_size_l: |
michael@0 | 444 | *va_arg(state->ap, PRInt32 *) = state->nChar; |
michael@0 | 445 | break; |
michael@0 | 446 | case _PR_size_ll: |
michael@0 | 447 | LL_I2L(*va_arg(state->ap, PRInt64 *), state->nChar); |
michael@0 | 448 | break; |
michael@0 | 449 | default: |
michael@0 | 450 | PR_ASSERT(0); |
michael@0 | 451 | } |
michael@0 | 452 | } |
michael@0 | 453 | break; |
michael@0 | 454 | case 's': |
michael@0 | 455 | if (state->width == 0) { |
michael@0 | 456 | state->width = INT_MAX; |
michael@0 | 457 | } |
michael@0 | 458 | if (state->assign) { |
michael@0 | 459 | cArg = va_arg(state->ap, char *); |
michael@0 | 460 | } |
michael@0 | 461 | for (; state->width > 0; state->width--) { |
michael@0 | 462 | ch = GET(state); |
michael@0 | 463 | if ((ch == EOF) || isspace(ch)) { |
michael@0 | 464 | UNGET(state, ch); |
michael@0 | 465 | break; |
michael@0 | 466 | } |
michael@0 | 467 | if (state->assign) { |
michael@0 | 468 | *cArg++ = ch; |
michael@0 | 469 | } |
michael@0 | 470 | } |
michael@0 | 471 | if (state->assign) { |
michael@0 | 472 | *cArg = '\0'; |
michael@0 | 473 | state->converted = PR_TRUE; |
michael@0 | 474 | } |
michael@0 | 475 | break; |
michael@0 | 476 | case '%': |
michael@0 | 477 | ch = GET(state); |
michael@0 | 478 | if (ch != '%') { |
michael@0 | 479 | UNGET(state, ch); |
michael@0 | 480 | return NULL; |
michael@0 | 481 | } |
michael@0 | 482 | break; |
michael@0 | 483 | case '[': |
michael@0 | 484 | { |
michael@0 | 485 | PRBool complement = PR_FALSE; |
michael@0 | 486 | const char *closeBracket; |
michael@0 | 487 | size_t n; |
michael@0 | 488 | |
michael@0 | 489 | if (*++cPtr == '^') { |
michael@0 | 490 | complement = PR_TRUE; |
michael@0 | 491 | cPtr++; |
michael@0 | 492 | } |
michael@0 | 493 | closeBracket = strchr(*cPtr == ']' ? cPtr + 1 : cPtr, ']'); |
michael@0 | 494 | if (closeBracket == NULL) { |
michael@0 | 495 | return NULL; |
michael@0 | 496 | } |
michael@0 | 497 | n = closeBracket - cPtr; |
michael@0 | 498 | if (state->width == 0) { |
michael@0 | 499 | state->width = INT_MAX; |
michael@0 | 500 | } |
michael@0 | 501 | if (state->assign) { |
michael@0 | 502 | cArg = va_arg(state->ap, char *); |
michael@0 | 503 | } |
michael@0 | 504 | for (; state->width > 0; state->width--) { |
michael@0 | 505 | ch = GET(state); |
michael@0 | 506 | if ((ch == EOF) |
michael@0 | 507 | || (!complement && !memchr(cPtr, ch, n)) |
michael@0 | 508 | || (complement && memchr(cPtr, ch, n))) { |
michael@0 | 509 | UNGET(state, ch); |
michael@0 | 510 | break; |
michael@0 | 511 | } |
michael@0 | 512 | if (state->assign) { |
michael@0 | 513 | *cArg++ = ch; |
michael@0 | 514 | } |
michael@0 | 515 | } |
michael@0 | 516 | if (state->assign) { |
michael@0 | 517 | *cArg = '\0'; |
michael@0 | 518 | state->converted = PR_TRUE; |
michael@0 | 519 | } |
michael@0 | 520 | cPtr = closeBracket; |
michael@0 | 521 | } |
michael@0 | 522 | break; |
michael@0 | 523 | default: |
michael@0 | 524 | return NULL; |
michael@0 | 525 | } |
michael@0 | 526 | return cPtr; |
michael@0 | 527 | } |
michael@0 | 528 | |
michael@0 | 529 | static PRInt32 |
michael@0 | 530 | DoScanf(ScanfState *state, const char *fmt) |
michael@0 | 531 | { |
michael@0 | 532 | PRInt32 nConverted = 0; |
michael@0 | 533 | const char *cPtr; |
michael@0 | 534 | int ch; |
michael@0 | 535 | |
michael@0 | 536 | state->nChar = 0; |
michael@0 | 537 | cPtr = fmt; |
michael@0 | 538 | while (1) { |
michael@0 | 539 | if (isspace(*cPtr)) { |
michael@0 | 540 | /* white space: skip */ |
michael@0 | 541 | do { |
michael@0 | 542 | cPtr++; |
michael@0 | 543 | } while (isspace(*cPtr)); |
michael@0 | 544 | do { |
michael@0 | 545 | ch = GET(state); |
michael@0 | 546 | } while (isspace(ch)); |
michael@0 | 547 | UNGET(state, ch); |
michael@0 | 548 | } else if (*cPtr == '%') { |
michael@0 | 549 | /* format spec: convert */ |
michael@0 | 550 | cPtr++; |
michael@0 | 551 | state->assign = PR_TRUE; |
michael@0 | 552 | if (*cPtr == '*') { |
michael@0 | 553 | cPtr++; |
michael@0 | 554 | state->assign = PR_FALSE; |
michael@0 | 555 | } |
michael@0 | 556 | for (state->width = 0; isdigit(*cPtr); cPtr++) { |
michael@0 | 557 | state->width = state->width * 10 + *cPtr - '0'; |
michael@0 | 558 | } |
michael@0 | 559 | state->sizeSpec = _PR_size_none; |
michael@0 | 560 | if (*cPtr == 'h') { |
michael@0 | 561 | cPtr++; |
michael@0 | 562 | state->sizeSpec = _PR_size_h; |
michael@0 | 563 | } else if (*cPtr == 'l') { |
michael@0 | 564 | cPtr++; |
michael@0 | 565 | if (*cPtr == 'l') { |
michael@0 | 566 | cPtr++; |
michael@0 | 567 | state->sizeSpec = _PR_size_ll; |
michael@0 | 568 | } else { |
michael@0 | 569 | state->sizeSpec = _PR_size_l; |
michael@0 | 570 | } |
michael@0 | 571 | } else if (*cPtr == 'L') { |
michael@0 | 572 | cPtr++; |
michael@0 | 573 | state->sizeSpec = _PR_size_L; |
michael@0 | 574 | } |
michael@0 | 575 | cPtr = Convert(state, cPtr); |
michael@0 | 576 | if (cPtr == NULL) { |
michael@0 | 577 | return (nConverted > 0 ? nConverted : EOF); |
michael@0 | 578 | } |
michael@0 | 579 | if (state->converted) { |
michael@0 | 580 | nConverted++; |
michael@0 | 581 | } |
michael@0 | 582 | cPtr++; |
michael@0 | 583 | } else { |
michael@0 | 584 | /* others: must match */ |
michael@0 | 585 | if (*cPtr == '\0') { |
michael@0 | 586 | return nConverted; |
michael@0 | 587 | } |
michael@0 | 588 | ch = GET(state); |
michael@0 | 589 | if (ch != *cPtr) { |
michael@0 | 590 | UNGET(state, ch); |
michael@0 | 591 | return nConverted; |
michael@0 | 592 | } |
michael@0 | 593 | cPtr++; |
michael@0 | 594 | } |
michael@0 | 595 | } |
michael@0 | 596 | } |
michael@0 | 597 | |
michael@0 | 598 | static int |
michael@0 | 599 | StringGetChar(void *stream) |
michael@0 | 600 | { |
michael@0 | 601 | char *cPtr = *((char **) stream); |
michael@0 | 602 | |
michael@0 | 603 | if (*cPtr == '\0') { |
michael@0 | 604 | return EOF; |
michael@0 | 605 | } else { |
michael@0 | 606 | *((char **) stream) = cPtr + 1; |
michael@0 | 607 | return (unsigned char) *cPtr; |
michael@0 | 608 | } |
michael@0 | 609 | } |
michael@0 | 610 | |
michael@0 | 611 | static void |
michael@0 | 612 | StringUngetChar(void *stream, int ch) |
michael@0 | 613 | { |
michael@0 | 614 | char *cPtr = *((char **) stream); |
michael@0 | 615 | |
michael@0 | 616 | if (ch != EOF) { |
michael@0 | 617 | *((char **) stream) = cPtr - 1; |
michael@0 | 618 | } |
michael@0 | 619 | } |
michael@0 | 620 | |
michael@0 | 621 | PR_IMPLEMENT(PRInt32) |
michael@0 | 622 | PR_sscanf(const char *buf, const char *fmt, ...) |
michael@0 | 623 | { |
michael@0 | 624 | PRInt32 rv; |
michael@0 | 625 | ScanfState state; |
michael@0 | 626 | |
michael@0 | 627 | state.get = &StringGetChar; |
michael@0 | 628 | state.unget = &StringUngetChar; |
michael@0 | 629 | state.stream = (void *) &buf; |
michael@0 | 630 | va_start(state.ap, fmt); |
michael@0 | 631 | rv = DoScanf(&state, fmt); |
michael@0 | 632 | va_end(state.ap); |
michael@0 | 633 | return rv; |
michael@0 | 634 | } |