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 silent_printf.c
12
13Abstract:
14
15 Implementation of a silent version of PAL_vsprintf and PAL_vfprintf function.
16 (without any reference to TRACE/ERROR/... macros, needed by the tracing macros)
17
18Revision History:
19
20
21
22--*/
23
24
25#include "pal/palinternal.h"
26#include "pal/cruntime.h"
27#include "pal/locale.h"
28#include "pal/printfcpp.hpp"
29#include "pal/thread.hpp"
30
31/* clip strings (%s, %S) at this number of characters */
32#define MAX_STR_LEN 300
33
34static int Silent_WideCharToMultiByte(LPCWSTR lpWideCharStr, int cchWideChar,
35 LPSTR lpMultiByteStr, int cbMultiByte);
36static BOOL Silent_ExtractFormatA(LPCSTR *Fmt, LPSTR Out, LPINT Flags, LPINT Width,
37 LPINT Precision, LPINT Prefix, LPINT Type);
38static INT Silent_AddPaddingVfprintf(PAL_FILE *stream, LPSTR In, INT Padding,
39 INT Flags);
40
41static size_t Silent_PAL_wcslen(const wchar_16 *string);
42
43/*++
44Function:
45 PAL_vfprintf (silent version)
46
47 for more details, see PAL_vfprintf in printf.c
48--*/
49int Silent_PAL_vfprintf(PAL_FILE *stream, const char *format, va_list aparg)
50{
51 CHAR TempBuff[1024]; /* used to hold a single %<foo> format string */
52 LPCSTR Fmt = format;
53 LPWSTR TempWStr;
54 LPSTR TempStr;
55 WCHAR TempWChar;
56 INT Flags;
57 INT Width;
58 INT Precision;
59 INT Prefix;
60 INT Type;
61 INT Length;
62 INT TempInt;
63 int wctombResult;
64 int written = 0;
65 int paddingReturnValue;
66 va_list ap;
67
68 va_copy(ap, aparg);
69
70 while (*Fmt)
71 {
72 if (*Fmt == '%' &&
73 TRUE == Silent_ExtractFormatA(&Fmt, TempBuff, &Flags, &Width,
74 &Precision, &Prefix, &Type))
75 {
76 if (Prefix == PFF_PREFIX_LONG && Type == PFF_TYPE_STRING)
77 {
78 if (WIDTH_STAR == Width)
79 {
80 Width = va_arg(ap, INT);
81 }
82 else if (WIDTH_INVALID == Width)
83 {
84 /* both a '*' and a number, ignore, but remove arg */
85 TempInt = va_arg(ap, INT); /* value not used */
86 }
87
88 if (PRECISION_STAR == Precision)
89 {
90 Precision = va_arg(ap, INT);
91 }
92 else if (PRECISION_INVALID == Precision)
93 {
94 /* both a '*' and a number, ignore, but remove arg */
95 TempInt = va_arg(ap, INT); /* value not used */
96 }
97
98 TempWStr = va_arg(ap, LPWSTR);
99 Length = Silent_WideCharToMultiByte(TempWStr, -1, 0, 0);
100 if (!Length)
101 {
102 va_end(ap);
103 return -1;
104 }
105 TempStr = (LPSTR) PAL_malloc(Length);
106 if (!TempStr)
107 {
108 va_end(ap);
109 return -1;
110 }
111 if (PRECISION_DOT == Precision)
112 {
113 /* copy nothing */
114 *TempStr = 0;
115 Length = 0;
116 }
117 else if (Precision > 0 && Precision < Length - 1)
118 {
119 Length = Silent_WideCharToMultiByte(TempWStr, Precision,
120 TempStr, Length);
121 if (!Length)
122 {
123 PAL_free(TempStr);
124 va_end(ap);
125 return -1;
126 }
127 TempStr[Length] = 0;
128 Length = Precision;
129 }
130 /* copy everything */
131 else
132 {
133 wctombResult = Silent_WideCharToMultiByte(TempWStr, -1,
134 TempStr, Length);
135 if (!wctombResult)
136 {
137 PAL_free(TempStr);
138 va_end(ap);
139 return -1;
140 }
141 --Length; /* exclude null char */
142 }
143
144 /* do the padding (if needed)*/
145 paddingReturnValue =
146 Silent_AddPaddingVfprintf(stream, TempStr, Width - Length, Flags);
147 if (-1 == paddingReturnValue)
148 {
149 PAL_free(TempStr);
150 va_end(ap);
151 return -1;
152 }
153 written += paddingReturnValue;
154
155 PAL_free(TempStr);
156 }
157 else if (Prefix == PFF_PREFIX_LONG && Type == PFF_TYPE_CHAR)
158 {
159 CHAR TempBuffer[4];
160 if (WIDTH_STAR == Width ||
161 WIDTH_INVALID == Width)
162 {
163 /* ignore (because it's a char), and remove arg */
164 TempInt = va_arg(ap, INT); /* value not used */
165 }
166 if (PRECISION_STAR == Precision ||
167 PRECISION_INVALID == Precision)
168 {
169 /* ignore (because it's a char), and remove arg */
170 TempInt = va_arg(ap, INT); /* value not used */
171 }
172
173 TempWChar = va_arg(ap, int);
174 Length = Silent_WideCharToMultiByte(&TempWChar, 1, TempBuffer, 4);
175 if (!Length)
176 {
177 va_end(ap);
178 return -1;
179 }
180 TempBuffer[Length] = 0;
181
182 /* do the padding (if needed)*/
183 paddingReturnValue =
184 Silent_AddPaddingVfprintf(stream, TempBuffer,
185 Width - Length, Flags);
186 if (-1 == paddingReturnValue)
187 {
188 va_end(ap);
189 return -1;
190 }
191 written += paddingReturnValue;
192
193 }
194 /* this places the number of bytes written to the buffer in the
195 next arg */
196 else if (Type == PFF_TYPE_N)
197 {
198 if (WIDTH_STAR == Width)
199 {
200 Width = va_arg(ap, INT);
201 }
202 if (PRECISION_STAR == Precision)
203 {
204 Precision = va_arg(ap, INT);
205 }
206
207 if (Prefix == PFF_PREFIX_SHORT)
208 {
209 *(va_arg(ap, short *)) = written;
210 }
211 else
212 {
213 *(va_arg(ap, LPLONG)) = written;
214 }
215 }
216 /* types that sprintf can handle */
217 else
218 {
219 TempInt = 0;
220
221 /* %h (short) doesn't seem to be handled properly by local sprintf,
222 so lets do the truncation ourselves. (ptr -> int -> short to avoid
223 warnings */
224 if (Type == PFF_TYPE_P && Prefix == PFF_PREFIX_SHORT)
225 {
226 long trunc1;
227 short trunc2;
228
229 trunc1 = va_arg(ap, LONG);
230 trunc2 = (short)trunc1;
231
232 TempInt = fprintf((FILE*)stream, TempBuff, trunc2);
233 }
234 else if (Type == PFF_TYPE_INT && Prefix == PFF_PREFIX_SHORT)
235 {
236 // Convert explicitly from int to short to get
237 // correct sign extension for shorts on all systems.
238 int n;
239 short s;
240
241 n = va_arg(ap, int);
242 s = (short)n;
243
244 TempInt = fprintf((FILE*)stream, TempBuff, s);
245 }
246 else
247 {
248 va_list apcopy;
249 va_copy(apcopy, ap);
250 TempInt = PAL_vfprintf(stream, TempBuff, apcopy);
251 va_end(apcopy);
252 PAL_printf_arg_remover(&ap, Width, Precision, Type, Prefix);
253 }
254
255 if (-1 != TempInt)
256 {
257 written += TempInt;
258 }
259 }
260 }
261 else
262 {
263#if FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL
264 clearerr((FILE*)stream);
265#endif
266
267 PAL_fwrite(Fmt++, 1, 1, stream); /* copy regular chars into buffer */
268 if (stream->PALferrorCode == PAL_FILE_ERROR)
269 {
270 va_end(ap);
271 return -1;
272 }
273 ++written;
274 }
275 }
276
277 va_end(ap);
278 return written;
279}
280
281/*++
282Function:
283 WideCharToMultiByte (reduced and silent version)
284
285See MSDN doc.
286--*/
287int Silent_WideCharToMultiByte(LPCWSTR lpWideCharStr, int cchWideChar,
288 LPSTR lpMultiByteStr, int cbMultiByte)
289{
290 INT retval =0;
291
292 if ((lpWideCharStr == NULL)||
293 (lpWideCharStr == (LPCWSTR) lpMultiByteStr))
294 {
295 goto EXIT;
296 }
297
298 if (cchWideChar == -1)
299 {
300 cchWideChar = Silent_PAL_wcslen(lpWideCharStr) + 1;
301 }
302
303 if (cbMultiByte == 0)
304 {
305 retval = cchWideChar;
306 goto EXIT;
307 }
308 else if(cbMultiByte < cchWideChar)
309 {
310 retval = 0;
311 goto EXIT;
312 }
313
314 retval = cchWideChar;
315 while(cchWideChar > 0)
316 {
317 if(*lpWideCharStr > 255)
318 {
319 *lpMultiByteStr = '?';
320 }
321 else
322 {
323 *lpMultiByteStr = (unsigned char)*lpWideCharStr;
324 }
325 lpMultiByteStr++;
326 lpWideCharStr++;
327 cchWideChar--;
328 }
329
330EXIT:
331 return retval;
332}
333
334/*******************************************************************************
335Function:
336 Internal_ExtractFormatA (silent version)
337
338 see Internal_ExtractFormatA function in printf.c
339*******************************************************************************/
340BOOL Silent_ExtractFormatA(LPCSTR *Fmt, LPSTR Out, LPINT Flags, LPINT Width, LPINT Precision, LPINT Prefix, LPINT Type)
341{
342 BOOL Result = FALSE;
343 LPSTR TempStr;
344 LPSTR TempStrPtr;
345
346 *Width = WIDTH_DEFAULT;
347 *Precision = PRECISION_DEFAULT;
348 *Flags = PFF_NONE;
349 *Prefix = PFF_PREFIX_DEFAULT;
350 *Type = PFF_TYPE_DEFAULT;
351
352 if (*Fmt && **Fmt == '%')
353 {
354 *Out++ = *(*Fmt)++;
355 }
356 else
357 {
358 return Result;
359 }
360
361 /* we'll never need a temp string longer than the original */
362 TempStrPtr = TempStr = (LPSTR) PAL_malloc(strlen(*Fmt)+1);
363 if (!TempStr)
364 {
365 return Result;
366 }
367
368 /* parse flags */
369 while (**Fmt && (**Fmt == '-' || **Fmt == '+' ||
370 **Fmt == '0' || **Fmt == ' ' || **Fmt == '#'))
371 {
372 switch (**Fmt)
373 {
374 case '-':
375 *Flags |= PFF_MINUS; break;
376 case '+':
377 *Flags |= PFF_PLUS; break;
378 case '0':
379 *Flags |= PFF_ZERO; break;
380 case ' ':
381 *Flags |= PFF_SPACE; break;
382 case '#':
383 *Flags |= PFF_POUND; break;
384 }
385 *Out++ = *(*Fmt)++;
386 }
387 /* '-' flag negates '0' flag */
388 if ((*Flags & PFF_MINUS) && (*Flags & PFF_ZERO))
389 {
390 *Flags -= PFF_ZERO;
391 }
392
393 /* grab width specifier */
394 if (isdigit((unsigned char) **Fmt))
395 {
396 TempStrPtr = TempStr;
397 while (isdigit((unsigned char) **Fmt))
398 {
399 *TempStrPtr++ = **Fmt;
400 *Out++ = *(*Fmt)++;
401 }
402 *TempStrPtr = 0; /* end string */
403 *Width = atoi(TempStr);
404 if (*Width < 0)
405 {
406 SetLastError(ERROR_INTERNAL_ERROR);
407 return Result;
408 }
409 }
410 else if (**Fmt == '*')
411 {
412 *Width = WIDTH_STAR;
413 *Out++ = *(*Fmt)++;
414 if (isdigit((unsigned char) **Fmt))
415 {
416 /* this is an invalid width because we have a * then a number */
417 /* printf handles this by just printing the whole string */
418 *Width = WIDTH_INVALID;
419 while (isdigit((unsigned char) **Fmt))
420 {
421 *Out++ = *(*Fmt)++;
422 }
423 }
424 }
425
426
427 /* grab precision specifier */
428 if (**Fmt == '.')
429 {
430 *Out++ = *(*Fmt)++;
431 if (isdigit((unsigned char) **Fmt))
432 {
433 TempStrPtr = TempStr;
434 while (isdigit((unsigned char) **Fmt))
435 {
436 *TempStrPtr++ = **Fmt;
437 *Out++ = *(*Fmt)++;
438 }
439 *TempStrPtr = 0; /* end string */
440 *Precision = atoi(TempStr);
441 if (*Precision < 0)
442 {
443 SetLastError(ERROR_INTERNAL_ERROR);
444 return Result;
445 }
446 }
447 else if (**Fmt == '*')
448 {
449 *Precision = PRECISION_STAR;
450 *Out++ = *(*Fmt)++;
451 if (isdigit((unsigned char) **Fmt))
452 {
453 /* this is an invalid precision because we have a .* then a
454 number */
455 /* printf handles this by just printing the whole string */
456 *Precision = PRECISION_INVALID;
457 while (isdigit((unsigned char) **Fmt))
458 {
459 *Out++ = *(*Fmt)++;
460 }
461 }
462 }
463 else
464 {
465 *Precision = PRECISION_DOT;
466 }
467 }
468
469#ifdef BIT64
470 if (**Fmt == 'p')
471 {
472 *Prefix = PFF_PREFIX_LONGLONG;
473 }
474#endif
475 /* grab prefix of 'I64' for __int64 */
476 if ((*Fmt)[0] == 'I' && (*Fmt)[1] == '6' && (*Fmt)[2] == '4')
477 {
478 /* convert to 'll' so BSD's snprintf can handle it */
479 *Fmt += 3;
480 *Prefix = PFF_PREFIX_LONGLONG;
481 }
482 /* grab a prefix of 'h' */
483 else if (**Fmt == 'h')
484 {
485 *Prefix = PFF_PREFIX_SHORT;
486 ++(*Fmt);
487 }
488 /* grab prefix of 'l' or the undocumented 'w' (at least in MSDN) */
489 else if (**Fmt == 'l' || **Fmt == 'w')
490 {
491 ++(*Fmt);
492#ifdef BIT64
493 // Only want to change the prefix on 64 bit when printing characters.
494 if (**Fmt == 'c' || **Fmt == 's')
495#endif
496 {
497 *Prefix = PFF_PREFIX_LONG;
498 }
499 }
500 else if (**Fmt == 'L')
501 {
502 /* a prefix of 'L' seems to be ignored */
503 ++(*Fmt);
504 }
505
506 /* grab type 'c' */
507 if (**Fmt == 'c' || **Fmt == 'C')
508 {
509 *Type = PFF_TYPE_CHAR;
510 if (*Prefix != PFF_PREFIX_SHORT && **Fmt == 'C')
511 {
512 *Prefix = PFF_PREFIX_LONG; /* give it a wide prefix */
513 }
514 if (*Prefix == PFF_PREFIX_LONG)
515 {
516 *Out++ = 'l';
517 }
518 *Out++ = 'c';
519 ++(*Fmt);
520 Result = TRUE;
521 }
522 /* grab type 's' */
523 else if (**Fmt == 's' || **Fmt == 'S')
524 {
525 *Type = PFF_TYPE_STRING;
526 if (*Prefix != PFF_PREFIX_SHORT && **Fmt == 'S')
527 {
528 *Prefix = PFF_PREFIX_LONG; /* give it a wide prefix */
529 }
530 if (*Prefix == PFF_PREFIX_LONG)
531 {
532 *Out++ = 'l';
533 }
534 *Out++ = 's';
535 ++(*Fmt);
536 Result = TRUE;
537 }
538 /* grab int types types */
539 else if (**Fmt == 'd' || **Fmt == 'i' || **Fmt == 'o' ||
540 **Fmt == 'u' || **Fmt == 'x' || **Fmt == 'X')
541 {
542 *Type = PFF_TYPE_INT;
543 if (*Prefix == PFF_PREFIX_SHORT)
544 {
545 *Out++ = 'h';
546 }
547 else if (*Prefix == PFF_PREFIX_LONG)
548 {
549 *Out++ = 'l';
550 }
551 else if (*Prefix == PFF_PREFIX_LONGLONG)
552 {
553 *Out++ = 'l';
554 *Out++ = 'l';
555 }
556 *Out++ = *(*Fmt)++;
557 Result = TRUE;
558 }
559 else if (**Fmt == 'e' || **Fmt == 'E' || **Fmt == 'f' ||
560 **Fmt == 'g' || **Fmt == 'G')
561 {
562 /* we can safely ignore the prefixes and only add the type*/
563 *Type = PFF_TYPE_FLOAT;
564 *Out++ = *(*Fmt)++;
565 Result = TRUE;
566 }
567 else if (**Fmt == 'n')
568 {
569 if (*Prefix == PFF_PREFIX_SHORT)
570 {
571 *Out++ = 'h';
572 }
573 *Out++ = *(*Fmt)++;
574 *Type = PFF_TYPE_N;
575 Result = TRUE;
576 }
577 else if (**Fmt == 'p')
578 {
579 *Type = PFF_TYPE_P;
580 *Out++ = *(*Fmt)++;
581
582 if (*Prefix == PFF_PREFIX_LONGLONG)
583 {
584 if (*Precision == PRECISION_DEFAULT)
585 {
586 *Precision = 16;
587 }
588 }
589 else
590 {
591 if (*Precision == PRECISION_DEFAULT)
592 {
593 *Precision = 8;
594 }
595 }
596 Result = TRUE;
597 }
598
599 *Out = 0; /* end the string */
600 PAL_free(TempStr);
601 return Result;
602}
603
604/*******************************************************************************
605Function:
606 AddPaddingVfprintf (silent version)
607 see Internal_AddPaddingVfprintf in printf.c
608*******************************************************************************/
609INT Silent_AddPaddingVfprintf(PAL_FILE *stream, LPSTR In, INT Padding, INT Flags)
610{
611 LPSTR Out;
612 INT LengthInStr;
613 INT Length;
614 LPSTR OutOriginal;
615 INT Written;
616
617 LengthInStr = strlen(In);
618 Length = LengthInStr;
619
620
621 if (Padding > 0)
622 {
623 Length += Padding;
624 }
625 Out = (LPSTR) PAL_malloc(Length+1);
626 int iLen = Length+1;
627 if (!Out)
628 {
629 return -1;
630 }
631 OutOriginal = Out;
632
633 if (Flags & PFF_MINUS) /* pad on right */
634 {
635 if (strcpy_s(Out, iLen, In) != SAFECRT_SUCCESS)
636 {
637 Written = -1;
638 goto Done;
639 }
640
641 Out += LengthInStr;
642 iLen -= LengthInStr;
643 }
644 if (Padding > 0)
645 {
646 iLen -= Padding;
647 if (Flags & PFF_ZERO) /* '0', pad with zeros */
648 {
649 while (Padding--)
650 {
651 *Out++ = '0';
652 }
653 }
654 else /* pad with spaces */
655 {
656 while (Padding--)
657 {
658 *Out++ = ' ';
659 }
660 }
661 }
662 if (!(Flags & PFF_MINUS)) /* put 'In' after padding */
663 {
664 if (strcpy_s(Out, Length+1, In) != SAFECRT_SUCCESS)
665 {
666 Written = -1;
667 goto Done;
668 }
669
670 Out += LengthInStr;
671 iLen -= LengthInStr;
672 }
673
674#if FILE_OPS_CHECK_FERROR_OF_PREVIOUS_CALL
675 clearerr((FILE*)stream);
676#endif
677
678 Written = PAL_fwrite(OutOriginal, 1, Length, stream);
679 if (stream->PALferrorCode == PAL_FILE_ERROR)
680 {
681 Written = -1;
682 }
683
684Done:
685 PAL_free(OutOriginal);
686 return Written;
687}
688
689/*++
690Function:
691 PAL_wcslen (silent version)
692
693See MSDN or the man page for wcslen.
694
695--*/
696size_t Silent_PAL_wcslen(const wchar_16 *string)
697{
698 size_t nChar = 0;
699
700 if ( !string )
701 {
702 return 0;
703 }
704 while (*string++)
705 {
706 nChar++;
707 }
708
709 return nChar;
710}
711