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 | |
7 | |
8 | |
9 | Module Name: |
10 | |
11 | printf.c |
12 | |
13 | Abstract: |
14 | |
15 | Implementation of the printf family functions. |
16 | |
17 | Revision History: |
18 | |
19 | |
20 | |
21 | --*/ |
22 | |
23 | #include "pal/palinternal.h" |
24 | #include "pal/dbgmsg.h" |
25 | #include "pal/cruntime.h" |
26 | #include "pal/thread.hpp" |
27 | #include "pal/threadsusp.hpp" |
28 | #include "pal/printfcpp.hpp" |
29 | |
30 | /* <stdarg.h> needs to be included after "palinternal.h" to avoid name |
31 | collision for va_start and va_end */ |
32 | #include <stdarg.h> |
33 | #include <errno.h> |
34 | |
35 | SET_DEFAULT_DEBUG_CHANNEL(CRT); |
36 | |
37 | #if SSCANF_SUPPORT_ll |
38 | const static char *scanf_longlongfmt = "ll" ; |
39 | #else |
40 | const static char *scanf_longlongfmt = "q" ; |
41 | #endif |
42 | |
43 | #if SSCANF_CANNOT_HANDLE_MISSING_EXPONENT |
44 | static int SscanfFloatCheckExponent(LPCSTR buff, LPCSTR floatFmt, |
45 | void * voidPtr, int * pn); |
46 | #endif // SSCANF_CANNOT_HANDLE_MISSING_EXPONENT |
47 | |
48 | /******************************************************************************* |
49 | Function: |
50 | PAL_printf_arg_remover |
51 | |
52 | Parameters: |
53 | ap |
54 | - pointer to the va_list from which to remove arguments |
55 | Width |
56 | - the width of the current format operation |
57 | Precision |
58 | - the precision of the current format option |
59 | Type |
60 | - the type of the argument for the current format option |
61 | Prefix |
62 | - the prefix for the current format option |
63 | *******************************************************************************/ |
64 | void PAL_printf_arg_remover(va_list *ap, INT Width, INT Precision, INT Type, INT Prefix) |
65 | { |
66 | /* remove arg and precision if needed */ |
67 | if (PRECISION_STAR == Precision || |
68 | PRECISION_INVALID == Precision) |
69 | { |
70 | (void)va_arg(*ap, int); |
71 | } |
72 | if (WIDTH_STAR == Width || |
73 | WIDTH_INVALID == Width) |
74 | { |
75 | (void)va_arg(*ap, int); |
76 | } |
77 | if (Type == PFF_TYPE_FLOAT) |
78 | { |
79 | (void)va_arg(*ap, double); |
80 | } |
81 | else if (Type == PFF_TYPE_INT && Prefix == PFF_PREFIX_LONGLONG) |
82 | { |
83 | (void)va_arg(*ap, INT64); |
84 | } |
85 | else if (Type == PFF_TYPE_INT || Type == PFF_TYPE_CHAR) |
86 | { |
87 | (void)va_arg(*ap, int); |
88 | } |
89 | else |
90 | { |
91 | (void)va_arg(*ap, void *); |
92 | } |
93 | } |
94 | |
95 | /*++ |
96 | Function: |
97 | PAL_printf |
98 | |
99 | See MSDN doc. |
100 | --*/ |
101 | int |
102 | __cdecl |
103 | PAL_printf( |
104 | const char *format, |
105 | ...) |
106 | { |
107 | LONG Length; |
108 | va_list ap; |
109 | |
110 | PERF_ENTRY(printf); |
111 | ENTRY("PAL_printf (format=%p (%s))\n" , format, format); |
112 | |
113 | va_start(ap, format); |
114 | Length = PAL_vprintf(format, ap); |
115 | va_end(ap); |
116 | |
117 | LOGEXIT("PAL_printf returns int %d\n" , Length); |
118 | PERF_EXIT(printf); |
119 | return Length; |
120 | } |
121 | |
122 | /*++ |
123 | Function: |
124 | PAL_fprintf |
125 | |
126 | See MSDN doc. |
127 | --*/ |
128 | int |
129 | __cdecl |
130 | PAL_fprintf(PAL_FILE *stream,const char *format,...) |
131 | { |
132 | LONG Length = 0; |
133 | va_list ap; |
134 | |
135 | PERF_ENTRY(fprintf); |
136 | ENTRY("PAL_fprintf(stream=%p,format=%p (%s))\n" ,stream, format, format); |
137 | |
138 | va_start(ap, format); |
139 | Length = PAL_vfprintf( stream, format, ap); |
140 | va_end(ap); |
141 | |
142 | LOGEXIT("PAL_fprintf returns int %d\n" , Length); |
143 | PERF_EXIT(fprintf); |
144 | return Length; |
145 | } |
146 | |
147 | /*++ |
148 | Function: |
149 | PAL_wprintf |
150 | |
151 | See MSDN doc. |
152 | --*/ |
153 | int |
154 | __cdecl |
155 | PAL_wprintf( |
156 | const wchar_16 *format, |
157 | ...) |
158 | { |
159 | LONG Length; |
160 | va_list ap; |
161 | |
162 | PERF_ENTRY(wprintf); |
163 | ENTRY("PAL_wprintf (format=%p (%S))\n" , format, format); |
164 | |
165 | va_start(ap, format); |
166 | Length = PAL_vfwprintf( PAL_get_stdout(PAL_get_caller), format, ap); |
167 | va_end(ap); |
168 | |
169 | LOGEXIT("PAL_wprintf returns int %d\n" , Length); |
170 | PERF_EXIT(wprintf); |
171 | return Length; |
172 | } |
173 | |
174 | |
175 | |
176 | /*++ |
177 | Function: |
178 | PAL_vprintf |
179 | |
180 | See MSDN doc. |
181 | --*/ |
182 | int |
183 | __cdecl |
184 | PAL_vprintf( |
185 | const char *format, |
186 | va_list ap) |
187 | { |
188 | LONG Length; |
189 | |
190 | PERF_ENTRY(vprintf); |
191 | ENTRY("PAL_vprintf (format=%p (%s))\n" , format, format); |
192 | |
193 | Length = PAL_vfprintf( PAL_get_stdout(PAL_get_caller), format, ap); |
194 | |
195 | LOGEXIT("PAL_vprintf returns int %d\n" , Length); |
196 | PERF_EXIT(vprintf); |
197 | return Length; |
198 | } |
199 | |
200 | |
201 | /*++ |
202 | Function: |
203 | fwprintf |
204 | |
205 | See MSDN doc. |
206 | --*/ |
207 | int |
208 | __cdecl |
209 | PAL_fwprintf( |
210 | PAL_FILE *stream, |
211 | const wchar_16 *format, |
212 | ...) |
213 | { |
214 | LONG Length; |
215 | va_list ap; |
216 | |
217 | PERF_ENTRY(fwprintf); |
218 | ENTRY("PAL_fwprintf (stream=%p, format=%p (%S))\n" , stream, format, format); |
219 | |
220 | va_start(ap, format); |
221 | Length = PAL_vfwprintf( stream, format, ap); |
222 | va_end(ap); |
223 | |
224 | LOGEXIT("PAL_fwprintf returns int %d\n" , Length); |
225 | PERF_EXIT(fwprintf); |
226 | return Length; |
227 | } |
228 | |
229 | /******************************************************************************* |
230 | Function: |
231 | Internal_ScanfExtractFormatA |
232 | |
233 | Paramaters: |
234 | Fmt |
235 | - format string to parse |
236 | - first character must be a '%' |
237 | - paramater gets updated to point to the character after |
238 | the %<foo> format string |
239 | Out |
240 | - buffer will contain the %<foo> format string |
241 | Store |
242 | - boolean value representing whether to store the type to be parsed |
243 | - '*' flag |
244 | Width |
245 | - will contain the width specified by the format string |
246 | - -1 if none given |
247 | Prefix |
248 | - an enumeration of the type prefix |
249 | Type |
250 | - an enumeration of the value type to be parsed |
251 | |
252 | Notes: |
253 | - I'm also handling the undocumented %ws, %wc, %w... |
254 | *******************************************************************************/ |
255 | |
256 | #define CHECK_OUT_IN_ITS_RANGE(Out,BeginOut,EndOut) \ |
257 | if ((Out)<(BeginOut) || (Out)>=(EndOut)) \ |
258 | { \ |
259 | SetLastError(ERROR_INSUFFICIENT_BUFFER); \ |
260 | ERROR("Pointer Out wanted to access 0x%p. However the range of buffer is [0x%p,0x%p).",\ |
261 | (Out), (BeginOut), (EndOut)); \ |
262 | return false; \ |
263 | } |
264 | |
265 | static BOOL (LPCSTR *Fmt, LPSTR Out, int iOutSize, LPBOOL Store, |
266 | LPINT Width, LPINT Prefix, LPINT Type) |
267 | { |
268 | BOOL Result = FALSE; |
269 | LPSTR TempStr; |
270 | LPSTR TempStrPtr; |
271 | LPSTR BaseOut = Out; |
272 | LPSTR EndOut = Out + iOutSize; |
273 | |
274 | *Width = -1; |
275 | *Store = TRUE; |
276 | *Prefix = -1; |
277 | *Type = -1; |
278 | |
279 | if (*Fmt && **Fmt == '%') |
280 | { |
281 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
282 | *Out++ = *(*Fmt)++; |
283 | } |
284 | else |
285 | { |
286 | return Result; |
287 | } |
288 | |
289 | /* we'll never need a temp string longer than the original */ |
290 | TempStrPtr = TempStr = (LPSTR) PAL_malloc(strlen(*Fmt)+1); |
291 | if (!TempStr) |
292 | { |
293 | ERROR("PAL_malloc failed\n" ); |
294 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
295 | return Result; |
296 | } |
297 | |
298 | /* parse '*' flag which means don't store */ |
299 | if (**Fmt == '*') |
300 | { |
301 | *Store = FALSE; |
302 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
303 | *Out++ = *(*Fmt)++; |
304 | } |
305 | |
306 | /* grab width specifier */ |
307 | if (isdigit((unsigned char) **Fmt)) |
308 | { |
309 | TempStrPtr = TempStr; |
310 | while (isdigit((unsigned char) **Fmt)) |
311 | { |
312 | *TempStrPtr++ = **Fmt; |
313 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
314 | *Out++ = *(*Fmt)++; |
315 | } |
316 | *TempStrPtr = 0; /* end string */ |
317 | *Width = atoi(TempStr); |
318 | if (*Width < 0) |
319 | { |
320 | ERROR("atoi returned a negative value indicative of an overflow.\n" ); |
321 | SetLastError(ERROR_INTERNAL_ERROR); |
322 | return Result; |
323 | } |
324 | } |
325 | |
326 | #ifdef BIT64 |
327 | if (**Fmt == 'p') |
328 | { |
329 | *Prefix = SCANF_PREFIX_LONGLONG; |
330 | } |
331 | #endif |
332 | /* grab prefix of 'I64' for __int64 */ |
333 | if ((*Fmt)[0] == 'I' && (*Fmt)[1] == '6' && (*Fmt)[2] == '4') |
334 | { |
335 | /* convert to 'q'/'ll' so Unix sscanf can handle it */ |
336 | *Fmt += 3; |
337 | *Prefix = SCANF_PREFIX_LONGLONG; |
338 | } |
339 | /* grab a prefix of 'h' */ |
340 | else if (**Fmt == 'h') |
341 | { |
342 | *Prefix = SCANF_PREFIX_SHORT; |
343 | ++(*Fmt); |
344 | } |
345 | /* grab prefix of 'l' or the undocumented 'w' (at least in MSDN) */ |
346 | else if (**Fmt == 'l' || **Fmt == 'w') |
347 | { |
348 | ++(*Fmt); |
349 | #ifdef BIT64 |
350 | // Only want to change the prefix on 64 bit when inputing characters. |
351 | if (**Fmt == 'c' || **Fmt == 's') |
352 | #endif |
353 | { |
354 | *Prefix = SCANF_PREFIX_LONG; /* give it a wide prefix */ |
355 | } |
356 | if (**Fmt == 'l') |
357 | { |
358 | *Prefix = SCANF_PREFIX_LONGLONG; |
359 | ++(*Fmt); |
360 | } |
361 | } |
362 | else if (**Fmt == 'L') |
363 | { |
364 | /* a prefix of 'L' seems to be ignored */ |
365 | ++(*Fmt); |
366 | } |
367 | |
368 | /* grab type 'c' */ |
369 | if (**Fmt == 'c' || **Fmt == 'C') |
370 | { |
371 | *Type = SCANF_TYPE_CHAR; |
372 | if (*Prefix != SCANF_PREFIX_SHORT && **Fmt == 'C') |
373 | { |
374 | *Prefix = SCANF_PREFIX_LONG; /* give it a wide prefix */ |
375 | } |
376 | if (*Prefix == SCANF_PREFIX_LONG) |
377 | { |
378 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
379 | *Out++ = 'l'; |
380 | } |
381 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
382 | *Out++ = 'c'; |
383 | ++(*Fmt); |
384 | Result = TRUE; |
385 | } |
386 | /* grab type 's' */ |
387 | else if (**Fmt == 's' || **Fmt == 'S') |
388 | { |
389 | *Type = SCANF_TYPE_STRING; |
390 | if (*Prefix != SCANF_PREFIX_SHORT && **Fmt == 'S') |
391 | { |
392 | *Prefix = SCANF_PREFIX_LONG; /* give it a wide prefix */ |
393 | } |
394 | if (*Prefix == SCANF_PREFIX_LONG) |
395 | { |
396 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
397 | *Out++ = 'l'; |
398 | } |
399 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
400 | *Out++ = 's'; |
401 | ++(*Fmt); |
402 | Result = TRUE; |
403 | } |
404 | /* grab int types */ |
405 | else if (**Fmt == 'd' || **Fmt == 'i' || **Fmt == 'o' || |
406 | **Fmt == 'u' || **Fmt == 'x' || **Fmt == 'X' || |
407 | **Fmt == 'p') |
408 | { |
409 | *Type = SCANF_TYPE_INT; |
410 | if (*Prefix == SCANF_PREFIX_SHORT) |
411 | { |
412 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
413 | *Out++ = 'h'; |
414 | } |
415 | else if (*Prefix == SCANF_PREFIX_LONG) |
416 | { |
417 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
418 | *Out++ = 'l'; |
419 | } |
420 | else if (*Prefix == SCANF_PREFIX_LONGLONG) |
421 | { |
422 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
423 | |
424 | if (strcpy_s(Out, iOutSize-(Out-BaseOut), scanf_longlongfmt) != SAFECRT_SUCCESS) |
425 | { |
426 | ERROR("strcpy_s failed\n" ); |
427 | SetLastError(ERROR_INSUFFICIENT_BUFFER); |
428 | return FALSE; |
429 | } |
430 | |
431 | Out += strlen(scanf_longlongfmt); |
432 | } |
433 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
434 | *Out++ = *(*Fmt)++; |
435 | Result = TRUE; |
436 | } |
437 | else if (**Fmt == 'e' || **Fmt == 'E' || **Fmt == 'f' || |
438 | **Fmt == 'g' || **Fmt == 'G') |
439 | { |
440 | /* we can safely ignore the prefixes and only add the type*/ |
441 | *Type = SCANF_TYPE_FLOAT; |
442 | /* this gets rid of %E/%G since they're they're the |
443 | same when scanning */ |
444 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
445 | *Out++ = tolower( *(*Fmt)++ ); |
446 | Result = TRUE; |
447 | } |
448 | else if (**Fmt == 'n') |
449 | { |
450 | if (*Prefix == SCANF_PREFIX_SHORT) |
451 | { |
452 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
453 | *Out++ = 'h'; |
454 | } |
455 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
456 | *Out++ = *(*Fmt)++; |
457 | *Type = SCANF_TYPE_N; |
458 | Result = TRUE; |
459 | } |
460 | else if (**Fmt == '[') |
461 | { |
462 | /* There is a small compatibility problem in the handling of the [] |
463 | option in FreeBSD vs. Windows. In Windows, you can have [z-a] |
464 | as well as [a-z]. In FreeBSD, [z-a] fails. So, we need to |
465 | reverse the instances of z-a to a-z (and [m-e] to [e-m], etc). */ |
466 | |
467 | /* step 1 : copy the leading [ */ |
468 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
469 | *Out++ = '['; |
470 | (*Fmt)++; |
471 | |
472 | /* step 2 : copy a leading ^, if present */ |
473 | if( '^' == **Fmt ) |
474 | { |
475 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
476 | *Out++ = '^'; |
477 | (*Fmt)++; |
478 | } |
479 | |
480 | /* step 3 : copy a leading ], if present; a ] immediately after the |
481 | leading [ (or [^) does *not* end the sequence, it is part of the |
482 | characters to match */ |
483 | if( ']' == **Fmt ) |
484 | { |
485 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
486 | *Out++ = ']'; |
487 | (*Fmt)++; |
488 | } |
489 | |
490 | /* step 4 : if the next character is already a '-', it's not part of an |
491 | interval specifier, so just copy it */ |
492 | if('-' == **Fmt ) |
493 | { |
494 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
495 | *Out++ = '-'; |
496 | (*Fmt)++; |
497 | } |
498 | |
499 | /* ok then, process the rest of it */ |
500 | while( '\0' != **Fmt ) |
501 | { |
502 | if(']' == **Fmt) |
503 | { |
504 | /* ']' marks end of the format specifier; we're done */ |
505 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
506 | *Out++ = ']'; |
507 | (*Fmt)++; |
508 | break; |
509 | } |
510 | if('-' == **Fmt) |
511 | { |
512 | if( ']' == (*Fmt)[1] ) |
513 | { |
514 | /* got a '-', next character is the terminating ']'; |
515 | copy '-' literally */ |
516 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
517 | *Out++ = '-'; |
518 | (*Fmt)++; |
519 | } |
520 | else |
521 | { |
522 | /* got a '-' indicating an interval specifier */ |
523 | unsigned char prev, next; |
524 | |
525 | /* get the interval boundaries */ |
526 | prev = (*Fmt)[-1]; |
527 | next = (*Fmt)[1]; |
528 | |
529 | /* if boundaries were inverted, replace the already-copied |
530 | low boundary by the 'real' low boundary */ |
531 | if( prev > next ) |
532 | { |
533 | CHECK_OUT_IN_ITS_RANGE(Out-1,BaseOut,EndOut) |
534 | Out[-1] = next; |
535 | |
536 | /* ...and save the 'real' upper boundary, which will be |
537 | copied to 'Out' below */ |
538 | next = prev; |
539 | } |
540 | |
541 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
542 | *Out++ = '-'; |
543 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
544 | *Out++ = next; |
545 | |
546 | /* skip over the '-' and the next character, which we |
547 | already copied */ |
548 | (*Fmt)+=2; |
549 | } |
550 | } |
551 | else |
552 | { |
553 | /* plain character; just copy it */ |
554 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
555 | *Out++ = **Fmt; |
556 | (*Fmt)++; |
557 | } |
558 | } |
559 | |
560 | *Type = SCANF_TYPE_BRACKETS; |
561 | Result = TRUE; |
562 | } |
563 | else if (**Fmt == ' ') |
564 | { |
565 | *Type = SCANF_TYPE_SPACE; |
566 | } |
567 | |
568 | /* add %n so we know how far to increment the pointer */ |
569 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
570 | *Out++ = '%'; |
571 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
572 | *Out++ = 'n'; |
573 | |
574 | CHECK_OUT_IN_ITS_RANGE(Out,BaseOut,EndOut) |
575 | *Out = 0; /* end the string */ |
576 | PAL_free(TempStr); |
577 | return Result; |
578 | } |
579 | |
580 | /******************************************************************************* |
581 | Function: |
582 | Internal_ScanfExtractFormatW |
583 | |
584 | -- see Internal_ScanfExtractFormatA above |
585 | *******************************************************************************/ |
586 | static BOOL (LPCWSTR *Fmt, LPSTR Out, int iOutSize, LPBOOL Store, |
587 | LPINT Width, LPINT Prefix, LPINT Type) |
588 | { |
589 | BOOL Result = FALSE; |
590 | LPSTR TempStr; |
591 | LPSTR TempStrPtr; |
592 | |
593 | *Width = -1; |
594 | *Store = TRUE; |
595 | *Prefix = -1; |
596 | *Type = -1; |
597 | |
598 | if (*Fmt && **Fmt == '%') |
599 | { |
600 | *Out++ = *(*Fmt)++; |
601 | } |
602 | else |
603 | { |
604 | return Result; |
605 | } |
606 | |
607 | /* we'll never need a temp string longer than the original */ |
608 | TempStrPtr = TempStr = (LPSTR) PAL_malloc(PAL_wcslen(*Fmt)+1); |
609 | if (!TempStr) |
610 | { |
611 | ERROR("PAL_malloc failed\n" ); |
612 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
613 | return Result; |
614 | } |
615 | |
616 | /* parse '*' flag which means don't store */ |
617 | if (**Fmt == '*') |
618 | { |
619 | *Store = FALSE; |
620 | *Out++ = *(*Fmt)++; |
621 | } |
622 | |
623 | /* grab width specifier */ |
624 | if (isdigit(**Fmt)) |
625 | { |
626 | TempStrPtr = TempStr; |
627 | while (isdigit(**Fmt)) |
628 | { |
629 | *TempStrPtr++ = **Fmt; |
630 | *Out++ = *(*Fmt)++; |
631 | } |
632 | *TempStrPtr = 0; /* end string */ |
633 | *Width = atoi(TempStr); |
634 | if (*Width < 0) |
635 | { |
636 | ERROR("atoi returned a negative value indicative of an overflow.\n" ); |
637 | SetLastError(ERROR_INTERNAL_ERROR); |
638 | return Result; |
639 | } |
640 | } |
641 | |
642 | #ifdef BIT64 |
643 | if (**Fmt == 'p') |
644 | { |
645 | *Prefix = SCANF_PREFIX_LONGLONG; |
646 | } |
647 | #endif |
648 | /* grab prefix of 'I64' for __int64 */ |
649 | if ((*Fmt)[0] == 'I' && (*Fmt)[1] == '6' && (*Fmt)[2] == '4') |
650 | { |
651 | /* convert to 'q'/'ll' so that Unix sscanf can handle it */ |
652 | *Fmt += 3; |
653 | *Prefix = SCANF_PREFIX_LONGLONG; |
654 | } |
655 | /* grab a prefix of 'h' */ |
656 | else if (**Fmt == 'h') |
657 | { |
658 | *Prefix = SCANF_PREFIX_SHORT; |
659 | ++(*Fmt); |
660 | } |
661 | /* grab prefix of 'l' or the undocumented 'w' (at least in MSDN) */ |
662 | else if (**Fmt == 'l' || **Fmt == 'w') |
663 | { |
664 | ++(*Fmt); |
665 | #ifdef BIT64 |
666 | // Only want to change the prefix on 64 bit when inputing characters. |
667 | if (**Fmt == 'C' || **Fmt == 'S') |
668 | #endif |
669 | { |
670 | *Prefix = SCANF_PREFIX_LONG; /* give it a wide prefix */ |
671 | } |
672 | if (**Fmt == 'l') |
673 | { |
674 | *Prefix = SCANF_PREFIX_LONGLONG; |
675 | ++(*Fmt); |
676 | } |
677 | } |
678 | else if (**Fmt == 'L') |
679 | { |
680 | /* a prefix of 'L' seems to be ignored */ |
681 | ++(*Fmt); |
682 | } |
683 | |
684 | /* grab type 'c' */ |
685 | if (**Fmt == 'c' || **Fmt == 'C') |
686 | { |
687 | *Type = SCANF_TYPE_CHAR; |
688 | if (*Prefix != SCANF_PREFIX_SHORT && **Fmt == 'c') |
689 | { |
690 | *Prefix = SCANF_PREFIX_LONG; /* give it a wide prefix */ |
691 | } |
692 | if (*Prefix == SCANF_PREFIX_LONG) |
693 | { |
694 | *Out++ = 'l'; |
695 | } |
696 | *Out++ = 'c'; |
697 | ++(*Fmt); |
698 | Result = TRUE; |
699 | } |
700 | /* grab type 's' */ |
701 | else if (**Fmt == 's' || **Fmt == 'S') |
702 | { |
703 | *Type = SCANF_TYPE_STRING; |
704 | if (*Prefix != SCANF_PREFIX_SHORT && **Fmt == 's') |
705 | { |
706 | *Prefix = SCANF_PREFIX_LONG; /* give it a wide prefix */ |
707 | } |
708 | if (*Prefix == SCANF_PREFIX_LONG) |
709 | { |
710 | *Out++ = 'l'; |
711 | } |
712 | *Out++ = 's'; |
713 | ++(*Fmt); |
714 | Result = TRUE; |
715 | } |
716 | /* grab int types */ |
717 | else if (**Fmt == 'd' || **Fmt == 'i' || **Fmt == 'o' || |
718 | **Fmt == 'u' || **Fmt == 'x' || **Fmt == 'X' || |
719 | **Fmt == 'p') |
720 | { |
721 | *Type = SCANF_TYPE_INT; |
722 | if (*Prefix == SCANF_PREFIX_SHORT) |
723 | { |
724 | *Out++ = 'h'; |
725 | } |
726 | else if (*Prefix == SCANF_PREFIX_LONG) |
727 | { |
728 | *Out++ = 'l'; |
729 | } |
730 | else if (*Prefix == SCANF_PREFIX_LONGLONG) |
731 | { |
732 | if (strcpy_s(Out, iOutSize, scanf_longlongfmt) != SAFECRT_SUCCESS) |
733 | { |
734 | ERROR("strcpy_s failed\n" ); |
735 | SetLastError(ERROR_INSUFFICIENT_BUFFER); |
736 | return FALSE; |
737 | } |
738 | |
739 | Out += strlen(scanf_longlongfmt); |
740 | } |
741 | *Out++ = *(*Fmt)++; |
742 | Result = TRUE; |
743 | } |
744 | else if (**Fmt == 'e' || **Fmt == 'E' || **Fmt == 'f' || |
745 | **Fmt == 'g' || **Fmt == 'G') |
746 | { |
747 | /* we can safely ignore the prefixes and only add the type*/ |
748 | *Type = SCANF_TYPE_FLOAT; |
749 | /* this gets rid of %E/%G since they're they're the |
750 | same when scanning */ |
751 | *Out++ = tolower( *(*Fmt)++ ); |
752 | Result = TRUE; |
753 | } |
754 | else if (**Fmt == 'n') |
755 | { |
756 | if (*Prefix == SCANF_PREFIX_SHORT) |
757 | { |
758 | *Out++ = 'h'; |
759 | } |
760 | *Out++ = *(*Fmt)++; |
761 | *Type = SCANF_TYPE_N; |
762 | Result = TRUE; |
763 | } |
764 | else if (**Fmt == '[') |
765 | { |
766 | /* There is a small compatibility problem in the handling of the [] |
767 | option in FreeBSD vs. Windows. In Windows, you can have [z-a] |
768 | as well as [a-z]. In FreeBSD, [z-a] fails. So, we need to |
769 | reverse the instances of z-a to a-z (and [m-e] to [e-m], etc). */ |
770 | |
771 | /* step 1 : copy the leading [ */ |
772 | *Out++ = '['; |
773 | (*Fmt)++; |
774 | |
775 | /* step 2 : copy a leading ^, if present */ |
776 | if( '^' == **Fmt ) |
777 | { |
778 | *Out++ = '^'; |
779 | (*Fmt)++; |
780 | } |
781 | |
782 | /* step 3 : copy a leading ], if present; a ] immediately after the |
783 | leading [ (or [^) does *not* end the sequence, it is part of the |
784 | characters to match */ |
785 | if( ']' == **Fmt ) |
786 | { |
787 | *Out++ = ']'; |
788 | (*Fmt)++; |
789 | } |
790 | |
791 | /* step 4 : if the next character is already a '-', it's not part of an |
792 | interval specifier, so just copy it */ |
793 | if('-' == **Fmt ) |
794 | { |
795 | *Out++ = '-'; |
796 | (*Fmt)++; |
797 | } |
798 | |
799 | /* ok then, process the rest of it */ |
800 | while( '\0' != **Fmt ) |
801 | { |
802 | if(']' == **Fmt) |
803 | { |
804 | /* ']' marks end of the format specifier; we're done */ |
805 | *Out++ = ']'; |
806 | (*Fmt)++; |
807 | break; |
808 | } |
809 | if('-' == **Fmt) |
810 | { |
811 | if( ']' == (*Fmt)[1] ) |
812 | { |
813 | /* got a '-', next character is the terminating ']'; |
814 | copy '-' literally */ |
815 | *Out++ = '-'; |
816 | (*Fmt)++; |
817 | } |
818 | else |
819 | { |
820 | /* got a '-' indicating an interval specifier */ |
821 | unsigned char prev, next; |
822 | |
823 | /* get the interval boundaries */ |
824 | prev = (*Fmt)[-1]; |
825 | next = (*Fmt)[1]; |
826 | |
827 | /* if boundaries were inverted, replace the already-copied |
828 | low boundary by the 'real' low boundary */ |
829 | if( prev > next ) |
830 | { |
831 | Out[-1] = next; |
832 | |
833 | /* ...and save the 'real' upper boundary, which will be |
834 | copied to 'Out' below */ |
835 | next = prev; |
836 | } |
837 | |
838 | *Out++ = '-'; |
839 | *Out++ = next; |
840 | |
841 | /* skip over the '-' and the next character, which we |
842 | already copied */ |
843 | (*Fmt)+=2; |
844 | } |
845 | } |
846 | else |
847 | { |
848 | /* plain character; just copy it */ |
849 | *Out++ = **Fmt; |
850 | (*Fmt)++; |
851 | } |
852 | } |
853 | |
854 | *Type = SCANF_TYPE_BRACKETS; |
855 | Result = TRUE; |
856 | } |
857 | else if (**Fmt == ' ') |
858 | { |
859 | *Type = SCANF_TYPE_SPACE; |
860 | } |
861 | |
862 | /* add %n so we know how far to increment the pointer */ |
863 | *Out++ = '%'; |
864 | *Out++ = 'n'; |
865 | |
866 | *Out = 0; /* end the string */ |
867 | PAL_free(TempStr); |
868 | return Result; |
869 | } |
870 | |
871 | /******************************************************************************* |
872 | Function: |
873 | PAL_vsscanf |
874 | |
875 | Parameters: |
876 | Buffer |
877 | - buffer to parse values from |
878 | Format |
879 | - format string |
880 | ap |
881 | - stdarg parameter list |
882 | *******************************************************************************/ |
883 | int PAL_vsscanf(LPCSTR Buffer, LPCSTR Format, va_list ap) |
884 | { |
885 | INT Length = 0; |
886 | LPCSTR Buff = Buffer; |
887 | LPCSTR Fmt = Format; |
888 | CHAR TempBuff[1024]; /* used to hold a single %<foo> format string */ |
889 | BOOL Store; |
890 | INT Width; |
891 | INT Prefix; |
892 | INT Type = -1; |
893 | |
894 | while (*Fmt) |
895 | { |
896 | if (!*Buff && Length == 0) |
897 | { |
898 | Length = EOF; |
899 | break; |
900 | } |
901 | /* remove any number of blanks */ |
902 | else if (isspace((unsigned char) *Fmt)) |
903 | { |
904 | while (isspace((unsigned char) *Buff)) |
905 | { |
906 | ++Buff; |
907 | } |
908 | ++Fmt; |
909 | } |
910 | else if (*Fmt == '%' && |
911 | Internal_ScanfExtractFormatA(&Fmt, TempBuff, sizeof(TempBuff), &Store, |
912 | &Width, &Prefix, &Type)) |
913 | { |
914 | if (Prefix == SCANF_PREFIX_LONG && |
915 | (Type == SCANF_TYPE_STRING || Type == SCANF_TYPE_CHAR)) |
916 | { |
917 | int len = 0; |
918 | int res; |
919 | WCHAR *charPtr = 0; |
920 | |
921 | /* a single character */ |
922 | if (Type == SCANF_TYPE_CHAR && Width == -1) |
923 | { |
924 | len = Width = 1; |
925 | } |
926 | |
927 | /* calculate length of string to copy */ |
928 | while (Buff[len] && !isspace((unsigned char) Buff[len])) |
929 | { |
930 | if (Width != -1 && len >= Width) |
931 | { |
932 | break; |
933 | } |
934 | ++len; |
935 | } |
936 | |
937 | if (Store) |
938 | { |
939 | charPtr = va_arg(ap, WCHAR *); |
940 | |
941 | res = MultiByteToWideChar(CP_ACP, 0, Buff, len, |
942 | charPtr, len); |
943 | if (!res) |
944 | { |
945 | ASSERT("MultiByteToWideChar failed. Error is %d\n" , |
946 | GetLastError()); |
947 | return -1; |
948 | } |
949 | if (Type == SCANF_TYPE_STRING) |
950 | { |
951 | /* end string */ |
952 | charPtr[res] = 0; |
953 | } |
954 | ++Length; |
955 | } |
956 | Buff += len; |
957 | } |
958 | /* this places the number of bytes stored into the next arg */ |
959 | else if (Type == SCANF_TYPE_N) |
960 | { |
961 | if (Prefix == SCANF_PREFIX_SHORT) |
962 | { |
963 | *(va_arg(ap, short *)) = Buff - Buffer; |
964 | } |
965 | else |
966 | { |
967 | *(va_arg(ap, LPLONG)) = Buff - Buffer; |
968 | } |
969 | } |
970 | /* types that sscanf can handle */ |
971 | else |
972 | { |
973 | int ret; |
974 | int n; |
975 | LPVOID voidPtr = NULL; |
976 | |
977 | if (Store) |
978 | { |
979 | // sscanf_s requires that if we are trying to read "%s" or "%c" or “%[“, then |
980 | // the size of the buffer must follow the buffer we are trying to read into. |
981 | voidPtr = va_arg(ap, LPVOID); |
982 | unsigned typeLen = 0; |
983 | if ((Type == SCANF_TYPE_STRING) || (Type == SCANF_TYPE_BRACKETS)) |
984 | { |
985 | // Since this is not a Safe CRT API we don’t really know the size of the destination |
986 | // buffer provided by the caller. So we have to assume that the caller has allocated |
987 | // enough space to hold either the width specified in the format or the entire input |
988 | // string plus ‘\0’. |
989 | typeLen = ((Width > 0) ? Width : strlen(Buffer)) + 1; |
990 | } |
991 | else if (Type == SCANF_TYPE_CHAR) |
992 | { |
993 | // Check whether the format string contains number of characters |
994 | // that should be read from the input string. |
995 | // Note: ‘\0’ does not get appended in the “%c” case. |
996 | typeLen = (Width > 0) ? Width : 1; |
997 | } |
998 | |
999 | if (typeLen > 0) |
1000 | { |
1001 | ret = sscanf_s(Buff, TempBuff, voidPtr, typeLen, &n); |
1002 | } |
1003 | else |
1004 | { |
1005 | ret = sscanf_s(Buff, TempBuff, voidPtr, &n); |
1006 | } |
1007 | } |
1008 | else |
1009 | { |
1010 | ret = sscanf_s(Buff, TempBuff, &n); |
1011 | } |
1012 | |
1013 | #if SSCANF_CANNOT_HANDLE_MISSING_EXPONENT |
1014 | if ((ret == 0) && (Type == SCANF_TYPE_FLOAT)) |
1015 | { |
1016 | ret = SscanfFloatCheckExponent(Buff, TempBuff, voidPtr, &n); |
1017 | } |
1018 | #endif // SSCANF_CANNOT_HANDLE_MISSING_EXPONENT |
1019 | |
1020 | if (ret > 0) |
1021 | { |
1022 | Length += ret; |
1023 | } |
1024 | else |
1025 | { |
1026 | /* no match, break scan */ |
1027 | break; |
1028 | } |
1029 | Buff += n; |
1030 | } |
1031 | } |
1032 | else |
1033 | { |
1034 | /* grab, but not store */ |
1035 | if (*Fmt == *Buff && Type != SCANF_TYPE_SPACE) |
1036 | { |
1037 | ++Fmt; |
1038 | ++Buff; |
1039 | } |
1040 | /* doesn't match, break scan */ |
1041 | else |
1042 | { |
1043 | break; |
1044 | } |
1045 | } |
1046 | } |
1047 | |
1048 | return Length; |
1049 | } |
1050 | |
1051 | /******************************************************************************* |
1052 | Function: |
1053 | PAL_wvsscanf |
1054 | |
1055 | -- see PAL_vsscanf above |
1056 | *******************************************************************************/ |
1057 | int PAL_wvsscanf(LPCWSTR Buffer, LPCWSTR Format, va_list ap) |
1058 | { |
1059 | INT Length = 0; |
1060 | LPCWSTR Buff = Buffer; |
1061 | LPCWSTR Fmt = Format; |
1062 | CHAR TempBuff[1024]; /* used to hold a single %<foo> format string */ |
1063 | BOOL Store; |
1064 | INT Width; |
1065 | INT Prefix; |
1066 | INT Type = -1; |
1067 | |
1068 | while (*Fmt) |
1069 | { |
1070 | if (!*Buff && Length == 0) |
1071 | { |
1072 | Length = EOF; |
1073 | break; |
1074 | } |
1075 | /* remove any number of blanks */ |
1076 | else if (isspace(*Fmt)) |
1077 | { |
1078 | while (isspace(*Buff)) |
1079 | { |
1080 | ++Buff; |
1081 | } |
1082 | ++Fmt; |
1083 | } |
1084 | else if (*Fmt == '%' && |
1085 | Internal_ScanfExtractFormatW(&Fmt, TempBuff, sizeof(TempBuff), &Store, |
1086 | &Width, &Prefix, &Type)) |
1087 | { |
1088 | if (Prefix == SCANF_PREFIX_LONG && |
1089 | (Type == SCANF_TYPE_STRING || Type == SCANF_TYPE_CHAR)) |
1090 | { |
1091 | int len = 0; |
1092 | WCHAR *charPtr = 0; |
1093 | |
1094 | /* a single character */ |
1095 | if (Type == SCANF_TYPE_CHAR && Width == -1) |
1096 | { |
1097 | len = Width = 1; |
1098 | } |
1099 | |
1100 | /* calculate length of string to copy */ |
1101 | while (Buff[len] && !isspace(Buff[len])) |
1102 | { |
1103 | if (Width != -1 && len >= Width) |
1104 | { |
1105 | break; |
1106 | } |
1107 | ++len; |
1108 | } |
1109 | |
1110 | if (Store) |
1111 | { |
1112 | int i; |
1113 | charPtr = va_arg(ap, WCHAR *); |
1114 | |
1115 | for (i = 0; i < len; i++) |
1116 | { |
1117 | charPtr[i] = Buff[i]; |
1118 | } |
1119 | if (Type == SCANF_TYPE_STRING) |
1120 | { |
1121 | /* end string */ |
1122 | charPtr[len] = 0; |
1123 | } |
1124 | ++Length; |
1125 | } |
1126 | Buff += len; |
1127 | } |
1128 | /* this places the number of bytes stored into the next arg */ |
1129 | else if (Type == SCANF_TYPE_N) |
1130 | { |
1131 | if (Prefix == SCANF_PREFIX_SHORT) |
1132 | { |
1133 | *(va_arg(ap, short *)) = Buff - Buffer; |
1134 | } |
1135 | else |
1136 | { |
1137 | *(va_arg(ap, LPLONG)) = Buff - Buffer; |
1138 | } |
1139 | } |
1140 | /* types that sscanf can handle */ |
1141 | else |
1142 | { |
1143 | int ret; |
1144 | int n; |
1145 | int size; |
1146 | LPSTR newBuff = 0; |
1147 | LPVOID voidPtr = NULL; |
1148 | |
1149 | size = WideCharToMultiByte(CP_ACP, 0, Buff, -1, 0, 0, 0, 0); |
1150 | if (!size) |
1151 | { |
1152 | ASSERT("WideCharToMultiByte failed. Error is %d\n" , |
1153 | GetLastError()); |
1154 | return -1; |
1155 | } |
1156 | newBuff = (LPSTR) PAL_malloc(size); |
1157 | if (!newBuff) |
1158 | { |
1159 | ERROR("PAL_malloc failed\n" ); |
1160 | SetLastError(ERROR_NOT_ENOUGH_MEMORY); |
1161 | return -1; |
1162 | } |
1163 | size = WideCharToMultiByte(CP_ACP, 0, Buff, size, |
1164 | newBuff, size, 0, 0); |
1165 | if (!size) |
1166 | { |
1167 | ASSERT("WideCharToMultiByte failed. Error is %d\n" , |
1168 | GetLastError()); |
1169 | PAL_free(newBuff); |
1170 | return -1; |
1171 | } |
1172 | |
1173 | if (Store) |
1174 | { |
1175 | if (Type == SCANF_TYPE_BRACKETS) |
1176 | { |
1177 | WCHAR *strPtr; |
1178 | int i; |
1179 | |
1180 | /* add a '*' to %[] --> %*[] */ |
1181 | i = strlen(TempBuff) + 1; |
1182 | while (i) |
1183 | { |
1184 | /* shift everything right one */ |
1185 | TempBuff[i] = TempBuff[i - 1]; |
1186 | --i; |
1187 | } |
1188 | TempBuff[0] = '%'; |
1189 | TempBuff[1] = '*'; |
1190 | |
1191 | /* %n doesn't count as a conversion. Since we're |
1192 | suppressing conversion of the %[], sscanf will |
1193 | always return 0, so we can't use the return value |
1194 | to determine success. Set n to 0 before the call; if |
1195 | it's still 0 afterwards, we know the call failed */ |
1196 | n = 0; |
1197 | sscanf_s(newBuff, TempBuff, &n); |
1198 | if(0 == n) |
1199 | { |
1200 | /* sscanf failed, nothing matched. set ret to 0, |
1201 | so we know we have to break */ |
1202 | ret = 0; |
1203 | } |
1204 | else |
1205 | { |
1206 | strPtr = va_arg(ap, WCHAR *); |
1207 | for (i = 0; i < n; i++) |
1208 | { |
1209 | strPtr[i] = Buff[i]; |
1210 | } |
1211 | strPtr[n] = 0; /* end string */ |
1212 | ret = 1; |
1213 | } |
1214 | } |
1215 | else |
1216 | { |
1217 | voidPtr = va_arg(ap, LPVOID); |
1218 | // sscanf_s requires that if we are trying to read "%s" or "%c", then |
1219 | // the size of the buffer must follow the buffer we are trying to read into. |
1220 | unsigned typeLen = 0; |
1221 | if (Type == SCANF_TYPE_STRING) |
1222 | { |
1223 | // We don’t really know the size of the destination buffer provided by the |
1224 | // caller. So we have to assume that the caller has allocated enough space |
1225 | // to hold either the width specified in the format or the entire input |
1226 | // string plus ‘\0’. |
1227 | typeLen = ((Width > 0) ? Width : PAL_wcslen(Buffer)) + 1; |
1228 | } |
1229 | else if (Type == SCANF_TYPE_CHAR) |
1230 | { |
1231 | // Check whether the format string contains number of characters |
1232 | // that should be read from the input string. |
1233 | // Note: ‘\0’ does not get appended in the “%c” case. |
1234 | typeLen = (Width > 0) ? Width : 1; |
1235 | } |
1236 | |
1237 | if (typeLen > 0) |
1238 | { |
1239 | ret = sscanf_s(newBuff, TempBuff, voidPtr, typeLen, &n); |
1240 | } |
1241 | else |
1242 | ret = sscanf_s(newBuff, TempBuff, voidPtr, &n); |
1243 | } |
1244 | } |
1245 | else |
1246 | { |
1247 | ret = sscanf_s(newBuff, TempBuff, &n); |
1248 | } |
1249 | |
1250 | #if SSCANF_CANNOT_HANDLE_MISSING_EXPONENT |
1251 | if ((ret == 0) && (Type == SCANF_TYPE_FLOAT)) |
1252 | { |
1253 | ret = SscanfFloatCheckExponent(newBuff, TempBuff, voidPtr, &n); |
1254 | } |
1255 | #endif // SSCANF_CANNOT_HANDLE_MISSING_EXPONENT |
1256 | |
1257 | PAL_free(newBuff); |
1258 | if (ret > 0) |
1259 | { |
1260 | Length += ret; |
1261 | } |
1262 | else |
1263 | { |
1264 | /* no match; break scan */ |
1265 | break; |
1266 | } |
1267 | Buff += n; |
1268 | } |
1269 | } |
1270 | else |
1271 | { |
1272 | /* grab, but not store */ |
1273 | if (*Fmt == *Buff && Type != SCANF_TYPE_SPACE) |
1274 | { |
1275 | ++Fmt; |
1276 | ++Buff; |
1277 | } |
1278 | /* doesn't match, break scan */ |
1279 | else |
1280 | { |
1281 | break; |
1282 | } |
1283 | } |
1284 | } |
1285 | |
1286 | return Length; |
1287 | } |
1288 | |
1289 | /*++ |
1290 | Function: |
1291 | PAL_swscanf |
1292 | |
1293 | See MSDN doc. |
1294 | --*/ |
1295 | int |
1296 | __cdecl |
1297 | PAL_swscanf( |
1298 | const wchar_16 *buffer, |
1299 | const wchar_16 *format, |
1300 | ...) |
1301 | { |
1302 | int Length; |
1303 | va_list ap; |
1304 | |
1305 | PERF_ENTRY(swscanf); |
1306 | ENTRY("PAL_swscanf (buffer=%p (%S), format=%p (%S))\n" , buffer, buffer, format, format); |
1307 | |
1308 | va_start(ap, format); |
1309 | Length = PAL_wvsscanf(buffer, format, ap); |
1310 | va_end(ap); |
1311 | |
1312 | LOGEXIT("PAL_swscanf returns int %d\n" , Length); |
1313 | PERF_EXIT(swscanf); |
1314 | return Length; |
1315 | } |
1316 | |
1317 | |
1318 | #if SSCANF_CANNOT_HANDLE_MISSING_EXPONENT |
1319 | /*++ |
1320 | Function: |
1321 | SscanfFloatCheckExponent |
1322 | |
1323 | Parameters: |
1324 | buff: pointer to the buffer to be parsed; the target float must be at |
1325 | the beginning of the buffer, except for any number of leading |
1326 | spaces |
1327 | floatFmt: must be "%e%n" (or "%f%n" or "%g%n") |
1328 | voidptr: optional pointer to output variable (which should be a float) |
1329 | pn: pointer to an int to receive the number of bytes parsed. |
1330 | |
1331 | Notes: |
1332 | On some platforms (specifically AIX) sscanf fails to parse a float from |
1333 | a string such as 12.34e (while it succeeds for e.g. 12.34a). Sscanf |
1334 | initially interprets the 'e' as the keyword for the beginning of a |
1335 | 10-exponent of a floating point in scientific notation (as in 12.34e5), |
1336 | but then it fails to parse the actual exponent. At this point sscanf should |
1337 | be able to fall back on the narrower pattern, and parse the floating point |
1338 | in common decimal notation (i.e. 12.34). However AIX's sscanf fails to do |
1339 | so and it does not parse any number. |
1340 | This function checks the given string for a such case and removes |
1341 | the 'e' before parsing the float. |
1342 | |
1343 | --*/ |
1344 | |
1345 | static int SscanfFloatCheckExponent(LPCSTR buff, LPCSTR floatFmt, |
1346 | void * voidPtr, int * pn) |
1347 | { |
1348 | int ret = 0; |
1349 | int digits = 0; |
1350 | int points = 0; |
1351 | LPCSTR pos = buff; |
1352 | |
1353 | /* skip initial spaces */ |
1354 | while (*pos && isspace(*pos)) |
1355 | pos++; |
1356 | |
1357 | /* go to the end of a float, if there is one */ |
1358 | while (*pos) |
1359 | { |
1360 | if (isdigit(*pos)) |
1361 | digits++; |
1362 | else if (*pos == '.') |
1363 | { |
1364 | if (++points > 1) |
1365 | break; |
1366 | } |
1367 | else |
1368 | break; |
1369 | |
1370 | pos++; |
1371 | } |
1372 | |
1373 | /* check if it is something like 12.34e and the trailing 'e' is not |
1374 | the suffix of a valid exponent of 10, such as 12.34e+5 */ |
1375 | if ( digits > 0 && *pos && tolower(*pos) == 'e' && |
1376 | !( *(pos+1) && |
1377 | ( isdigit(*(pos+1)) || |
1378 | ( (*(pos+1) == '+' || *(pos+1) == '-') && isdigit(*(pos+2)) ) |
1379 | ) |
1380 | ) |
1381 | ) |
1382 | { |
1383 | CHAR * pLocBuf = (CHAR *)PAL_malloc((pos-buff+1)*sizeof(CHAR)); |
1384 | if (pLocBuf) |
1385 | { |
1386 | memcpy(pLocBuf, buff, (pos-buff)*sizeof(CHAR)); |
1387 | pLocBuf[pos-buff] = 0; |
1388 | if (voidPtr) |
1389 | ret = sscanf_s(pLocBuf, floatFmt, voidPtr, pn); |
1390 | else |
1391 | ret = sscanf_s(pLocBuf, floatFmt, pn); |
1392 | PAL_free (pLocBuf); |
1393 | } |
1394 | } |
1395 | return ret; |
1396 | } |
1397 | #endif // SSCANF_CANNOT_HANDLE_MISSING_EXPONENT |
1398 | |