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 | silent_printf.c |
12 | |
13 | Abstract: |
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 | |
18 | Revision 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 | |
34 | static int Silent_WideCharToMultiByte(LPCWSTR lpWideCharStr, int cchWideChar, |
35 | LPSTR lpMultiByteStr, int cbMultiByte); |
36 | static BOOL Silent_ExtractFormatA(LPCSTR *Fmt, LPSTR Out, LPINT Flags, LPINT Width, |
37 | LPINT Precision, LPINT Prefix, LPINT Type); |
38 | static INT Silent_AddPaddingVfprintf(PAL_FILE *stream, LPSTR In, INT Padding, |
39 | INT Flags); |
40 | |
41 | static size_t Silent_PAL_wcslen(const wchar_16 *string); |
42 | |
43 | /*++ |
44 | Function: |
45 | PAL_vfprintf (silent version) |
46 | |
47 | for more details, see PAL_vfprintf in printf.c |
48 | --*/ |
49 | int 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 | /*++ |
282 | Function: |
283 | WideCharToMultiByte (reduced and silent version) |
284 | |
285 | See MSDN doc. |
286 | --*/ |
287 | int 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 | |
330 | EXIT: |
331 | return retval; |
332 | } |
333 | |
334 | /******************************************************************************* |
335 | Function: |
336 | Internal_ExtractFormatA (silent version) |
337 | |
338 | see Internal_ExtractFormatA function in printf.c |
339 | *******************************************************************************/ |
340 | BOOL (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 | /******************************************************************************* |
605 | Function: |
606 | AddPaddingVfprintf (silent version) |
607 | see Internal_AddPaddingVfprintf in printf.c |
608 | *******************************************************************************/ |
609 | INT 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 | |
684 | Done: |
685 | PAL_free(OutOriginal); |
686 | return Written; |
687 | } |
688 | |
689 | /*++ |
690 | Function: |
691 | PAL_wcslen (silent version) |
692 | |
693 | See MSDN or the man page for wcslen. |
694 | |
695 | --*/ |
696 | size_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 | |