source: trunk/src/msvcrt/scanf.h@ 10367

Last change on this file since 10367 was 10005, checked in by sandervl, 22 years ago

PF: MSVCRT update

File size: 17.3 KB
Line 
1/*
2 * general implementation of scanf used by scanf, sscanf, fscanf,
3 * _cscanf, wscanf, swscanf and fwscanf
4 *
5 * Copyright 1996,1998 Marcus Meissner
6 * Copyright 1996 Jukka Iivonen
7 * Copyright 1997,2000 Uwe Bonnes
8 * Copyright 2000 Jon Griffiths
9 * Copyright 2002 Daniel Gudbjartsson
10 *
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
20 *
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 */
25
26#ifdef WIDE_SCANF
27#define _CHAR_ MSVCRT_wchar_t
28#define _EOF_ MSVCRT_WEOF
29#define _ISSPACE_(c) MSVCRT_iswspace(c)
30#define _ISDIGIT_(c) MSVCRT_iswdigit(c)
31#define _CONVERT_(c) c /*** FIXME ***/
32#define _CHAR2DIGIT_(c, base) wchar2digit((c), (base))
33#else /* WIDE_SCANF */
34#define _CHAR_ char
35#define _EOF_ MSVCRT_EOF
36#define _ISSPACE_(c) MSVCRT_isspace(c)
37#define _ISDIGIT_(c) MSVCRT_isdigit(c)
38#define _CONVERT_(c) c /*** FIXME ***/
39#define _CHAR2DIGIT_(c, base) char2digit((c), (base))
40#endif /* WIDE_SCANF */
41
42#ifdef CONSOLE
43#define _GETC_(file) MSVCRT__getch()
44#define _UNGETC_(nch, file) MSVCRT__ungetch(nch)
45#define _FUNCTION_ MSVCRT__cscanf(const _CHAR_ *format, ...)
46#else
47#ifdef STRING
48#undef _EOF_
49#define _EOF_ 0
50#define _GETC_(file) *file++
51#define _UNGETC_(nch, file) file--
52#ifdef WIDE_SCANF
53#define _FUNCTION_ MSVCRT_swscanf(const MSVCRT_wchar_t *file, const MSVCRT_wchar_t *format, ...)
54#else /* WIDE_SCANF */
55#define _FUNCTION_ MSVCRT_sscanf(const char *file, const char *format, ...)
56#endif /* WIDE_SCANF */
57#else /* STRING */
58#ifdef WIDE_SCANF
59#define _GETC_(file) MSVCRT_fgetwc(file)
60#define _UNGETC_(nch, file) MSVCRT_ungetwc(nch, file)
61#define _FUNCTION_ MSVCRT_fwscanf(MSVCRT_FILE* file, const MSVCRT_wchar_t *format, ...)
62#else /* WIDE_SCANF */
63#define _GETC_(file) MSVCRT_fgetc(file)
64#define _UNGETC_(nch, file) MSVCRT_ungetc(nch, file)
65#define _FUNCTION_ MSVCRT_fscanf(MSVCRT_FILE* file, const char *format, ...)
66#endif /* WIDE_SCANF */
67#endif /* STRING */
68#endif /* CONSOLE */
69
70/*********************************************************************
71 * Implemented based on
72 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore98/html/_crt_format_specification_fields_.2d_.scanf_and_wscanf_functions.asp
73 * Extended by C. Scott Ananian <cananian@alumni.princeton.edu> to handle
74 * more types of format spec.
75 */
76int _FUNCTION_ {
77 int rd = 0;
78 int nch;
79 va_list ap;
80 if (!*format) return 0;
81#ifndef WIDE_SCANF
82#ifdef CONSOLE
83 WARN("(\"%s\"): semi-stub\n", format);
84#else /* CONSOLE */
85#ifdef STRING
86 WARN("%s (\"%s\"): semi-stub\n", file, format);
87#else /* STRING */
88 WARN("%p (\"%s\"): semi-stub\n", file, format);
89#endif /* STRING */
90#endif /* CONSOLE */
91#endif /* WIDE_SCANF */
92 nch = _GETC_(file);
93 va_start(ap, format);
94 while (*format) {
95 /* a whitespace character in the format string causes scanf to read,
96 * but not store, all consecutive white-space characters in the input
97 * up to the next non-white-space character. One white space character
98 * in the input matches any number (including zero) and combination of
99 * white-space characters in the input. */
100 if (_ISSPACE_(*format)) {
101 /* skip whitespace */
102 while ((nch!=_EOF_) && _ISSPACE_(nch))
103 nch = _GETC_(file);
104 }
105 /* a format specification causes scanf to read and convert characters
106 * in the input into values of a specified type. The value is assigned
107 * to an argument in the argument list. Format specifications have
108 * the form %[*][width][{h | l | I64 | L}]type */
109 else if (*format == '%') {
110 int st = 0; int suppress = 0; int width = 0;
111 int base, number_signed;
112 int h_prefix = 0;
113 int l_prefix = 0;
114 int L_prefix = 0;
115 int w_prefix = 0;
116 int prefix_finished = 0;
117 int I64_prefix = 0;
118 format++;
119 /* look for leading asterisk, which means 'suppress assignment of
120 * this field'. */
121 if (*format=='*') {
122 format++;
123 suppress=1;
124 }
125 /* look for width specification */
126 while (_ISDIGIT_(*format)) {
127 width*=10;
128 width+=*format++ - '0';
129 }
130 if (width==0) width=-1; /* no width spec seen */
131 /* read prefix (if any) */
132 while (!prefix_finished) {
133 switch(*format) {
134 case 'h': h_prefix = 1; break;
135 case 'l': l_prefix = 1; break;
136 case 'w': w_prefix = 1; break;
137 case 'L': L_prefix = 1; break;
138 case 'I':
139 if (*(format + 1) == '6' &&
140 *(format + 2) == '4') {
141 I64_prefix = 1;
142 format += 2;
143 FIXME("I64 prefix currently not implemented in fscanf/fwscanf");
144 }
145 break;
146 default:
147 prefix_finished = 1;
148 }
149 if (!prefix_finished) format++;
150 }
151 /* read type */
152 switch(*format) {
153 case 'x':
154 case 'X': /* hexadecimal integer. */
155 base = 16; number_signed = 0;
156 goto number;
157 case 'o': /* octal integer */
158 base = 8; number_signed = 0;
159 goto number;
160 case 'u': /* unsigned decimal integer */
161 base = 10; number_signed = 0;
162 goto number;
163 case 'd': /* signed decimal integer */
164 base = 10; number_signed = 1;
165 goto number;
166 case 'i': /* generic integer */
167 base = 0; number_signed = 1;
168 number: {
169 /* read an integer */
170 long unsigned int cur = 0;
171 int negative = 0;
172 int seendigit=0;
173 /* skip initial whitespace */
174 while ((nch!=_EOF_) && _ISSPACE_(nch))
175 nch = _GETC_(file);
176 /* get sign */
177 if (number_signed && (nch == '-' ||
178 nch == '+')) {
179 negative = (nch=='-');
180 nch = _GETC_(file);
181 if (width>0) width--;
182 }
183 /* look for leading indication of base */
184 if (width!=0 && nch == '0') {
185 nch = _GETC_(file);
186 if (width>0) width--;
187 seendigit=1;
188 if (width!=0 && (nch=='x' || nch=='X')) {
189 if (base==0)
190 base=16;
191 if (base==16) {
192 nch = _GETC_(file);
193 if (width>0) width--;
194 seendigit=0;
195 }
196 } else if (base==0)
197 base = 8;
198 }
199 /* throw away leading zeros */
200 while (width!=0 && nch=='0') {
201 nch = _GETC_(file);
202 if (width>0) width--;
203 seendigit=1;
204 }
205 if (width!=0 && _CHAR2DIGIT_(nch, base)!=-1) {
206 cur = _CHAR2DIGIT_(nch, base);
207 nch = _GETC_(file);
208 if (width>0) width--;
209 seendigit=1;
210 }
211 /* read until no more digits */
212 while (width!=0 && (nch!=_EOF_) && _CHAR2DIGIT_(nch, base)!=-1) {
213 cur = cur*base + _CHAR2DIGIT_(nch, base);
214 nch = _GETC_(file);
215 if (width>0) width--;
216 seendigit=1;
217 }
218 /* okay, done! */
219 if (!seendigit) break; /* not a valid number */
220 st = 1;
221 if (!suppress) {
222#define _SET_NUMBER_(type) *va_arg(ap, type*) = negative ? -cur : cur
223 if (number_signed) {
224 if (I64_prefix) _SET_NUMBER_(long long);
225 else if (l_prefix) _SET_NUMBER_(long int);
226 else if (h_prefix) _SET_NUMBER_(short int);
227 else _SET_NUMBER_(int);
228 } else {
229 if (negative) {
230 WARN("Dropping sign in reading a negative number into an unsigned value");
231 negative = 0;
232 }
233 if (I64_prefix) _SET_NUMBER_(long long);
234 else if (l_prefix) _SET_NUMBER_(unsigned long int);
235 else if (h_prefix)
236 _SET_NUMBER_(unsigned short int);
237 else _SET_NUMBER_(unsigned int);
238 }
239 }
240 }
241 break;
242 case 'e':
243 case 'E':
244 case 'f':
245 case 'g':
246 case 'G': { /* read a float */
247 long double cur = 0;
248 int negative = 0;
249 /* skip initial whitespace */
250 while ((nch!=_EOF_) && _ISSPACE_(nch))
251 nch = _GETC_(file);
252 /* get sign. */
253 if (nch == '-' || nch == '+') {
254 negative = (nch=='-');
255 if (width>0) width--;
256 if (width==0) break;
257 nch = _GETC_(file);
258 }
259 /* get first digit. */
260 if (!_ISDIGIT_(nch)) break;
261 cur = (nch - '0') * (negative ? -1 : 1);
262 nch = _GETC_(file);
263 if (width>0) width--;
264 /* read until no more digits */
265 while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) {
266 cur = cur*10 + (nch - '0');
267 nch = _GETC_(file);
268 if (width>0) width--;
269 }
270 /* handle decimals */
271 if (width!=0 && nch == '.') {
272 float dec = 1;
273 nch = _GETC_(file);
274 if (width>0) width--;
275 while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) {
276 dec /= 10;
277 cur += dec * (nch - '0');
278 nch = _GETC_(file);
279 if (width>0) width--;
280 }
281 }
282 /* handle exponent */
283 if (width!=0 && (nch == 'e' || nch == 'E')) {
284 int exponent = 0, negexp = 0;
285 float expcnt;
286 nch = _GETC_(file);
287 if (width>0) width--;
288 /* possible sign on the exponent */
289 if (width!=0 && (nch=='+' || nch=='-')) {
290 negexp = (nch=='-');
291 nch = _GETC_(file);
292 if (width>0) width--;
293 }
294 /* exponent digits */
295 while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) {
296 exponent *= 10;
297 exponent += (nch - '0');
298 nch = _GETC_(file);
299 if (width>0) width--;
300 }
301 /* update 'cur' with this exponent. */
302 expcnt = negexp ? .1 : 10;
303 while (exponent!=0) {
304 if (exponent&1)
305 cur*=expcnt;
306 exponent/=2;
307 expcnt=expcnt*expcnt;
308 }
309 }
310 st = 1;
311 if (!suppress) {
312 if (L_prefix) _SET_NUMBER_(long double);
313 else if (l_prefix) _SET_NUMBER_(double);
314 else _SET_NUMBER_(float);
315 }
316 }
317 break;
318 /* According to
319 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt_scanf_type_field_characters.asp
320 * 's' reads a character string in a call to fscanf
321 * and 'S' a wide character string and vice versa in a
322 * call to fwscanf. The 'h', 'w' and 'l' prefixes override
323 * this behaviour. 'h' forces reading char * but 'l' and 'w'
324 * force reading WCHAR. */
325 case 's':
326 if (w_prefix || l_prefix) goto widecharstring;
327 else if (h_prefix) goto charstring;
328#ifdef WIDE_SCANF
329 else goto widecharstring;
330#else /* WIDE_SCANF */
331 else goto charstring;
332#endif /* WIDE_SCANF */
333 case 'S':
334 if (w_prefix || l_prefix) goto widecharstring;
335 else if (h_prefix) goto charstring;
336#ifdef WIDE_SCANF
337 else goto charstring;
338#else /* WIDE_SCANF */
339 else goto widecharstring;
340#endif /* WIDE_SCANF */
341 charstring: { /* read a word into a char */
342 char*str = suppress ? NULL : va_arg(ap, char*);
343 char*sptr = str;
344 /* skip initial whitespace */
345 while ((nch!=_EOF_) && _ISSPACE_(nch))
346 nch = _GETC_(file);
347 /* read until whitespace */
348 while (width!=0 && (nch!=_EOF_) && !_ISSPACE_(nch)) {
349#ifdef WIDE_SCANF
350 if (!suppress) *sptr++ = _CONVERT_(nch);
351#else /* WIDE_SCANF */
352 if (!suppress) *sptr++ = nch;
353#endif /* WIDE_SCANF */
354 st++;
355 nch = _GETC_(file);
356 if (width>0) width--;
357 }
358 /* terminate */
359 if (!suppress) *sptr = 0;
360 }
361 break;
362 widecharstring: { /* read a word into a wchar_t* */
363 MSVCRT_wchar_t*str =
364 suppress ? NULL : va_arg(ap, MSVCRT_wchar_t*);
365 MSVCRT_wchar_t*sptr = str;
366 /* skip initial whitespace */
367 while ((nch!=_EOF_) && _ISSPACE_(nch))
368 nch = _GETC_(file);
369 /* read until whitespace */
370 while (width!=0 && (nch!=_EOF_) && !_ISSPACE_(nch)) {
371#ifdef WIDE_SCANF
372 if (!suppress) *sptr++ = nch;
373#else /* WIDE_SCANF */
374 if (!suppress) *sptr++ = _CONVERT_(nch);
375#endif /* WIDE_SCANF */
376 st++;
377 nch = _GETC_(file);
378 if (width>0) width--;
379 }
380 /* terminate */
381 if (!suppress) *sptr = 0;
382 }
383 break;
384 /* 'c' and 'C work analogously to 's' and 'S' as described
385 * above */
386 case 'c':
387 if (w_prefix || l_prefix) goto widecharacter;
388 else if (h_prefix) goto character;
389#ifdef WIDE_SCANF
390 else goto widecharacter;
391#else /* WIDE_SCANF */
392 else goto character;
393#endif /* WIDE_SCANF */
394 case 'C':
395 if (w_prefix || l_prefix) goto widecharacter;
396 else if (h_prefix) goto character;
397#ifdef WIDE_SCANF
398 else goto character;
399#else /* WIDE_SCANF */
400 else goto widecharacter;
401#endif /* WIDE_SCANF */
402 character: { /* read single character into char */
403 if (!suppress) {
404 char*c = va_arg(ap, char*);
405#ifdef WIDE_SCANF
406 *c = _CONVERT_(nch);
407#else /* WIDE_SCANF */
408 *c = nch;
409#endif /* WIDE_SCANF */
410 st = 1;
411 }
412 nch = _GETC_(file);
413 }
414 break;
415 widecharacter: {
416 if (!suppress) { /* read single character into a wchar_t */
417 MSVCRT_wchar_t*c = va_arg(ap, MSVCRT_wchar_t*);
418#ifdef WIDE_SCANF
419 *c = nch;
420#else /* WIDE_SCANF */
421 *c = _CONVERT_(nch);
422#endif /* WIDE_SCANF */
423 st = 1;
424 }
425 nch = _GETC_(file);
426 }
427 break;
428 case 'n': {
429 if (!suppress) {
430 int*n = va_arg(ap, int*);
431 *n = rd;
432 }
433 }
434 break;
435 case '[': {
436 _CHAR_ *str = suppress ? NULL : va_arg(ap, _CHAR_*);
437 _CHAR_ *sptr = str;
438 RTL_BITMAP bitMask;
439 LPBYTE Mask;
440 int invert = 0; /* Set if we are NOT to find the chars */
441
442 /* Init our bitmap */
443#ifdef WIDE_SCANF
444 Mask = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 65536/8);
445 RtlInitializeBitMap(&bitMask, Mask, 65536);
446#else /* WIDE_SCANF */
447 Mask = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 256/8);
448 RtlInitializeBitMap(&bitMask, Mask, 256);
449#endif /* WIDE_SCANF */
450
451 /* Read the format */
452 format++;
453 if(*format == '^') {
454 invert = 1;
455 format++;
456 }
457 if(*format == ']') {
458 RtlSetBits(&bitMask, ']', 1);
459 format++;
460 }
461 while(*format && (*format != ']')) {
462 if((*format == '-') && (*(format + 1) != ']')) {
463 int n = 0;
464 for(;(n + *(format - 1)) < *(format + 1); n++)
465 RtlSetBits(&bitMask, n + *(format - 1), 1);
466 format++;
467 }
468 RtlSetBits(&bitMask, *format, 1);
469 format++;
470 }
471 /* read until char is not suitable */
472 while ((width != 0) && (nch != _EOF_)) {
473 if(!invert) {
474 if(RtlAreBitsSet(&bitMask, nch, 1)) {
475#ifdef WIDE_SCANF
476 if (!suppress) *sptr++ = _CONVERT_(nch);
477#else /* WIDE_SCANF */
478 if (!suppress) *sptr++ = nch;
479#endif /* WIDE_SCANF */
480 } else
481 break;
482 } else {
483 if(RtlAreBitsClear(&bitMask, nch, 1)) {
484#ifdef WIDE_SCANF
485 if (!suppress) *sptr++ = _CONVERT_(nch);
486#else /* WIDE_SCANF */
487 if (!suppress) *sptr++ = nch;
488#endif /* WIDE_SCANF */
489 } else
490 break;
491 }
492 st++;
493 nch = _GETC_(file);
494 if (width>0) width--;
495 }
496 /* terminate */
497 if (!suppress) *sptr = 0;
498 HeapFree(GetProcessHeap(), 0, Mask);
499 }
500 break;
501 default:
502 /* From spec: "if a percent sign is followed by a character
503 * that has no meaning as a format-control character, that
504 * character and the following characters are treated as
505 * an ordinary sequence of characters, that is, a sequence
506 * of characters that must match the input. For example,
507 * to specify that a percent-sign character is to be input,
508 * use %%." */
509 while ((nch!=_EOF_) && _ISSPACE_(nch))
510 nch = _GETC_(file);
511 if (nch==*format) {
512 suppress = 1; /* whoops no field to be read */
513 st = 1; /* but we got what we expected */
514 nch = _GETC_(file);
515 }
516 break;
517 }
518 if (st && !suppress) rd++;
519 else if (!st) break;
520 }
521 /* a non-white-space character causes scanf to read, but not store,
522 * a matching non-white-space character. */
523 else {
524 /* check for character match */
525 if (nch == *format) {
526 nch = _GETC_(file);
527 } else break;
528 }
529 format++;
530 }
531 if (nch!=_EOF_) {
532 _UNGETC_(nch, file);
533 }
534 va_end(ap);
535 TRACE("returning %d\n", rd);
536 return rd;
537}
538
539#undef _CHAR_
540#undef _EOF_
541#undef _ISSPACE_
542#undef _ISDIGIT_
543#undef _CONVERT_
544#undef _CHAR2DIGIT_
545#undef _GETC_
546#undef _UNGETC_
547#undef _FUNCTION_
Note: See TracBrowser for help on using the repository browser.