1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 * Scan functions for NSPR types
9 * Author: Wan-Teh Chang
11 * Acknowledgment: The implementation is inspired by the source code
12 * in P.J. Plauger's "The Standard C Library," Prentice-Hall, 1992.
25 * A function that reads a character from 'stream'.
26 * Returns the character read, or EOF if end of stream is reached.
28 typedef int (*_PRGetCharFN
)(void* stream
);
31 * A function that pushes the character 'ch' back to 'stream'.
33 typedef void (*_PRUngetCharFN
)(void* stream
, int ch
);
36 * The size specifier for the integer and floating point number
37 * conversions in format control strings.
40 _PR_size_none
, /* No size specifier is given */
41 _PR_size_h
, /* The 'h' specifier, suggesting "short" */
42 _PR_size_l
, /* The 'l' specifier, suggesting "long" */
43 _PR_size_L
, /* The 'L' specifier, meaning a 'long double' */
44 _PR_size_ll
/* The 'll' specifier, suggesting "long long" */
48 * The collection of data that is passed between the scan function
49 * and its subordinate functions. The fields of this structure
50 * serve as the input or output arguments for these functions.
53 _PRGetCharFN get
; /* get a character from input stream */
54 _PRUngetCharFN unget
; /* unget (push back) a character */
55 void* stream
; /* argument for get and unget */
56 va_list ap
; /* the variable argument list */
57 int nChar
; /* number of characters read from 'stream' */
59 PRBool assign
; /* assign, or suppress assignment? */
60 int width
; /* field width */
61 _PRSizeSpec sizeSpec
; /* 'h', 'l', 'L', or 'll' */
63 PRBool converted
; /* is the value actually converted? */
66 #define GET(state) ((state)->nChar++, (state)->get((state)->stream))
67 #define UNGET(state, ch) ((state)->nChar--, (state)->unget((state)->stream, ch))
70 * The following two macros, GET_IF_WITHIN_WIDTH and WITHIN_WIDTH,
71 * are always used together.
73 * GET_IF_WITHIN_WIDTH calls the GET macro and assigns its return
74 * value to 'ch' only if we have not exceeded the field width of
75 * 'state'. Therefore, after GET_IF_WITHIN_WIDTH, the value of
76 * 'ch' is valid only if the macro WITHIN_WIDTH evaluates to true.
79 #define GET_IF_WITHIN_WIDTH(state, ch) \
80 if (--(state)->width >= 0) { \
83 #define WITHIN_WIDTH(state) ((state)->width >= 0)
87 * Convert a string to an unsigned 64-bit integer. The string
88 * 'str' is assumed to be a representation of the integer in
92 * - Only handle base 8, 10, and 16.
93 * - No overflow checking.
96 static PRUint64
_pr_strtoull(const char* str
, char** endptr
, int base
) {
97 static const int BASE_MAX
= 16;
98 static const char digits
[] = "0123456789abcdef";
100 PRUint64 x
; /* return value */
104 const char* digitStart
;
106 PR_ASSERT(base
== 0 || base
== 8 || base
== 10 || base
== 16);
107 if (base
< 0 || base
== 1 || base
> BASE_MAX
) {
109 *endptr
= (char*)str
;
115 while (isspace(*cPtr
)) {
123 } else if (*cPtr
== '+') {
128 if (*cPtr
== '0' && (cPtr
[1] == 'x' || cPtr
[1] == 'X')) {
131 } else if (base
== 0) {
134 } else if (cPtr
[1] == 'x' || cPtr
[1] == 'X') {
141 PR_ASSERT(base
!= 0);
142 LL_I2L(base64
, base
);
145 /* Skip leading zeros */
146 while (*cPtr
== '0') {
151 while ((digitPtr
= (char*)memchr(digits
, tolower(*cPtr
), base
)) != NULL
) {
154 LL_I2L(d
, (digitPtr
- digits
));
155 LL_MUL(x
, x
, base64
);
160 if (cPtr
== digitStart
) {
162 *endptr
= (char*)str
;
168 #ifdef HAVE_LONG_LONG
169 /* The cast to a signed type is to avoid a compiler warning */
177 *endptr
= (char*)cPtr
;
183 * The maximum field width (in number of characters) that is enough
184 * (may be more than necessary) to represent a 64-bit integer or
185 * floating point number.
188 #define DECIMAL_POINT '.'
190 static PRStatus
GetInt(ScanfState
* state
, int code
) {
191 char buf
[FMAX
+ 1], *p
;
193 static const char digits
[] = "0123456789abcdefABCDEF";
194 PRBool seenDigit
= PR_FALSE
;
217 if (state
->width
== 0 || state
->width
> FMAX
) {
221 GET_IF_WITHIN_WIDTH(state
, ch
);
222 if (WITHIN_WIDTH(state
) && (ch
== '+' || ch
== '-')) {
224 GET_IF_WITHIN_WIDTH(state
, ch
);
226 if (WITHIN_WIDTH(state
) && ch
== '0') {
229 GET_IF_WITHIN_WIDTH(state
, ch
);
230 if (WITHIN_WIDTH(state
) && (ch
== 'x' || ch
== 'X') &&
231 (base
== 0 || base
== 16)) {
234 GET_IF_WITHIN_WIDTH(state
, ch
);
235 } else if (base
== 0) {
239 if (base
== 0 || base
== 10) {
241 } else if (base
== 8) {
244 PR_ASSERT(base
== 16);
245 dlen
= 16 + 6; /* 16 digits, plus 6 in uppercase */
247 while (WITHIN_WIDTH(state
) && memchr(digits
, ch
, dlen
)) {
249 GET_IF_WITHIN_WIDTH(state
, ch
);
252 if (WITHIN_WIDTH(state
)) {
260 if (code
== 'd' || code
== 'i') {
261 if (state
->sizeSpec
== _PR_size_ll
) {
262 PRInt64 llval
= _pr_strtoull(buf
, NULL
, base
);
263 *va_arg(state
->ap
, PRInt64
*) = llval
;
265 long lval
= strtol(buf
, NULL
, base
);
267 if (state
->sizeSpec
== _PR_size_none
) {
268 *va_arg(state
->ap
, PRIntn
*) = lval
;
269 } else if (state
->sizeSpec
== _PR_size_h
) {
270 *va_arg(state
->ap
, PRInt16
*) = (PRInt16
)lval
;
271 } else if (state
->sizeSpec
== _PR_size_l
) {
272 *va_arg(state
->ap
, PRInt32
*) = lval
;
278 if (state
->sizeSpec
== _PR_size_ll
) {
279 PRUint64 llval
= _pr_strtoull(buf
, NULL
, base
);
280 *va_arg(state
->ap
, PRUint64
*) = llval
;
282 unsigned long lval
= strtoul(buf
, NULL
, base
);
284 if (state
->sizeSpec
== _PR_size_none
) {
285 *va_arg(state
->ap
, PRUintn
*) = lval
;
286 } else if (state
->sizeSpec
== _PR_size_h
) {
287 *va_arg(state
->ap
, PRUint16
*) = (PRUint16
)lval
;
288 } else if (state
->sizeSpec
== _PR_size_l
) {
289 *va_arg(state
->ap
, PRUint32
*) = lval
;
295 state
->converted
= PR_TRUE
;
300 static PRStatus
GetFloat(ScanfState
* state
) {
301 char buf
[FMAX
+ 1], *p
;
303 PRBool seenDigit
= PR_FALSE
;
305 if (state
->width
== 0 || state
->width
> FMAX
) {
309 GET_IF_WITHIN_WIDTH(state
, ch
);
310 if (WITHIN_WIDTH(state
) && (ch
== '+' || ch
== '-')) {
312 GET_IF_WITHIN_WIDTH(state
, ch
);
314 while (WITHIN_WIDTH(state
) && isdigit(ch
)) {
316 GET_IF_WITHIN_WIDTH(state
, ch
);
319 if (WITHIN_WIDTH(state
) && ch
== DECIMAL_POINT
) {
321 GET_IF_WITHIN_WIDTH(state
, ch
);
322 while (WITHIN_WIDTH(state
) && isdigit(ch
)) {
324 GET_IF_WITHIN_WIDTH(state
, ch
);
330 * This is not robust. For example, "1.2e+" would confuse
331 * the code below to read 'e' and '+', only to realize that
332 * it should have stopped at "1.2". But we can't push back
333 * more than one character, so there is nothing I can do.
337 if (WITHIN_WIDTH(state
) && (ch
== 'e' || ch
== 'E') && seenDigit
) {
339 GET_IF_WITHIN_WIDTH(state
, ch
);
340 if (WITHIN_WIDTH(state
) && (ch
== '+' || ch
== '-')) {
342 GET_IF_WITHIN_WIDTH(state
, ch
);
344 while (WITHIN_WIDTH(state
) && isdigit(ch
)) {
346 GET_IF_WITHIN_WIDTH(state
, ch
);
349 if (WITHIN_WIDTH(state
)) {
357 PRFloat64 dval
= PR_strtod(buf
, NULL
);
359 state
->converted
= PR_TRUE
;
360 if (state
->sizeSpec
== _PR_size_l
) {
361 *va_arg(state
->ap
, PRFloat64
*) = dval
;
362 } else if (state
->sizeSpec
== _PR_size_L
) {
363 *va_arg(state
->ap
, long double*) = dval
;
365 *va_arg(state
->ap
, float*) = (float)dval
;
372 * Convert, and return the end of the conversion spec.
373 * Return NULL on error.
376 static const char* Convert(ScanfState
* state
, const char* fmt
) {
381 state
->converted
= PR_FALSE
;
383 if (*cPtr
!= 'c' && *cPtr
!= 'n' && *cPtr
!= '[') {
386 } while (isspace(ch
));
392 cArg
= va_arg(state
->ap
, char*);
394 if (state
->width
== 0) {
397 for (; state
->width
> 0; state
->width
--) {
407 state
->converted
= PR_TRUE
;
417 if (GetInt(state
, *cPtr
) == PR_FAILURE
) {
426 if (GetFloat(state
) == PR_FAILURE
) {
431 /* do not consume any input */
433 switch (state
->sizeSpec
) {
435 *va_arg(state
->ap
, PRIntn
*) = state
->nChar
;
438 *va_arg(state
->ap
, PRInt16
*) = state
->nChar
;
441 *va_arg(state
->ap
, PRInt32
*) = state
->nChar
;
444 LL_I2L(*va_arg(state
->ap
, PRInt64
*), state
->nChar
);
452 if (state
->width
== 0) {
453 state
->width
= INT_MAX
;
456 cArg
= va_arg(state
->ap
, char*);
458 for (; state
->width
> 0; state
->width
--) {
460 if ((ch
== EOF
) || isspace(ch
)) {
470 state
->converted
= PR_TRUE
;
481 PRBool complement
= PR_FALSE
;
482 const char* closeBracket
;
485 if (*++cPtr
== '^') {
486 complement
= PR_TRUE
;
489 closeBracket
= strchr(*cPtr
== ']' ? cPtr
+ 1 : cPtr
, ']');
490 if (closeBracket
== NULL
) {
493 n
= closeBracket
- cPtr
;
494 if (state
->width
== 0) {
495 state
->width
= INT_MAX
;
498 cArg
= va_arg(state
->ap
, char*);
500 for (; state
->width
> 0; state
->width
--) {
502 if ((ch
== EOF
) || (!complement
&& !memchr(cPtr
, ch
, n
)) ||
503 (complement
&& memchr(cPtr
, ch
, n
))) {
513 state
->converted
= PR_TRUE
;
523 static PRInt32
DoScanf(ScanfState
* state
, const char* fmt
) {
524 PRInt32 nConverted
= 0;
531 if (isspace(*cPtr
)) {
532 /* white space: skip */
535 } while (isspace(*cPtr
));
538 } while (isspace(ch
));
540 } else if (*cPtr
== '%') {
541 /* format spec: convert */
543 state
->assign
= PR_TRUE
;
546 state
->assign
= PR_FALSE
;
548 for (state
->width
= 0; isdigit(*cPtr
); cPtr
++) {
549 state
->width
= state
->width
* 10 + *cPtr
- '0';
551 state
->sizeSpec
= _PR_size_none
;
554 state
->sizeSpec
= _PR_size_h
;
555 } else if (*cPtr
== 'l') {
559 state
->sizeSpec
= _PR_size_ll
;
561 state
->sizeSpec
= _PR_size_l
;
563 } else if (*cPtr
== 'L') {
565 state
->sizeSpec
= _PR_size_L
;
567 cPtr
= Convert(state
, cPtr
);
569 return (nConverted
> 0 ? nConverted
: EOF
);
571 if (state
->converted
) {
576 /* others: must match */
590 static int StringGetChar(void* stream
) {
591 char* cPtr
= *((char**)stream
);
596 *((char**)stream
) = cPtr
+ 1;
597 return (unsigned char)*cPtr
;
600 static void StringUngetChar(void* stream
, int ch
) {
601 char* cPtr
= *((char**)stream
);
604 *((char**)stream
) = cPtr
- 1;
608 PR_IMPLEMENT(PRInt32
)
609 PR_sscanf(const char* buf
, const char* fmt
, ...) {
613 state
.get
= &StringGetChar
;
614 state
.unget
= &StringUngetChar
;
615 state
.stream
= (void*)&buf
;
616 va_start(state
.ap
, fmt
);
617 rv
= DoScanf(&state
, fmt
);