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
9Module Name:
10
11 printf.c
12
13Abstract:
14
15 Implementation of the printf family functions.
16
17Revision 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
35SET_DEFAULT_DEBUG_CHANNEL(CRT);
36
37#if SSCANF_SUPPORT_ll
38const static char *scanf_longlongfmt = "ll";
39#else
40const static char *scanf_longlongfmt = "q";
41#endif
42
43#if SSCANF_CANNOT_HANDLE_MISSING_EXPONENT
44static int SscanfFloatCheckExponent(LPCSTR buff, LPCSTR floatFmt,
45 void * voidPtr, int * pn);
46#endif // SSCANF_CANNOT_HANDLE_MISSING_EXPONENT
47
48/*******************************************************************************
49Function:
50 PAL_printf_arg_remover
51
52Parameters:
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*******************************************************************************/
64void 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/*++
96Function:
97 PAL_printf
98
99See MSDN doc.
100--*/
101int
102__cdecl
103PAL_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/*++
123Function:
124 PAL_fprintf
125
126See MSDN doc.
127--*/
128int
129__cdecl
130PAL_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/*++
148Function:
149 PAL_wprintf
150
151See MSDN doc.
152--*/
153int
154__cdecl
155PAL_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/*++
177Function:
178 PAL_vprintf
179
180See MSDN doc.
181--*/
182int
183__cdecl
184PAL_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/*++
202Function:
203 fwprintf
204
205See MSDN doc.
206--*/
207int
208__cdecl
209PAL_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/*******************************************************************************
230Function:
231 Internal_ScanfExtractFormatA
232
233Paramaters:
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
252Notes:
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
265static BOOL Internal_ScanfExtractFormatA(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/*******************************************************************************
581Function:
582 Internal_ScanfExtractFormatW
583
584 -- see Internal_ScanfExtractFormatA above
585*******************************************************************************/
586static BOOL Internal_ScanfExtractFormatW(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/*******************************************************************************
872Function:
873 PAL_vsscanf
874
875Parameters:
876 Buffer
877 - buffer to parse values from
878 Format
879 - format string
880 ap
881 - stdarg parameter list
882*******************************************************************************/
883int 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/*******************************************************************************
1052Function:
1053 PAL_wvsscanf
1054
1055 -- see PAL_vsscanf above
1056*******************************************************************************/
1057int 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/*++
1290Function:
1291 PAL_swscanf
1292
1293See MSDN doc.
1294--*/
1295int
1296__cdecl
1297PAL_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/*++
1320Function:
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
1345static 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