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

Last change on this file since 9633 was 9633, checked in by sandervl, 23 years ago

PF: Msvcrt Wine port with GCC

File size: 17.2 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) isspace(c)
37#define _ISDIGIT_(c) 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) _getch()
44#define _UNGETC_(nch, file) _ungetch(nch)
45#define _FUNCTION_ _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 (l_prefix) _SET_NUMBER_(long int);
225 else if (h_prefix) _SET_NUMBER_(short int);
226 else _SET_NUMBER_(int);
227 } else {
228 if (negative) {
229 WARN("Dropping sign in reading a negative number into an unsigned value");
230 negative = 0;
231 }
232 if (l_prefix) _SET_NUMBER_(unsigned long int);
233 else if (h_prefix)
234 _SET_NUMBER_(unsigned short int);
235 else _SET_NUMBER_(unsigned int);
236 }
237 }
238 }
239 break;
240 case 'e':
241 case 'E':
242 case 'f':
243 case 'g':
244 case 'G': { /* read a float */
245 long double cur = 0;
246 int negative = 0;
247 /* skip initial whitespace */
248 while ((nch!=_EOF_) && _ISSPACE_(nch))
249 nch = _GETC_(file);
250 /* get sign. */
251 if (nch == '-' || nch == '+') {
252 negative = (nch=='-');
253 if (width>0) width--;
254 if (width==0) break;
255 nch = _GETC_(file);
256 }
257 /* get first digit. */
258 if (!_ISDIGIT_(nch)) break;
259 cur = (nch - '0') * (negative ? -1 : 1);
260 nch = _GETC_(file);
261 if (width>0) width--;
262 /* read until no more digits */
263 while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) {
264 cur = cur*10 + (nch - '0');
265 nch = _GETC_(file);
266 if (width>0) width--;
267 }
268 /* handle decimals */
269 if (width!=0 && nch == '.') {
270 float dec = 1;
271 nch = _GETC_(file);
272 if (width>0) width--;
273 while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) {
274 dec /= 10;
275 cur += dec * (nch - '0');
276 nch = _GETC_(file);
277 if (width>0) width--;
278 }
279 }
280 /* handle exponent */
281 if (width!=0 && (nch == 'e' || nch == 'E')) {
282 int exponent = 0, negexp = 0;
283 float expcnt;
284 nch = _GETC_(file);
285 if (width>0) width--;
286 /* possible sign on the exponent */
287 if (width!=0 && (nch=='+' || nch=='-')) {
288 negexp = (nch=='-');
289 nch = _GETC_(file);
290 if (width>0) width--;
291 }
292 /* exponent digits */
293 while (width!=0 && (nch!=_EOF_) && _ISDIGIT_(nch)) {
294 exponent *= 10;
295 exponent += (nch - '0');
296 nch = _GETC_(file);
297 if (width>0) width--;
298 }
299 /* update 'cur' with this exponent. */
300 expcnt = negexp ? .1 : 10;
301 while (exponent!=0) {
302 if (exponent&1)
303 cur*=expcnt;
304 exponent/=2;
305 expcnt=expcnt*expcnt;
306 }
307 }
308 st = 1;
309 if (!suppress) {
310 if (L_prefix) _SET_NUMBER_(long double);
311 else if (l_prefix) _SET_NUMBER_(double);
312 else _SET_NUMBER_(float);
313 }
314 }
315 break;
316 /* According to
317 * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_crt_scanf_type_field_characters.asp
318 * 's' reads a character string in a call to fscanf
319 * and 'S' a wide character string and vice versa in a
320 * call to fwscanf. The 'h', 'w' and 'l' prefixes override
321 * this behaviour. 'h' forces reading char * but 'l' and 'w'
322 * force reading WCHAR. */
323 case 's':
324 if (w_prefix || l_prefix) goto widecharstring;
325 else if (h_prefix) goto charstring;
326#ifdef WIDE_SCANF
327 else goto widecharstring;
328#else /* WIDE_SCANF */
329 else goto charstring;
330#endif /* WIDE_SCANF */
331 case 'S':
332 if (w_prefix || l_prefix) goto widecharstring;
333 else if (h_prefix) goto charstring;
334#ifdef WIDE_SCANF
335 else goto charstring;
336#else /* WIDE_SCANF */
337 else goto widecharstring;
338#endif /* WIDE_SCANF */
339 charstring: { /* read a word into a char */
340 char*str = suppress ? NULL : va_arg(ap, char*);
341 char*sptr = str;
342 /* skip initial whitespace */
343 while ((nch!=_EOF_) && _ISSPACE_(nch))
344 nch = _GETC_(file);
345 /* read until whitespace */
346 while (width!=0 && (nch!=_EOF_) && !_ISSPACE_(nch)) {
347#ifdef WIDE_SCANF
348 if (!suppress) *sptr++ = _CONVERT_(nch);
349#else /* WIDE_SCANF */
350 if (!suppress) *sptr++ = nch;
351#endif /* WIDE_SCANF */
352 st++;
353 nch = _GETC_(file);
354 if (width>0) width--;
355 }
356 /* terminate */
357 if (!suppress) *sptr = 0;
358 }
359 break;
360 widecharstring: { /* read a word into a wchar_t* */
361 MSVCRT_wchar_t*str =
362 suppress ? NULL : va_arg(ap, MSVCRT_wchar_t*);
363 MSVCRT_wchar_t*sptr = str;
364 /* skip initial whitespace */
365 while ((nch!=_EOF_) && _ISSPACE_(nch))
366 nch = _GETC_(file);
367 /* read until whitespace */
368 while (width!=0 && (nch!=_EOF_) && !_ISSPACE_(nch)) {
369#ifdef WIDE_SCANF
370 if (!suppress) *sptr++ = nch;
371#else /* WIDE_SCANF */
372 if (!suppress) *sptr++ = _CONVERT_(nch);
373#endif /* WIDE_SCANF */
374 st++;
375 nch = _GETC_(file);
376 if (width>0) width--;
377 }
378 /* terminate */
379 if (!suppress) *sptr = 0;
380 }
381 break;
382 /* 'c' and 'C work analogously to 's' and 'S' as described
383 * above */
384 case 'c':
385 if (w_prefix || l_prefix) goto widecharacter;
386 else if (h_prefix) goto character;
387#ifdef WIDE_SCANF
388 else goto widecharacter;
389#else /* WIDE_SCANF */
390 else goto character;
391#endif /* WIDE_SCANF */
392 case 'C':
393 if (w_prefix || l_prefix) goto widecharacter;
394 else if (h_prefix) goto character;
395#ifdef WIDE_SCANF
396 else goto character;
397#else /* WIDE_SCANF */
398 else goto widecharacter;
399#endif /* WIDE_SCANF */
400 character: { /* read single character into char */
401 if (!suppress) {
402 char*c = va_arg(ap, char*);
403#ifdef WIDE_SCANF
404 *c = _CONVERT_(nch);
405#else /* WIDE_SCANF */
406 *c = nch;
407#endif /* WIDE_SCANF */
408 st = 1;
409 }
410 nch = _GETC_(file);
411 }
412 break;
413 widecharacter: {
414 if (!suppress) { /* read single character into a wchar_t */
415 MSVCRT_wchar_t*c = va_arg(ap, MSVCRT_wchar_t*);
416#ifdef WIDE_SCANF
417 *c = nch;
418#else /* WIDE_SCANF */
419 *c = _CONVERT_(nch);
420#endif /* WIDE_SCANF */
421 st = 1;
422 }
423 nch = _GETC_(file);
424 }
425 break;
426 case 'n': {
427 if (!suppress) {
428 int*n = va_arg(ap, int*);
429 *n = rd;
430 }
431 }
432 break;
433 case '[': {
434 _CHAR_ *str = suppress ? NULL : va_arg(ap, _CHAR_*);
435 _CHAR_ *sptr = str;
436 RTL_BITMAP bitMask;
437 LPBYTE Mask;
438 int invert = 0; /* Set if we are NOT to find the chars */
439
440 /* Init our bitmap */
441#ifdef WIDE_SCANF
442 Mask = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 65536/8);
443 RtlInitializeBitMap(&bitMask, Mask, 65536);
444#else /* WIDE_SCANF */
445 Mask = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, 256/8);
446 RtlInitializeBitMap(&bitMask, Mask, 256);
447#endif /* WIDE_SCANF */
448
449 /* Read the format */
450 format++;
451 if(*format == '^') {
452 invert = 1;
453 format++;
454 }
455 if(*format == ']') {
456 RtlSetBits(&bitMask, ']', 1);
457 format++;
458 }
459 while(*format && (*format != ']')) {
460 if((*format == '-') && (*(format + 1) != ']')) {
461 int n = 0;
462 for(;(n + *(format - 1)) < *(format + 1); n++)
463 RtlSetBits(&bitMask, n + *(format - 1), 1);
464 format++;
465 }
466 RtlSetBits(&bitMask, *format, 1);
467 format++;
468 }
469 /* read until char is not suitable */
470 while ((width != 0) && (nch != _EOF_)) {
471 if(!invert) {
472 if(RtlAreBitsSet(&bitMask, nch, 1)) {
473#ifdef WIDE_SCANF
474 if (!suppress) *sptr++ = _CONVERT_(nch);
475#else /* WIDE_SCANF */
476 if (!suppress) *sptr++ = nch;
477#endif /* WIDE_SCANF */
478 } else
479 break;
480 } else {
481 if(RtlAreBitsClear(&bitMask, nch, 1)) {
482#ifdef WIDE_SCANF
483 if (!suppress) *sptr++ = _CONVERT_(nch);
484#else /* WIDE_SCANF */
485 if (!suppress) *sptr++ = nch;
486#endif /* WIDE_SCANF */
487 } else
488 break;
489 }
490 st++;
491 nch = _GETC_(file);
492 if (width>0) width--;
493 }
494 /* terminate */
495 if (!suppress) *sptr = 0;
496 HeapFree(GetProcessHeap(), 0, Mask);
497 }
498 break;
499 default:
500 /* From spec: "if a percent sign is followed by a character
501 * that has no meaning as a format-control character, that
502 * character and the following characters are treated as
503 * an ordinary sequence of characters, that is, a sequence
504 * of characters that must match the input. For example,
505 * to specify that a percent-sign character is to be input,
506 * use %%." */
507 while ((nch!=_EOF_) && _ISSPACE_(nch))
508 nch = _GETC_(file);
509 if (nch==*format) {
510 suppress = 1; /* whoops no field to be read */
511 st = 1; /* but we got what we expected */
512 nch = _GETC_(file);
513 }
514 break;
515 }
516 if (st && !suppress) rd++;
517 else if (!st) break;
518 }
519 /* a non-white-space character causes scanf to read, but not store,
520 * a matching non-white-space character. */
521 else {
522 /* check for character match */
523 if (nch == *format) {
524 nch = _GETC_(file);
525 } else break;
526 }
527 format++;
528 }
529 if (nch!=_EOF_) {
530 _UNGETC_(nch, file);
531 }
532 va_end(ap);
533 TRACE("returning %d\n", rd);
534 return rd;
535}
536
537#undef _CHAR_
538#undef _EOF_
539#undef _ISSPACE_
540#undef _ISDIGIT_
541#undef _CONVERT_
542#undef _CHAR2DIGIT_
543#undef _GETC_
544#undef _UNGETC_
545#undef _FUNCTION_
Note: See TracBrowser for help on using the repository browser.