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