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