1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is the Netscape Portable Runtime (NSPR).
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
39 * Scan functions for NSPR types
41 * Author: Wan-Teh Chang
43 * Acknowledgment: The implementation is inspired by the source code
44 * in P.J. Plauger's "The Standard C Library," Prentice-Hall, 1992.
52 #include "md/sunos4.h" /* for strtoul */
60 * A function that reads a character from 'stream'.
61 * Returns the character read, or EOF if end of stream is reached.
63 typedef int (*_PRGetCharFN
)(void *stream
);
66 * A function that pushes the character 'ch' back to 'stream'.
68 typedef void (*_PRUngetCharFN
)(void *stream
, int ch
);
71 * The size specifier for the integer and floating point number
72 * conversions in format control strings.
75 _PR_size_none
, /* No size specifier is given */
76 _PR_size_h
, /* The 'h' specifier, suggesting "short" */
77 _PR_size_l
, /* The 'l' specifier, suggesting "long" */
78 _PR_size_L
, /* The 'L' specifier, meaning a 'long double' */
79 _PR_size_ll
/* The 'll' specifier, suggesting "long long" */
83 * The collection of data that is passed between the scan function
84 * and its subordinate functions. The fields of this structure
85 * serve as the input or output arguments for these functions.
88 _PRGetCharFN get
; /* get a character from input stream */
89 _PRUngetCharFN unget
; /* unget (push back) a character */
90 void *stream
; /* argument for get and unget */
91 va_list ap
; /* the variable argument list */
92 int nChar
; /* number of characters read from 'stream' */
94 PRBool assign
; /* assign, or suppress assignment? */
95 int width
; /* field width */
96 _PRSizeSpec sizeSpec
; /* 'h', 'l', 'L', or 'll' */
98 PRBool converted
; /* is the value actually converted? */
101 #define GET(state) ((state)->nChar++, (state)->get((state)->stream))
102 #define UNGET(state, ch) \
103 ((state)->nChar--, (state)->unget((state)->stream, ch))
106 * The following two macros, GET_IF_WITHIN_WIDTH and WITHIN_WIDTH,
107 * are always used together.
109 * GET_IF_WITHIN_WIDTH calls the GET macro and assigns its return
110 * value to 'ch' only if we have not exceeded the field width of
111 * 'state'. Therefore, after GET_IF_WITHIN_WIDTH, the value of
112 * 'ch' is valid only if the macro WITHIN_WIDTH evaluates to true.
115 #define GET_IF_WITHIN_WIDTH(state, ch) \
116 if (--(state)->width >= 0) { \
119 #define WITHIN_WIDTH(state) ((state)->width >= 0)
123 * Convert a string to an unsigned 64-bit integer. The string
124 * 'str' is assumed to be a representation of the integer in
128 * - Only handle base 8, 10, and 16.
129 * - No overflow checking.
133 _pr_strtoull(const char *str
, char **endptr
, int base
)
135 static const int BASE_MAX
= 16;
136 static const char digits
[] = "0123456789abcdef";
138 PRUint64 x
; /* return value */
142 const char *digitStart
;
144 PR_ASSERT(base
== 0 || base
== 8 || base
== 10 || base
== 16);
145 if (base
< 0 || base
== 1 || base
> BASE_MAX
) {
147 *endptr
= (char *) str
;
153 while (isspace(*cPtr
)) {
161 } else if (*cPtr
== '+') {
166 if (*cPtr
== '0' && (cPtr
[1] == 'x' || cPtr
[1] == 'X')) {
169 } else if (base
== 0) {
172 } else if (cPtr
[1] == 'x' || cPtr
[1] == 'X') {
179 PR_ASSERT(base
!= 0);
180 LL_I2L(base64
, base
);
183 /* Skip leading zeros */
184 while (*cPtr
== '0') {
189 while ((digitPtr
= (char*)memchr(digits
, tolower(*cPtr
), base
)) != NULL
) {
192 LL_I2L(d
, (digitPtr
- digits
));
193 LL_MUL(x
, x
, base64
);
198 if (cPtr
== digitStart
) {
200 *endptr
= (char *) str
;
206 #ifdef HAVE_LONG_LONG
207 /* The cast to a signed type is to avoid a compiler warning */
215 *endptr
= (char *) cPtr
;
221 * The maximum field width (in number of characters) that is enough
222 * (may be more than necessary) to represent a 64-bit integer or
223 * floating point number.
226 #define DECIMAL_POINT '.'
229 GetInt(ScanfState
*state
, int code
)
231 char buf
[FMAX
+ 1], *p
;
233 static const char digits
[] = "0123456789abcdefABCDEF";
234 PRBool seenDigit
= PR_FALSE
;
245 case 'x': case 'X': case 'p':
254 if (state
->width
== 0 || state
->width
> FMAX
) {
258 GET_IF_WITHIN_WIDTH(state
, ch
);
259 if (WITHIN_WIDTH(state
) && (ch
== '+' || ch
== '-')) {
261 GET_IF_WITHIN_WIDTH(state
, ch
);
263 if (WITHIN_WIDTH(state
) && ch
== '0') {
266 GET_IF_WITHIN_WIDTH(state
, ch
);
267 if (WITHIN_WIDTH(state
)
268 && (ch
== 'x' || ch
== 'X')
269 && (base
== 0 || base
== 16)) {
272 GET_IF_WITHIN_WIDTH(state
, ch
);
273 } else if (base
== 0) {
277 if (base
== 0 || base
== 10) {
279 } else if (base
== 8) {
282 PR_ASSERT(base
== 16);
283 dlen
= 16 + 6; /* 16 digits, plus 6 in uppercase */
285 while (WITHIN_WIDTH(state
) && memchr(digits
, ch
, dlen
)) {
287 GET_IF_WITHIN_WIDTH(state
, ch
);
290 if (WITHIN_WIDTH(state
)) {
298 if (code
== 'd' || code
== 'i') {
299 if (state
->sizeSpec
== _PR_size_ll
) {
300 PRInt64 llval
= _pr_strtoull(buf
, NULL
, base
);
301 *va_arg(state
->ap
, PRInt64
*) = llval
;
303 long lval
= strtol(buf
, NULL
, base
);
305 if (state
->sizeSpec
== _PR_size_none
) {
306 *va_arg(state
->ap
, PRIntn
*) = lval
;
307 } else if (state
->sizeSpec
== _PR_size_h
) {
308 *va_arg(state
->ap
, PRInt16
*) = (PRInt16
)lval
;
309 } else if (state
->sizeSpec
== _PR_size_l
) {
310 *va_arg(state
->ap
, PRInt32
*) = lval
;
316 if (state
->sizeSpec
== _PR_size_ll
) {
317 PRUint64 llval
= _pr_strtoull(buf
, NULL
, base
);
318 *va_arg(state
->ap
, PRUint64
*) = llval
;
320 unsigned long lval
= strtoul(buf
, NULL
, base
);
322 if (state
->sizeSpec
== _PR_size_none
) {
323 *va_arg(state
->ap
, PRUintn
*) = lval
;
324 } else if (state
->sizeSpec
== _PR_size_h
) {
325 *va_arg(state
->ap
, PRUint16
*) = (PRUint16
)lval
;
326 } else if (state
->sizeSpec
== _PR_size_l
) {
327 *va_arg(state
->ap
, PRUint32
*) = lval
;
333 state
->converted
= PR_TRUE
;
339 GetFloat(ScanfState
*state
)
341 char buf
[FMAX
+ 1], *p
;
343 PRBool seenDigit
= PR_FALSE
;
345 if (state
->width
== 0 || state
->width
> FMAX
) {
349 GET_IF_WITHIN_WIDTH(state
, ch
);
350 if (WITHIN_WIDTH(state
) && (ch
== '+' || ch
== '-')) {
352 GET_IF_WITHIN_WIDTH(state
, ch
);
354 while (WITHIN_WIDTH(state
) && isdigit(ch
)) {
356 GET_IF_WITHIN_WIDTH(state
, ch
);
359 if (WITHIN_WIDTH(state
) && ch
== DECIMAL_POINT
) {
361 GET_IF_WITHIN_WIDTH(state
, ch
);
362 while (WITHIN_WIDTH(state
) && isdigit(ch
)) {
364 GET_IF_WITHIN_WIDTH(state
, ch
);
370 * This is not robust. For example, "1.2e+" would confuse
371 * the code below to read 'e' and '+', only to realize that
372 * it should have stopped at "1.2". But we can't push back
373 * more than one character, so there is nothing I can do.
377 if (WITHIN_WIDTH(state
) && (ch
== 'e' || ch
== 'E') && seenDigit
) {
379 GET_IF_WITHIN_WIDTH(state
, ch
);
380 if (WITHIN_WIDTH(state
) && (ch
== '+' || ch
== '-')) {
382 GET_IF_WITHIN_WIDTH(state
, ch
);
384 while (WITHIN_WIDTH(state
) && isdigit(ch
)) {
386 GET_IF_WITHIN_WIDTH(state
, ch
);
389 if (WITHIN_WIDTH(state
)) {
397 PRFloat64 dval
= PR_strtod(buf
, NULL
);
399 state
->converted
= PR_TRUE
;
400 if (state
->sizeSpec
== _PR_size_l
) {
401 *va_arg(state
->ap
, PRFloat64
*) = dval
;
402 } else if (state
->sizeSpec
== _PR_size_L
) {
403 #if defined(OSF1) || defined(IRIX)
404 *va_arg(state
->ap
, double *) = dval
;
406 *va_arg(state
->ap
, long double *) = dval
;
409 *va_arg(state
->ap
, float *) = (float) dval
;
416 * Convert, and return the end of the conversion spec.
417 * Return NULL on error.
421 Convert(ScanfState
*state
, const char *fmt
)
427 state
->converted
= PR_FALSE
;
429 if (*cPtr
!= 'c' && *cPtr
!= 'n' && *cPtr
!= '[') {
432 } while (isspace(ch
));
438 cArg
= va_arg(state
->ap
, char *);
440 if (state
->width
== 0) {
443 for (; state
->width
> 0; state
->width
--) {
447 } else if (state
->assign
) {
452 state
->converted
= PR_TRUE
;
456 case 'd': case 'i': case 'o':
457 case 'u': case 'x': case 'X':
458 if (GetInt(state
, *cPtr
) == PR_FAILURE
) {
462 case 'e': case 'E': case 'f':
464 if (GetFloat(state
) == PR_FAILURE
) {
469 /* do not consume any input */
471 switch (state
->sizeSpec
) {
473 *va_arg(state
->ap
, PRIntn
*) = state
->nChar
;
476 *va_arg(state
->ap
, PRInt16
*) = state
->nChar
;
479 *va_arg(state
->ap
, PRInt32
*) = state
->nChar
;
482 LL_I2L(*va_arg(state
->ap
, PRInt64
*), state
->nChar
);
490 if (state
->width
== 0) {
491 state
->width
= INT_MAX
;
494 cArg
= va_arg(state
->ap
, char *);
496 for (; state
->width
> 0; state
->width
--) {
498 if ((ch
== EOF
) || isspace(ch
)) {
508 state
->converted
= PR_TRUE
;
520 PRBool complement
= PR_FALSE
;
521 const char *closeBracket
;
524 if (*++cPtr
== '^') {
525 complement
= PR_TRUE
;
528 closeBracket
= strchr(*cPtr
== ']' ? cPtr
+ 1 : cPtr
, ']');
529 if (closeBracket
== NULL
) {
532 n
= closeBracket
- cPtr
;
533 if (state
->width
== 0) {
534 state
->width
= INT_MAX
;
537 cArg
= va_arg(state
->ap
, char *);
539 for (; state
->width
> 0; state
->width
--) {
542 || (!complement
&& !memchr(cPtr
, ch
, n
))
543 || (complement
&& memchr(cPtr
, ch
, n
))) {
553 state
->converted
= PR_TRUE
;
565 DoScanf(ScanfState
*state
, const char *fmt
)
567 PRInt32 nConverted
= 0;
574 if (isspace(*cPtr
)) {
575 /* white space: skip */
578 } while (isspace(*cPtr
));
581 } while (isspace(ch
));
583 } else if (*cPtr
== '%') {
584 /* format spec: convert */
586 state
->assign
= PR_TRUE
;
589 state
->assign
= PR_FALSE
;
591 for (state
->width
= 0; isdigit(*cPtr
); cPtr
++) {
592 state
->width
= state
->width
* 10 + *cPtr
- '0';
594 state
->sizeSpec
= _PR_size_none
;
597 state
->sizeSpec
= _PR_size_h
;
598 } else if (*cPtr
== 'l') {
602 state
->sizeSpec
= _PR_size_ll
;
604 state
->sizeSpec
= _PR_size_l
;
606 } else if (*cPtr
== 'L') {
608 state
->sizeSpec
= _PR_size_L
;
610 cPtr
= Convert(state
, cPtr
);
612 return (nConverted
> 0 ? nConverted
: EOF
);
614 if (state
->converted
) {
619 /* others: must match */
634 StringGetChar(void *stream
)
636 char *cPtr
= *((char **) stream
);
641 *((char **) stream
) = cPtr
+ 1;
642 return (unsigned char) *cPtr
;
647 StringUngetChar(void *stream
, int ch
)
649 char *cPtr
= *((char **) stream
);
652 *((char **) stream
) = cPtr
- 1;
656 PR_IMPLEMENT(PRInt32
)
657 PR_sscanf(const char *buf
, const char *fmt
, ...)
662 state
.get
= &StringGetChar
;
663 state
.unget
= &StringUngetChar
;
664 state
.stream
= (void *) &buf
;
665 va_start(state
.ap
, fmt
);
666 rv
= DoScanf(&state
, fmt
);