1 | // Licensed to the .NET Foundation under one or more agreements. |
2 | // The .NET Foundation licenses this file to you under the MIT license. |
3 | // See the LICENSE file in the project root for more information. |
4 | |
5 | /*** |
6 | *input.c - C formatted input, used by scanf, etc. |
7 | * |
8 | |
9 | * |
10 | *Purpose: |
11 | * defines _input() to do formatted input; called from scanf(), |
12 | * etc. functions. This module defines _cscanf() instead when |
13 | * CPRFLAG is defined. The file cscanf.c defines that symbol |
14 | * and then includes this file in order to implement _cscanf(). |
15 | * |
16 | *Note: |
17 | * this file is included in safecrt.lib build directly, plese refer |
18 | * to safecrt_[w]input_s.c |
19 | * |
20 | *******************************************************************************/ |
21 | |
22 | |
23 | #define ALLOW_RANGE /* enable "%[a-z]"-style scansets */ |
24 | |
25 | |
26 | /* temporary work-around for compiler without 64-bit support */ |
27 | |
28 | #ifndef _INTEGRAL_MAX_BITS |
29 | #define _INTEGRAL_MAX_BITS 64 |
30 | #endif /* _INTEGRAL_MAX_BITS */ |
31 | |
32 | // typedef __int64_t __int64; |
33 | |
34 | #ifndef FALSE |
35 | #define FALSE 0 |
36 | #endif |
37 | |
38 | #ifndef TRUE |
39 | #define TRUE 1 |
40 | #endif |
41 | |
42 | #define UNALIGNED |
43 | |
44 | #define _BEGIN_SECURE_CRT_DEPRECATION_DISABLE |
45 | #define _END_SECURE_CRT_DEPRECATION_DISABLE |
46 | |
47 | #define _CVTBUFSIZE (309+40) /* # of digits in max. dp value + slop */ |
48 | |
49 | //#include <cruntime.h> |
50 | //#include <stdio.h> |
51 | //#include <ctype.h> |
52 | //#include <cvt.h> |
53 | //#include <conio.h> |
54 | //#include <stdarg.h> |
55 | //#include <string.h> |
56 | //#include <internal.h> |
57 | //#include <fltintrn.h> |
58 | //#include <malloc.h> |
59 | //#include <locale.h> |
60 | //#include <mtdll.h> |
61 | //#include <stdlib.h> |
62 | //#include <setlocal.h> |
63 | //#include <dbgint.h> |
64 | |
65 | //#ifndef _INC_INTERNAL_SAFECRT |
66 | //#include <internal_securecrt.h> |
67 | //#endif /* _INC_INTERNAL_SAFECRT */ |
68 | |
69 | //#ifdef _MBCS |
70 | //#undef _MBCS |
71 | //#endif /* _MBCS */ |
72 | //#include <tchar.h> |
73 | |
74 | #define _MBTOWC(x,y,z) _minimal_chartowchar( x, y ) |
75 | |
76 | #define _istspace(x) isspace((unsigned char)x) |
77 | |
78 | #define _malloc_crt PAL_malloc |
79 | #define _realloc_crt PAL_realloc |
80 | #define _free_crt PAL_free |
81 | |
82 | #define _FASSIGN(flag, argument, number, dec_point, locale) _safecrt_fassign((flag), (argument), (number)) |
83 | #define _WFASSIGN(flag, argument, number, dec_point, locale) _safecrt_wfassign((flag), (argument), (number)) |
84 | |
85 | #if defined (UNICODE) |
86 | #define ALLOC_TABLE 1 |
87 | #else /* defined (UNICODE) */ |
88 | #define ALLOC_TABLE 0 |
89 | #endif /* defined (UNICODE) */ |
90 | |
91 | #define HEXTODEC(chr) _hextodec(chr) |
92 | |
93 | #define LEFT_BRACKET ('[' | ('a' - 'A')) /* 'lowercase' version */ |
94 | |
95 | static int __cdecl _hextodec(_TCHAR); |
96 | #ifdef CPRFLAG |
97 | |
98 | #define INC() (++charcount, _inc()) |
99 | #define UN_INC(chr) (--charcount, _un_inc(chr)) |
100 | #define EAT_WHITE() _whiteout(&charcount) |
101 | |
102 | static int __cdecl _inc(void); |
103 | static void __cdecl _un_inc(int); |
104 | static int __cdecl _whiteout(int *); |
105 | |
106 | #else /* CPRFLAG */ |
107 | |
108 | #define INC() (++charcount, _inc(stream)) |
109 | #define UN_INC(chr) (--charcount, _un_inc(chr, stream)) |
110 | #define EAT_WHITE() _whiteout(&charcount, stream) |
111 | |
112 | static int __cdecl _inc(miniFILE *); |
113 | static void __cdecl _un_inc(int, miniFILE *); |
114 | static int __cdecl _whiteout(int *, miniFILE *); |
115 | |
116 | #endif /* CPRFLAG */ |
117 | |
118 | #ifndef _UNICODE |
119 | #define _ISDIGIT(chr) isdigit((unsigned char)chr) |
120 | #define _ISXDIGIT(chr) isxdigit((unsigned char)chr) |
121 | #else /* _UNICODE */ |
122 | #define _ISDIGIT(chr) ( !(chr & 0xff00) && isdigit( ((chr) & 0x00ff) ) ) |
123 | #define _ISXDIGIT(chr) ( !(chr & 0xff00) && isxdigit( ((chr) & 0x00ff) ) ) |
124 | #endif /* _UNICODE */ |
125 | |
126 | #define MUL10(x) ( (((x)<<2) + (x))<<1 ) |
127 | |
128 | |
129 | #define LONGLONG_IS_INT64 1 /* 1 means long long is same as int64 |
130 | 0 means long long is same as long */ |
131 | |
132 | /*** |
133 | * int __check_float_string(size_t,size_t *, _TCHAR**, _TCHAR*, int*) |
134 | * |
135 | * Purpose: |
136 | * Check if there is enough space insert onemore character in the given |
137 | * block, if not then allocate more memory. |
138 | * |
139 | * Return: |
140 | * FALSE if more memory needed and the reallocation failed. |
141 | * |
142 | *******************************************************************************/ |
143 | |
144 | static int __check_float_string(size_t nFloatStrUsed, |
145 | size_t *pnFloatStrSz, |
146 | _TCHAR **pFloatStr, |
147 | _TCHAR *floatstring, |
148 | int *pmalloc_FloatStrFlag) |
149 | { |
150 | void *tmpPointer; |
151 | _ASSERTE(nFloatStrUsed<=(*pnFloatStrSz)); |
152 | if (nFloatStrUsed==(*pnFloatStrSz)) |
153 | { |
154 | size_t newSize; |
155 | |
156 | // Will (*pnFloatStrSz) * 2 * sizeof(_TCHAR) overflow? |
157 | if ( *pnFloatStrSz > (SIZE_T_MAX / 2 / sizeof(_TCHAR))) |
158 | { |
159 | return FALSE; |
160 | } |
161 | |
162 | newSize = *pnFloatStrSz * 2 * sizeof(_TCHAR); |
163 | |
164 | if ((*pFloatStr)==floatstring) |
165 | { |
166 | if (((*pFloatStr)=(_TCHAR *)_malloc_crt(newSize))==NULL) |
167 | { |
168 | return FALSE; |
169 | } |
170 | |
171 | (*pmalloc_FloatStrFlag)=1; |
172 | |
173 | memcpy((*pFloatStr),floatstring,(*pnFloatStrSz)*sizeof(_TCHAR)); |
174 | (*pnFloatStrSz)*=2; |
175 | } |
176 | else |
177 | { |
178 | if ((tmpPointer=(_TCHAR *)_realloc_crt((*pFloatStr), newSize))==NULL) |
179 | { |
180 | return FALSE; |
181 | } |
182 | (*pFloatStr)=(_TCHAR *)(tmpPointer); |
183 | (*pnFloatStrSz)*=2; |
184 | } |
185 | } |
186 | return TRUE; |
187 | } |
188 | |
189 | |
190 | #define ASCII 32 /* # of bytes needed to hold 256 bits */ |
191 | |
192 | #define SCAN_SHORT 0 /* also for FLOAT */ |
193 | #define SCAN_LONG 1 /* also for DOUBLE */ |
194 | #define SCAN_L_DOUBLE 2 /* only for LONG DOUBLE */ |
195 | |
196 | #define SCAN_NEAR 0 |
197 | #define SCAN_FAR 1 |
198 | |
199 | #ifndef _UNICODE |
200 | #define TABLESIZE ASCII |
201 | #else /* _UNICODE */ |
202 | #define TABLESIZE (ASCII * 256) |
203 | #endif /* _UNICODE */ |
204 | |
205 | |
206 | /*** |
207 | *int _input(stream, format, arglist), static int input(format, arglist) |
208 | * |
209 | *Purpose: |
210 | * get input items (data items or literal matches) from the input stream |
211 | * and assign them if appropriate to the items thru the arglist. this |
212 | * function is intended for internal library use only, not for the user |
213 | * |
214 | * The _input entry point is for the normal scanf() functions |
215 | * The input entry point is used when compiling for _cscanf() [CPRFLAF |
216 | * defined] and is a static function called only by _cscanf() -- reads from |
217 | * console. |
218 | * |
219 | * This code also defines _input_s, which works differently for %c, %s & %[. |
220 | * For these, _input_s first picks up the next argument from the variable |
221 | * argument list & uses it as the maximum size of the character array pointed |
222 | * to by the next argument in the list. |
223 | * |
224 | *Entry: |
225 | * FILE *stream - file to read from |
226 | * char *format - format string to determine the data to read |
227 | * arglist - list of pointer to data items |
228 | * |
229 | *Exit: |
230 | * returns number of items assigned and fills in data items |
231 | * returns EOF if error or EOF found on stream before 1st data item matched |
232 | * |
233 | *Exceptions: |
234 | * |
235 | *******************************************************************************/ |
236 | |
237 | #define _INTRN_LOCALE_CONV( x ) localeconv() |
238 | |
239 | #ifndef _UNICODE |
240 | int __cdecl __tinput_s (miniFILE* stream, const _TUCHAR* format, va_list arglist) |
241 | #else |
242 | int __cdecl __twinput_s (miniFILE* stream, const _TUCHAR* format, va_list arglist) |
243 | #endif /* _UNICODE */ |
244 | { |
245 | _TCHAR floatstring[_CVTBUFSIZE + 1]; |
246 | _TCHAR *pFloatStr=floatstring; |
247 | size_t nFloatStrUsed=0; |
248 | size_t nFloatStrSz=sizeof(floatstring)/sizeof(floatstring[0]); |
249 | int malloc_FloatStrFlag=0; |
250 | |
251 | unsigned long number; /* temp hold-value */ |
252 | #if ALLOC_TABLE |
253 | char *table = NULL; /* which chars allowed for %[] */ |
254 | int malloc_flag = 0; /* is "table" allocated on the heap? */ |
255 | #else /* ALLOC_TABLE */ |
256 | char AsciiTable[TABLESIZE]; |
257 | char *table = AsciiTable; |
258 | #endif /* ALLOC_TABLE */ |
259 | |
260 | #if _INTEGRAL_MAX_BITS >= 64 |
261 | uint64_t num64 = 0LL; /* temp for 64-bit integers */ |
262 | #endif /* _INTEGRAL_MAX_BITS >= 64 */ |
263 | void *pointer=NULL; /* points to user data receptacle */ |
264 | void *start; /* indicate non-empty string */ |
265 | |
266 | |
267 | #ifndef _UNICODE |
268 | wchar_t wctemp=L'\0'; |
269 | #endif /* _UNICODE */ |
270 | _TUCHAR *scanptr; /* for building "table" data */ |
271 | int ch = 0; |
272 | int charcount; /* total number of chars read */ |
273 | int comchr; /* holds designator type */ |
274 | int count; /* return value. # of assignments */ |
275 | |
276 | int started; /* indicate good number */ |
277 | int width; /* width of field */ |
278 | int widthset; /* user has specified width */ |
279 | #ifdef _SECURE_SCANF |
280 | size_t array_width = 0; |
281 | size_t original_array_width = 0; |
282 | int enomem = 0; |
283 | int format_error = FALSE; |
284 | #endif /* _SECURE_SCANF */ |
285 | |
286 | /* Neither coerceshort nor farone are need for the 386 */ |
287 | |
288 | |
289 | char done_flag; /* general purpose loop monitor */ |
290 | char longone; /* 0 = SHORT, 1 = LONG, 2 = L_DOUBLE */ |
291 | #if _INTEGRAL_MAX_BITS >= 64 |
292 | int integer64; /* 1 for 64-bit integer, 0 otherwise */ |
293 | #endif /* _INTEGRAL_MAX_BITS >= 64 */ |
294 | signed char widechar; /* -1 = char, 0 = ????, 1 = wchar_t */ |
295 | char reject; /* %[^ABC] instead of %[ABC] */ |
296 | char negative; /* flag for '-' detected */ |
297 | char suppress; /* don't assign anything */ |
298 | char match; /* flag: !0 if any fields matched */ |
299 | va_list arglistsave; /* save arglist value */ |
300 | |
301 | char fl_wchar_arg; /* flags wide char/string argument */ |
302 | |
303 | _TCHAR decimal; |
304 | |
305 | |
306 | _TUCHAR rngch; |
307 | _TUCHAR last; |
308 | _TUCHAR prevchar; |
309 | _TCHAR tch; |
310 | |
311 | _VALIDATE_RETURN( (format != NULL), EINVAL, EOF); |
312 | |
313 | #ifndef CPRFLAG |
314 | _VALIDATE_RETURN( (stream != NULL), EINVAL, EOF); |
315 | #endif /* CPRFLAG */ |
316 | |
317 | /* |
318 | count = # fields assigned |
319 | charcount = # chars read |
320 | match = flag indicating if any fields were matched |
321 | |
322 | [Note that we need both count and match. For example, a field |
323 | may match a format but have assignments suppressed. In this case, |
324 | match will get set, but 'count' will still equal 0. We need to |
325 | distinguish 'match vs no-match' when terminating due to EOF.] |
326 | */ |
327 | |
328 | count = charcount = match = 0; |
329 | |
330 | while (*format) { |
331 | |
332 | if (_istspace((_TUCHAR)*format)) { |
333 | |
334 | UN_INC(EAT_WHITE()); /* put first non-space char back */ |
335 | |
336 | do { |
337 | tch = *++format; |
338 | } while (_istspace((_TUCHAR)tch)); |
339 | |
340 | continue; |
341 | |
342 | } |
343 | |
344 | if (_T('%') == *format) { |
345 | |
346 | number = 0; |
347 | prevchar = 0; |
348 | width = widthset = started = 0; |
349 | #ifdef _SECURE_SCANF |
350 | original_array_width = array_width = 0; |
351 | enomem = 0; |
352 | #endif /* _SECURE_SCANF */ |
353 | fl_wchar_arg = done_flag = suppress = negative = reject = 0; |
354 | widechar = 0; |
355 | |
356 | longone = 1; |
357 | |
358 | #if _INTEGRAL_MAX_BITS >= 64 |
359 | integer64 = 0; |
360 | #endif /* _INTEGRAL_MAX_BITS >= 64 */ |
361 | |
362 | while (!done_flag) { |
363 | |
364 | comchr = *++format; |
365 | if (_ISDIGIT((_TUCHAR)comchr)) { |
366 | ++widthset; |
367 | width = MUL10(width) + (comchr - _T('0')); |
368 | } else |
369 | switch (comchr) { |
370 | case _T('F') : |
371 | case _T('N') : /* no way to push NEAR in large model */ |
372 | break; /* NEAR is default in small model */ |
373 | case _T('h') : |
374 | /* set longone to 0 */ |
375 | --longone; |
376 | --widechar; /* set widechar = -1 */ |
377 | break; |
378 | |
379 | #if _INTEGRAL_MAX_BITS >= 64 |
380 | case _T('I'): |
381 | if ( (*(format + 1) == _T('6')) && |
382 | (*(format + 2) == _T('4')) ) |
383 | { |
384 | format += 2; |
385 | ++integer64; |
386 | num64 = 0; |
387 | break; |
388 | } |
389 | else if ( (*(format + 1) == _T('3')) && |
390 | (*(format + 2) == _T('2')) ) |
391 | { |
392 | format += 2; |
393 | break; |
394 | } |
395 | else if ( (*(format + 1) == _T('d')) || |
396 | (*(format + 1) == _T('i')) || |
397 | (*(format + 1) == _T('o')) || |
398 | (*(format + 1) == _T('x')) || |
399 | (*(format + 1) == _T('X')) ) |
400 | { |
401 | if (sizeof(void*) == sizeof(__int64)) |
402 | { |
403 | ++integer64; |
404 | num64 = 0; |
405 | } |
406 | break; |
407 | } |
408 | if (sizeof(void*) == sizeof(__int64)) |
409 | { |
410 | ++integer64; |
411 | num64 = 0; |
412 | } |
413 | goto DEFAULT_LABEL; |
414 | #endif /* _INTEGRAL_MAX_BITS >= 64 */ |
415 | |
416 | case _T('L') : |
417 | /* ++longone; */ |
418 | ++longone; |
419 | break; |
420 | |
421 | case _T('q'): |
422 | ++integer64; |
423 | num64 = 0; |
424 | break; |
425 | |
426 | case _T('l') : |
427 | if (*(format + 1) == _T('l')) |
428 | { |
429 | ++format; |
430 | #ifdef LONGLONG_IS_INT64 |
431 | ++integer64; |
432 | num64 = 0; |
433 | break; |
434 | #else /* LONGLONG_IS_INT64 */ |
435 | ++longone; |
436 | /* NOBREAK */ |
437 | #endif /* LONGLONG_IS_INT64 */ |
438 | } |
439 | else |
440 | { |
441 | ++longone; |
442 | /* NOBREAK */ |
443 | } |
444 | case _T('w') : |
445 | ++widechar; /* set widechar = 1 */ |
446 | break; |
447 | |
448 | case _T('*') : |
449 | ++suppress; |
450 | break; |
451 | |
452 | default: |
453 | DEFAULT_LABEL: |
454 | ++done_flag; |
455 | break; |
456 | } |
457 | } |
458 | |
459 | if (!suppress) { |
460 | va_copy(arglistsave, arglist); |
461 | pointer = va_arg(arglist,void *); |
462 | } else { |
463 | pointer = NULL; // doesn't matter what value we use here - we're only using it as a flag |
464 | } |
465 | |
466 | done_flag = 0; |
467 | |
468 | if (!widechar) { /* use case if not explicitly specified */ |
469 | if ((*format == _T('S')) || (*format == _T('C'))) |
470 | #ifdef _UNICODE |
471 | --widechar; |
472 | else |
473 | ++widechar; |
474 | #else /* _UNICODE */ |
475 | ++widechar; |
476 | else |
477 | --widechar; |
478 | #endif /* _UNICODE */ |
479 | } |
480 | |
481 | /* switch to lowercase to allow %E,%G, and to |
482 | keep the switch table small */ |
483 | |
484 | comchr = *format | (_T('a') - _T('A')); |
485 | |
486 | if (_T('n') != comchr) |
487 | { |
488 | if (_T('c') != comchr && LEFT_BRACKET != comchr) |
489 | ch = EAT_WHITE(); |
490 | else |
491 | ch = INC(); |
492 | } |
493 | |
494 | if (_T('n') != comchr) |
495 | { |
496 | if (_TEOF == ch) |
497 | goto error_return; |
498 | } |
499 | |
500 | if (!widthset || width) { |
501 | |
502 | #ifdef _SECURE_SCANF |
503 | if(!suppress && (comchr == _T('c') || comchr == _T('s') || comchr == LEFT_BRACKET)) { |
504 | |
505 | va_copy(arglist, arglistsave); |
506 | |
507 | /* Reinitialize pointer to point to the array to which we write the input */ |
508 | pointer = va_arg(arglist, void*); |
509 | |
510 | va_copy(arglistsave, arglist); |
511 | |
512 | /* Get the next argument - size of the array in characters */ |
513 | #ifdef _WIN64 |
514 | original_array_width = array_width = (size_t)(va_arg(arglist, unsigned int)); |
515 | #else /* _WIN64 */ |
516 | original_array_width = array_width = va_arg(arglist, size_t); |
517 | #endif /* _WIN64 */ |
518 | |
519 | if(array_width < 1) { |
520 | if (widechar > 0) |
521 | *(wchar_t UNALIGNED *)pointer = L'\0'; |
522 | else |
523 | *(char *)pointer = '\0'; |
524 | |
525 | errno = ENOMEM; |
526 | |
527 | goto error_return; |
528 | } |
529 | } |
530 | #endif /* _SECURE_SCANF */ |
531 | switch(comchr) { |
532 | |
533 | case _T('c'): |
534 | /* case _T('C'): */ |
535 | if (!widthset) { |
536 | ++widthset; |
537 | ++width; |
538 | } |
539 | if (widechar > 0) |
540 | fl_wchar_arg++; |
541 | goto scanit; |
542 | |
543 | |
544 | case _T('s'): |
545 | /* case _T('S'): */ |
546 | if(widechar > 0) |
547 | fl_wchar_arg++; |
548 | goto scanit; |
549 | |
550 | |
551 | case LEFT_BRACKET : /* scanset */ |
552 | if (widechar>0) |
553 | fl_wchar_arg++; |
554 | scanptr = (_TUCHAR *)(++format); |
555 | |
556 | if (_T('^') == *scanptr) { |
557 | ++scanptr; |
558 | --reject; /* set reject to 255 */ |
559 | } |
560 | |
561 | /* Allocate "table" on first %[] spec */ |
562 | #if ALLOC_TABLE |
563 | if (table == NULL) { |
564 | table = (char*)_malloc_crt(TABLESIZE); |
565 | if ( table == NULL) |
566 | goto error_return; |
567 | malloc_flag = 1; |
568 | } |
569 | #endif /* ALLOC_TABLE */ |
570 | memset(table, 0, TABLESIZE); |
571 | |
572 | |
573 | if (LEFT_BRACKET == comchr) |
574 | if (_T(']') == *scanptr) { |
575 | prevchar = _T(']'); |
576 | ++scanptr; |
577 | |
578 | table[ _T(']') >> 3] = 1 << (_T(']') & 7); |
579 | |
580 | } |
581 | |
582 | while (_T(']') != *scanptr) { |
583 | |
584 | rngch = *scanptr++; |
585 | |
586 | if (_T('-') != rngch || |
587 | !prevchar || /* first char */ |
588 | _T(']') == *scanptr) /* last char */ |
589 | |
590 | table[(prevchar = rngch) >> 3] |= 1 << (rngch & 7); |
591 | |
592 | else { /* handle a-z type set */ |
593 | |
594 | rngch = *scanptr++; /* get end of range */ |
595 | |
596 | if (prevchar < rngch) /* %[a-z] */ |
597 | last = rngch; |
598 | else { /* %[z-a] */ |
599 | last = prevchar; |
600 | prevchar = rngch; |
601 | } |
602 | for (rngch = prevchar; rngch <= last; ++rngch) |
603 | table[rngch >> 3] |= 1 << (rngch & 7); |
604 | |
605 | prevchar = 0; |
606 | |
607 | } |
608 | } |
609 | |
610 | |
611 | if (!*scanptr) |
612 | goto error_return; /* trunc'd format string */ |
613 | |
614 | /* scanset completed. Now read string */ |
615 | |
616 | if (LEFT_BRACKET == comchr) |
617 | format = scanptr; |
618 | |
619 | scanit: |
620 | start = pointer; |
621 | |
622 | /* |
623 | * execute the format directive. that is, scan input |
624 | * characters until the directive is fulfilled, eof |
625 | * is reached, or a non-matching character is |
626 | * encountered. |
627 | * |
628 | * it is important not to get the next character |
629 | * unless that character needs to be tested! other- |
630 | * wise, reads from line-buffered devices (e.g., |
631 | * scanf()) would require an extra, spurious, newline |
632 | * if the first newline completes the current format |
633 | * directive. |
634 | */ |
635 | UN_INC(ch); |
636 | |
637 | #ifdef _SECURE_SCANF |
638 | /* One element is needed for '\0' for %s & %[ */ |
639 | if(comchr != _T('c')) { |
640 | --array_width; |
641 | } |
642 | #endif /* _SECURE_SCANF */ |
643 | while ( !widthset || width-- ) { |
644 | |
645 | ch = INC(); |
646 | if ( |
647 | #ifndef CPRFLAG |
648 | (_TEOF != ch) && |
649 | #endif /* CPRFLAG */ |
650 | // char conditions |
651 | ( ( comchr == _T('c')) || |
652 | // string conditions !isspace() |
653 | ( ( comchr == _T('s') && |
654 | (!(ch >= _T('\t') && ch <= _T('\r')) && |
655 | ch != _T(' ')))) || |
656 | // BRACKET conditions |
657 | ( (comchr == LEFT_BRACKET) && |
658 | ((table[ch >> 3] ^ reject) & (1 << (ch & 7))) |
659 | ) |
660 | ) |
661 | ) |
662 | { |
663 | if (!suppress) { |
664 | #ifdef _SECURE_SCANF |
665 | if(!array_width) { |
666 | /* We have exhausted the user's buffer */ |
667 | |
668 | enomem = 1; |
669 | break; |
670 | } |
671 | #endif /* _SECURE_SCANF */ |
672 | #ifndef _UNICODE |
673 | if (fl_wchar_arg) { |
674 | wctemp = W('?'); |
675 | char temp[2]; |
676 | temp[0] = (char) ch; |
677 | #if 0 // we are not supporting multibyte input strings |
678 | if (isleadbyte((unsigned char)ch)) |
679 | { |
680 | temp[1] = (char) INC(); |
681 | } |
682 | #endif /* 0 */ |
683 | _MBTOWC(&wctemp, temp, MB_CUR_MAX); |
684 | *(wchar_t UNALIGNED *)pointer = wctemp; |
685 | /* just copy W('?') if mbtowc fails, errno is set by mbtowc */ |
686 | pointer = (wchar_t *)pointer + 1; |
687 | #ifdef _SECURE_SCANF |
688 | --array_width; |
689 | #endif /* _SECURE_SCANF */ |
690 | } else |
691 | #else /* _UNICODE */ |
692 | if (fl_wchar_arg) { |
693 | *(wchar_t UNALIGNED *)pointer = ch; |
694 | pointer = (wchar_t *)pointer + 1; |
695 | #ifdef _SECURE_SCANF |
696 | --array_width; |
697 | #endif /* _SECURE_SCANF */ |
698 | } else |
699 | #endif /* _UNICODE */ |
700 | { |
701 | #ifndef _UNICODE |
702 | *(char *)pointer = (char)ch; |
703 | pointer = (char *)pointer + 1; |
704 | #ifdef _SECURE_SCANF |
705 | --array_width; |
706 | #endif /* _SECURE_SCANF */ |
707 | #else /* _UNICODE */ |
708 | int temp = 0; |
709 | #ifndef _SECURE_SCANF |
710 | /* convert wide to multibyte */ |
711 | if (_ERRCHECK_EINVAL_ERANGE(wctomb_s(&temp, (char *)pointer, MB_LEN_MAX, ch)) == 0) |
712 | { |
713 | /* do nothing if wctomb fails, errno will be set to EILSEQ */ |
714 | pointer = (char *)pointer + temp; |
715 | } |
716 | #else /* _SECURE_SCANF */ |
717 | /* convert wide to multibyte */ |
718 | if (array_width >= ((size_t)MB_CUR_MAX)) |
719 | { |
720 | _BEGIN_SECURE_CRT_DEPRECATION_DISABLE |
721 | temp = wctomb((char *)pointer, ch); |
722 | _END_SECURE_CRT_DEPRECATION_DISABLE |
723 | } |
724 | else |
725 | { |
726 | char tmpbuf[MB_LEN_MAX]; |
727 | _BEGIN_SECURE_CRT_DEPRECATION_DISABLE |
728 | temp = wctomb(tmpbuf, ch); |
729 | _END_SECURE_CRT_DEPRECATION_DISABLE |
730 | if (temp > 0 && ((size_t)temp) > array_width) |
731 | { |
732 | /* We have exhausted the user's buffer */ |
733 | enomem = 1; |
734 | break; |
735 | } |
736 | memcpy(pointer, tmpbuf, temp); |
737 | } |
738 | if (temp > 0) |
739 | { |
740 | /* do nothing if wctomb fails, errno will be set to EILSEQ */ |
741 | pointer = (char *)pointer + temp; |
742 | array_width -= temp; |
743 | } |
744 | #endif /* _SECURE_SCANF */ |
745 | #endif /* _UNICODE */ |
746 | } |
747 | } /* suppress */ |
748 | else { |
749 | /* just indicate a match */ |
750 | start = (_TCHAR *)start + 1; |
751 | } |
752 | } |
753 | else { |
754 | UN_INC(ch); |
755 | break; |
756 | } |
757 | } |
758 | |
759 | /* make sure something has been matched and, if |
760 | assignment is not suppressed, null-terminate |
761 | output string if comchr != c */ |
762 | |
763 | #ifdef _SECURE_SCANF |
764 | if(enomem) { |
765 | errno = ENOMEM; |
766 | /* In case of error, blank out the input buffer */ |
767 | if (fl_wchar_arg) |
768 | { |
769 | _RESET_STRING(((wchar_t UNALIGNED *)start), original_array_width); |
770 | } |
771 | else |
772 | { |
773 | _RESET_STRING(((char *)start), original_array_width); |
774 | } |
775 | |
776 | goto error_return; |
777 | } |
778 | #endif /* _SECURE_SCANF */ |
779 | |
780 | if (start != pointer) { |
781 | if (!suppress) { |
782 | ++count; |
783 | if ('c' != comchr) /* null-terminate strings */ |
784 | { |
785 | if (fl_wchar_arg) |
786 | { |
787 | *(wchar_t UNALIGNED *)pointer = L'\0'; |
788 | #ifdef _SECURE_SCANF |
789 | _FILL_STRING(((wchar_t UNALIGNED *)start), original_array_width, |
790 | ((wchar_t UNALIGNED *)pointer - (wchar_t UNALIGNED *)start + 1)) |
791 | #endif /* _SECURE_SCANF */ |
792 | } |
793 | else |
794 | { |
795 | *(char *)pointer = '\0'; |
796 | #ifdef _SECURE_SCANF |
797 | _FILL_STRING(((char *)start), original_array_width, |
798 | ((char *)pointer - (char *)start + 1)) |
799 | #endif /* _SECURE_SCANF */ |
800 | } |
801 | } |
802 | } |
803 | else |
804 | { |
805 | // supress set, do nothing |
806 | } |
807 | } |
808 | else |
809 | goto error_return; |
810 | |
811 | break; |
812 | |
813 | case _T('i') : /* could be d, o, or x */ |
814 | |
815 | comchr = _T('d'); /* use as default */ |
816 | |
817 | case _T('x'): |
818 | |
819 | if (_T('-') == ch) { |
820 | ++negative; |
821 | |
822 | goto x_incwidth; |
823 | |
824 | } else if (_T('+') == ch) { |
825 | x_incwidth: |
826 | if (!--width && widthset) |
827 | ++done_flag; |
828 | else |
829 | ch = INC(); |
830 | } |
831 | |
832 | if (_T('0') == ch) { |
833 | |
834 | if (_T('x') == (_TCHAR)(ch = INC()) || _T('X') == (_TCHAR)ch) { |
835 | ch = INC(); |
836 | if (widthset) { |
837 | width -= 2; |
838 | if (width < 1) |
839 | ++done_flag; |
840 | } |
841 | comchr = _T('x'); |
842 | } else { |
843 | ++started; |
844 | if (_T('x') != comchr) { |
845 | if (widthset && !--width) |
846 | ++done_flag; |
847 | comchr = _T('o'); |
848 | } |
849 | else { |
850 | /* scanning a hex number that starts */ |
851 | /* with a 0. push back the character */ |
852 | /* currently in ch and restore the 0 */ |
853 | UN_INC(ch); |
854 | ch = _T('0'); |
855 | } |
856 | } |
857 | } |
858 | goto getnum; |
859 | |
860 | /* NOTREACHED */ |
861 | |
862 | case _T('p') : |
863 | /* force %hp to be treated as %p */ |
864 | longone = 1; |
865 | #ifdef _WIN64 |
866 | /* force %p to be 64 bit in WIN64 */ |
867 | ++integer64; |
868 | num64 = 0; |
869 | #endif /* _WIN64 */ |
870 | case _T('o') : |
871 | case _T('u') : |
872 | case _T('d') : |
873 | |
874 | if (_T('-') == ch) { |
875 | ++negative; |
876 | |
877 | goto d_incwidth; |
878 | |
879 | } else if (_T('+') == ch) { |
880 | d_incwidth: |
881 | if (!--width && widthset) |
882 | ++done_flag; |
883 | else |
884 | ch = INC(); |
885 | } |
886 | |
887 | getnum: |
888 | #if _INTEGRAL_MAX_BITS >= 64 |
889 | if ( integer64 ) { |
890 | |
891 | while (!done_flag) { |
892 | |
893 | if (_T('x') == comchr || _T('p') == comchr) |
894 | |
895 | if (_ISXDIGIT(ch)) { |
896 | num64 <<= 4; |
897 | ch = _hextodec(ch); |
898 | } |
899 | else |
900 | ++done_flag; |
901 | |
902 | else if (_ISDIGIT(ch)) |
903 | |
904 | if (_T('o') == comchr) |
905 | if (_T('8') > ch) |
906 | num64 <<= 3; |
907 | else { |
908 | ++done_flag; |
909 | } |
910 | else /* _T('d') == comchr */ |
911 | num64 = MUL10(num64); |
912 | |
913 | else |
914 | ++done_flag; |
915 | |
916 | if (!done_flag) { |
917 | ++started; |
918 | num64 += ch - _T('0'); |
919 | |
920 | if (widthset && !--width) |
921 | ++done_flag; |
922 | else |
923 | ch = INC(); |
924 | } else |
925 | UN_INC(ch); |
926 | |
927 | } /* end of WHILE loop */ |
928 | |
929 | if (negative) |
930 | num64 = (uint64_t )(-(__int64)num64); |
931 | } |
932 | else { |
933 | #endif /* _INTEGRAL_MAX_BITS >= 64 */ |
934 | while (!done_flag) { |
935 | |
936 | if (_T('x') == comchr || _T('p') == comchr) |
937 | |
938 | if (_ISXDIGIT(ch)) { |
939 | number = (number << 4); |
940 | ch = _hextodec(ch); |
941 | } |
942 | else |
943 | ++done_flag; |
944 | |
945 | else if (_ISDIGIT(ch)) |
946 | |
947 | if (_T('o') == comchr) |
948 | if (_T('8') > ch) |
949 | number = (number << 3); |
950 | else { |
951 | ++done_flag; |
952 | } |
953 | else /* _T('d') == comchr */ |
954 | number = MUL10(number); |
955 | |
956 | else |
957 | ++done_flag; |
958 | |
959 | if (!done_flag) { |
960 | ++started; |
961 | number += ch - _T('0'); |
962 | |
963 | if (widthset && !--width) |
964 | ++done_flag; |
965 | else |
966 | ch = INC(); |
967 | } else |
968 | UN_INC(ch); |
969 | |
970 | } /* end of WHILE loop */ |
971 | |
972 | if (negative) |
973 | number = (unsigned long)(-(long)number); |
974 | #if _INTEGRAL_MAX_BITS >= 64 |
975 | } |
976 | #endif /* _INTEGRAL_MAX_BITS >= 64 */ |
977 | if (_T('F')==comchr) /* expected ':' in long pointer */ |
978 | started = 0; |
979 | |
980 | if (started) |
981 | if (!suppress) { |
982 | |
983 | ++count; |
984 | assign_num: |
985 | #if _INTEGRAL_MAX_BITS >= 64 |
986 | if ( integer64 ) |
987 | *(__int64 UNALIGNED *)pointer = ( uint64_t )num64; |
988 | else |
989 | #endif /* _INTEGRAL_MAX_BITS >= 64 */ |
990 | if (longone) |
991 | *(int UNALIGNED *)pointer = (unsigned int)number; |
992 | else |
993 | *(short UNALIGNED *)pointer = (unsigned short)number; |
994 | |
995 | } else /*NULL*/; |
996 | else |
997 | goto error_return; |
998 | |
999 | break; |
1000 | |
1001 | case _T('n') : /* char count, don't inc return value */ |
1002 | number = charcount; |
1003 | if(!suppress) |
1004 | goto assign_num; /* found in number code above */ |
1005 | break; |
1006 | |
1007 | |
1008 | case _T('e') : |
1009 | /* case _T('E') : */ |
1010 | case _T('f') : |
1011 | case _T('g') : /* scan a float */ |
1012 | /* case _T('G') : */ |
1013 | nFloatStrUsed=0; |
1014 | |
1015 | if (_T('-') == ch) { |
1016 | pFloatStr[nFloatStrUsed++] = _T('-'); |
1017 | goto f_incwidth; |
1018 | |
1019 | } else if (_T('+') == ch) { |
1020 | f_incwidth: |
1021 | --width; |
1022 | ch = INC(); |
1023 | } |
1024 | |
1025 | if (!widthset) /* must watch width */ |
1026 | width = -1; |
1027 | |
1028 | |
1029 | /* now get integral part */ |
1030 | |
1031 | while (_ISDIGIT(ch) && width--) { |
1032 | ++started; |
1033 | pFloatStr[nFloatStrUsed++] = (char)ch; |
1034 | if (__check_float_string(nFloatStrUsed, |
1035 | &nFloatStrSz, |
1036 | &pFloatStr, |
1037 | floatstring, |
1038 | &malloc_FloatStrFlag |
1039 | )==FALSE) { |
1040 | goto error_return; |
1041 | } |
1042 | ch = INC(); |
1043 | } |
1044 | |
1045 | #ifdef _UNICODE |
1046 | /* convert decimal point to wide-char */ |
1047 | /* if mbtowc fails (should never happen), we use L'.' */ |
1048 | decimal = L'.'; |
1049 | _MBTOWC(&decimal, _INTRN_LOCALE_CONV(_loc_update)->decimal_point, MB_CUR_MAX); |
1050 | #else /* _UNICODE */ |
1051 | |
1052 | decimal=*((_INTRN_LOCALE_CONV(_loc_update))->decimal_point); |
1053 | #endif /* _UNICODE */ |
1054 | |
1055 | /* now check for decimal */ |
1056 | if (decimal == (char)ch && width--) { |
1057 | ch = INC(); |
1058 | pFloatStr[nFloatStrUsed++] = decimal; |
1059 | if (__check_float_string(nFloatStrUsed, |
1060 | &nFloatStrSz, |
1061 | &pFloatStr, |
1062 | floatstring, |
1063 | &malloc_FloatStrFlag |
1064 | )==FALSE) { |
1065 | goto error_return; |
1066 | } |
1067 | |
1068 | while (_ISDIGIT(ch) && width--) { |
1069 | ++started; |
1070 | pFloatStr[nFloatStrUsed++] = (_TCHAR)ch; |
1071 | if (__check_float_string(nFloatStrUsed, |
1072 | &nFloatStrSz, |
1073 | &pFloatStr, |
1074 | floatstring, |
1075 | &malloc_FloatStrFlag |
1076 | )==FALSE) { |
1077 | goto error_return; |
1078 | } |
1079 | ch = INC(); |
1080 | } |
1081 | } |
1082 | |
1083 | /* now check for exponent */ |
1084 | |
1085 | if (started && (_T('e') == ch || _T('E') == ch) && width--) { |
1086 | pFloatStr[nFloatStrUsed++] = _T('e'); |
1087 | if (__check_float_string(nFloatStrUsed, |
1088 | &nFloatStrSz, |
1089 | &pFloatStr, |
1090 | floatstring, |
1091 | &malloc_FloatStrFlag |
1092 | )==FALSE) { |
1093 | goto error_return; |
1094 | } |
1095 | |
1096 | if (_T('-') == (ch = INC())) { |
1097 | |
1098 | pFloatStr[nFloatStrUsed++] = _T('-'); |
1099 | if (__check_float_string(nFloatStrUsed, |
1100 | &nFloatStrSz, |
1101 | &pFloatStr, |
1102 | floatstring, |
1103 | &malloc_FloatStrFlag |
1104 | )==FALSE) { |
1105 | goto error_return; |
1106 | } |
1107 | goto f_incwidth2; |
1108 | |
1109 | } else if (_T('+') == ch) { |
1110 | f_incwidth2: |
1111 | if (!width--) |
1112 | ++width; |
1113 | else |
1114 | ch = INC(); |
1115 | } |
1116 | |
1117 | |
1118 | while (_ISDIGIT(ch) && width--) { |
1119 | ++started; |
1120 | pFloatStr[nFloatStrUsed++] = (_TCHAR)ch; |
1121 | if (__check_float_string(nFloatStrUsed, |
1122 | &nFloatStrSz, |
1123 | &pFloatStr, |
1124 | floatstring, |
1125 | &malloc_FloatStrFlag |
1126 | )==FALSE) { |
1127 | goto error_return; |
1128 | } |
1129 | ch = INC(); |
1130 | } |
1131 | |
1132 | } |
1133 | |
1134 | UN_INC(ch); |
1135 | |
1136 | if (started) |
1137 | if (!suppress) { |
1138 | ++count; |
1139 | pFloatStr[nFloatStrUsed]= _T('\0'); |
1140 | #ifdef _UNICODE |
1141 | _WFASSIGN( longone-1, pointer, pFloatStr, (char)decimal, _loc_update.GetLocaleT()); |
1142 | #else /* _UNICODE */ |
1143 | _FASSIGN( longone-1, pointer, pFloatStr, (char)decimal, _loc_update.GetLocaleT()); |
1144 | #endif /* _UNICODE */ |
1145 | } else /*NULL */; |
1146 | else |
1147 | goto error_return; |
1148 | |
1149 | break; |
1150 | |
1151 | |
1152 | default: /* either found '%' or something else */ |
1153 | |
1154 | if ((int)*format != (int)ch) { |
1155 | UN_INC(ch); |
1156 | #ifdef _SECURE_SCANF |
1157 | /* error_return ASSERT's if format_error is true */ |
1158 | format_error = TRUE; |
1159 | #endif /* _SECURE_SCANF */ |
1160 | goto error_return; |
1161 | } |
1162 | else |
1163 | match--; /* % found, compensate for inc below */ |
1164 | |
1165 | if (!suppress) |
1166 | va_copy(arglist, arglistsave); |
1167 | |
1168 | } /* SWITCH */ |
1169 | |
1170 | match++; /* matched a format field - set flag */ |
1171 | |
1172 | } /* WHILE (width) */ |
1173 | |
1174 | else { /* zero-width field in format string */ |
1175 | UN_INC(ch); /* check for input error */ |
1176 | goto error_return; |
1177 | } |
1178 | |
1179 | ++format; /* skip to next char */ |
1180 | |
1181 | } else /* ('%' != *format) */ |
1182 | { |
1183 | |
1184 | if ((int)*format++ != (int)(ch = INC())) |
1185 | { |
1186 | UN_INC(ch); |
1187 | goto error_return; |
1188 | } |
1189 | #if 0 // we are not supporting multibyte input strings |
1190 | #ifndef _UNICODE |
1191 | if (isleadbyte((unsigned char)ch)) |
1192 | { |
1193 | int ch2; |
1194 | if ((int)*format++ != (ch2=INC())) |
1195 | { |
1196 | UN_INC(ch2); |
1197 | UN_INC(ch); |
1198 | goto error_return; |
1199 | } |
1200 | |
1201 | --charcount; /* only count as one character read */ |
1202 | } |
1203 | #endif /* _UNICODE */ |
1204 | #endif |
1205 | } |
1206 | |
1207 | #ifndef CPRFLAG |
1208 | if ( (_TEOF == ch) && ((*format != _T('%')) || (*(format + 1) != _T('n'))) ) |
1209 | break; |
1210 | #endif /* CPRFLAG */ |
1211 | |
1212 | } /* WHILE (*format) */ |
1213 | |
1214 | error_return: |
1215 | #if ALLOC_TABLE |
1216 | if (malloc_flag == 1) |
1217 | { |
1218 | _free_crt(table); |
1219 | } |
1220 | #endif /* ALLOC_TABLE */ |
1221 | if (malloc_FloatStrFlag == 1) |
1222 | { |
1223 | _free_crt(pFloatStr); |
1224 | } |
1225 | |
1226 | #ifndef CPRFLAG |
1227 | if (_TEOF == ch) |
1228 | /* If any fields were matched or assigned, return count */ |
1229 | return ( (count || match) ? count : EOF); |
1230 | else |
1231 | #endif /* CPRFLAG */ |
1232 | #ifdef _SECURE_SCANF |
1233 | if(format_error == TRUE) { |
1234 | _VALIDATE_RETURN( ("Invalid Input Format" && 0), EINVAL, count); |
1235 | } |
1236 | #endif /* _SECURE_SCANF */ |
1237 | return count; |
1238 | |
1239 | } |
1240 | |
1241 | /* _hextodec() returns a value of 0-15 and expects a char 0-9, a-f, A-F */ |
1242 | /* _inc() is the one place where we put the actual getc code. */ |
1243 | /* _whiteout() returns the first non-blank character, as defined by isspace() */ |
1244 | |
1245 | static int __cdecl _hextodec ( _TCHAR chr) |
1246 | { |
1247 | return _ISDIGIT(chr) ? chr : (chr & ~(_T('a') - _T('A'))) - _T('A') + 10 + _T('0'); |
1248 | } |
1249 | |
1250 | #ifdef CPRFLAG |
1251 | |
1252 | static int __cdecl _inc(void) |
1253 | { |
1254 | return (_gettche_nolock()); |
1255 | } |
1256 | |
1257 | static void __cdecl _un_inc(int chr) |
1258 | { |
1259 | if (_TEOF != chr) { |
1260 | _ungettch_nolock(chr); |
1261 | } |
1262 | } |
1263 | |
1264 | static int __cdecl _whiteout(REG1 int* counter) |
1265 | { |
1266 | REG2 int ch; |
1267 | |
1268 | do |
1269 | { |
1270 | ++*counter; |
1271 | ch = _inc(); |
1272 | |
1273 | if (ch == _TEOF) |
1274 | { |
1275 | break; |
1276 | } |
1277 | } |
1278 | while(_istspace((_TUCHAR)ch)); |
1279 | return ch; |
1280 | } |
1281 | |
1282 | #else /* CPRFLAG */ |
1283 | |
1284 | static int __cdecl _inc(miniFILE* fileptr) |
1285 | { |
1286 | return (_gettc_nolock(fileptr)); |
1287 | } |
1288 | |
1289 | static void __cdecl _un_inc(int chr, miniFILE* fileptr) |
1290 | { |
1291 | if (_TEOF != chr) { |
1292 | _ungettc_nolock(chr,fileptr); |
1293 | } |
1294 | } |
1295 | |
1296 | static int __cdecl _whiteout(int* counter, miniFILE* fileptr) |
1297 | { |
1298 | int ch; |
1299 | |
1300 | do |
1301 | { |
1302 | ++*counter; |
1303 | ch = _inc(fileptr); |
1304 | |
1305 | if (ch == _TEOF) |
1306 | { |
1307 | break; |
1308 | } |
1309 | } |
1310 | while(_istspace((_TUCHAR)ch)); |
1311 | return ch; |
1312 | } |
1313 | |
1314 | #endif /* CPRFLAG */ |
1315 | |