1 krisbash 1.1 /*
2 **==============================================================================
3 **
4 ** Open Management Infrastructure (OMI)
5 **
6 ** Copyright (c) Microsoft Corporation
7 **
8 ** Licensed under the Apache License, Version 2.0 (the "License"); you may not
9 ** use this file except in compliance with the License. You may obtain a copy
10 ** of the License at
11 **
12 ** http://www.apache.org/licenses/LICENSE-2.0
13 **
14 ** THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 ** KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
16 ** WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
17 ** MERCHANTABLITY OR NON-INFRINGEMENT.
18 **
19 ** See the Apache 2 License for the specific language governing permissions
20 ** and limitations under the License.
21 **
22 krisbash 1.1 **==============================================================================
23 */
24
25 #include "format.h"
26 #include <pal/strings.h>
27
28 #include <string.h>
29 #include <pal/once.h>
30
31 #include <locale.h>
32
33 char* FixupFormat(
34 _Out_writes_z_(size) _Null_terminated_ char* buf,
35 _In_ size_t size,
36 _In_z_ const char* fmt)
37 {
38 size_t n = strlen(fmt) + 1;
39 char* start;
40 char* p;
41
42 if (n > size)
43 krisbash 1.1 {
44 start = (char*)SystemMalloc(n * sizeof(char));
45
46 if (!start)
47 return NULL;
48 }
49 else
50 start = buf;
51
52 for (p = start; *fmt; )
53 {
54 if (fmt[0] == '%' && fmt[1] == 'T')
55 {
56 *p++ = '%';
57 #if defined(CONFIG_ENABLE_WCHAR)
58 *p++ = 'S';
59 #else
60 *p++ = 's';
61 #endif
62 fmt += 2;
63 }
64 krisbash 1.1 else
65 *p++ = *fmt++;
66 }
67
68 *p = '\0';
69
70 return start;
71 }
72
73 int Printf(
74 const char* format,
75 ...)
76 {
77 va_list ap;
78 int r;
79
80 memset(&ap, 0, sizeof(ap));
81 va_start(ap, format);
82 r = Vfprintf(stdout, format, ap);
83 va_end(ap);
84
85 krisbash 1.1 return r;
86 }
87
88 int Fprintf(
89 FILE* os,
90 const char* format,
91 ...)
92 {
93 va_list ap;
94 int r;
95
96 memset(&ap, 0, sizeof(ap));
97 va_start(ap, format);
98 r = Vfprintf(os, format, ap);
99 va_end(ap);
100
101 return r;
102 }
103
104 _Use_decl_annotations_
105 int Snprintf(
106 krisbash 1.1 char* buffer,
107 size_t size,
108 const char* format,
109 ...)
110 {
111 va_list ap;
112 int r;
113
114 va_start(ap, format);
115 r = Vsnprintf(buffer, size, format, ap);
116 va_end(ap);
117
118 return r;
119 }
120
121 _Use_decl_annotations_
122 int Snprintf_CultureInvariant(
123 char* buffer,
124 size_t size,
125 const char* format,
126 ...)
127 krisbash 1.1 {
128 va_list ap;
129 int r;
130
131 va_start(ap, format);
132 r = Vsnprintf_CultureInvariant(buffer, size, format, ap);
133 va_end(ap);
134
135 return r;
136 }
137
138 int Vprintf(
139 const char* format,
140 va_list ap)
141 {
142 return Vfprintf(stdout, format, ap);
143 }
144
145 int Vfprintf(
146 FILE* os,
147 const char* format,
148 krisbash 1.1 va_list ap)
149 {
150 int r;
151 char buf[128];
152 char* fmt;
153
154 memset(&buf, 0, sizeof(buf));
155
156 fmt = FixupFormat(buf, PAL_COUNT(buf), format);
157
158 if (!fmt)
159 return -1;
160
161 r = vfprintf(os, fmt, ap);
162
163 if (fmt != buf)
164 SystemFree(fmt);
165
166 return r;
167 }
168
169 krisbash 1.1 #if defined(CONFIG_VSNPRINTF_RETURN_MINUSONE_WITH_NULLBUFFER)
170 static int _GetFormattedSize(
171 const char* fmt,
172 va_list ap)
173 {
174 char buf[128];
175 char* p = buf;
176 size_t n = sizeof buf;
177 int r;
178
179 while ((r = vsnprintf(p, n, fmt, ap)) == -1)
180 {
181 n *= 2;
182
183 if (p == buf)
184 p = (char*)SystemMalloc(n);
185 else
186 {
187 char* q = (char*)SystemRealloc(p, n);
188
189 if (q != NULL)
190 krisbash 1.1 p = q;
191 }
192 if (p == NULL)
193 return -1;
194
195 if (p != buf)
196 SystemFree(p);
197 }
198 return r;
199 }
200 #endif /* defined(CONFIG_VSNPRINTF_RETURN_MINUSONE_WITH_NULLBUFFER) */
201
202 _Use_decl_annotations_
203 int Vsnprintf(
204 char* buffer,
205 size_t size,
206 const char* format,
207 va_list ap)
208 {
209 int r;
210 char* fmt;
211 krisbash 1.1 char buf[128];
212
213 memset(&buf, 0, sizeof(buf));
214
215 fmt = FixupFormat(buf, PAL_COUNT(buf), format);
216
217 if (!fmt)
218 {
219 buffer[0] = '\0';
220 return -1;
221 }
222
223 #ifdef _MSC_VER
224 r = _vsnprintf_s(buffer, size, _TRUNCATE, fmt, ap);
225 #else
226 # ifdef __hpux // HP-UX 11.21 and earlier will core dump if buffer is NULL
227 if (buffer == NULL || size == 0)
228 r = _GetFormattedSize(fmt, ap);
229 else
230 # endif
231 r = vsnprintf(buffer, size, fmt, ap);
232 krisbash 1.1 #if defined(CONFIG_VSNPRINTF_RETURN_MINUSONE_WITH_NULLBUFFER) // SUN-SPARC and HP-UX without the UNIX 2003 standard option returns -1
233 if (r == -1) // instead of the needed byte count if size was too small
234 r = _GetFormattedSize(fmt, ap);
235 # endif
236 #endif
237
238 if (fmt != buf)
239 SystemFree(fmt);
240
241 return r;
242 }
243
244 _Use_decl_annotations_
245 int Vsnprintf_CultureInvariant(
246 char* buffer,
247 size_t size,
248 const char* format,
249 va_list ap)
250 {
251 /* Ideally we would avoid calling setlocale and use _vsprintf_s_l
252 instead. The problem is that _create_locale is not exported on Win7 */
253 krisbash 1.1
254 int r;
255 char oldLocale[128];
256 Strlcpy(oldLocale, setlocale(LC_ALL, NULL), PAL_COUNT(oldLocale));
257 setlocale(LC_ALL, "C");
258 r = Vsnprintf(buffer, size, format, ap);
259 setlocale(LC_ALL, oldLocale);
260 return r;
261 }
262
263 wchar_t* WFixupFormat(
264 _Out_writes_z_(size) _Null_terminated_ wchar_t* buf,
265 _In_ size_t size,
266 _In_z_ const wchar_t* fmt)
267 {
268 size_t n = wcslen(fmt) + 1;
269 wchar_t* start;
270 wchar_t* p;
271
272 if (n > size)
273 {
274 krisbash 1.1 start = (wchar_t*)SystemMalloc(n * sizeof(wchar_t));
275
276 if (!start)
277 return NULL;
278 }
279 else
280 start = buf;
281
282 for (p = start; *fmt; )
283 {
284 if (fmt[0] == '%' && fmt[1] == 'T')
285 {
286 *p++ = '%';
287 #if defined(CONFIG_ENABLE_WCHAR)
288 # if defined(_MSC_VER)
289 *p++ = 's';
290 # else
291 *p++ = 'S';
292 # endif
293 #else
294 # if defined(_MSC_VER)
295 krisbash 1.1 *p++ = 'S';
296 # else
297 *p++ = 's';
298 # endif
299 #endif
300 fmt += 2;
301 }
302 #if defined(_MSC_VER)
303 else if (fmt[0] == '%' && fmt[1] == 's')
304 {
305 *p++ = '%';
306 *p++ = 'S';
307 fmt += 2;
308 }
309 else if (fmt[0] == '%' && fmt[1] == 'S')
310 {
311 *p++ = '%';
312 *p++ = 's';
313 fmt += 2;
314 }
315 #endif
316 krisbash 1.1 else
317 *p++ = *fmt++;
318 }
319
320 *p = '\0';
321
322 return start;
323 }
324
325 int Wprintf(
326 const wchar_t* format,
327 ...)
328 {
329 va_list ap;
330 int r;
331
332 memset(&ap, 0, sizeof(ap));
333 va_start(ap, format);
334 r = Vfwprintf(stdout, format, ap);
335 va_end(ap);
336
337 krisbash 1.1 return r;
338 }
339
340 int Fwprintf(
341 FILE* os,
342 const wchar_t* format,
343 ...)
344 {
345 va_list ap;
346 int r;
347
348 memset(&ap, 0, sizeof(ap));
349 va_start(ap, format);
350 r = Vfwprintf(os, format, ap);
351 va_end(ap);
352
353 return r;
354 }
355
356 _Use_decl_annotations_
357 int Swprintf(
358 krisbash 1.1 wchar_t* buffer,
359 size_t size,
360 const wchar_t* format,
361 ...)
362 {
363 va_list ap;
364 int r;
365
366 va_start(ap, format);
367 r = Vswprintf(buffer, size, format, ap);
368 va_end(ap);
369
370 return r;
371 }
372
373 _Use_decl_annotations_
374 int Swprintf_CultureInvariant(
375 wchar_t* buffer,
376 size_t size,
377 const wchar_t* format,
378 ...)
379 krisbash 1.1 {
380 va_list ap;
381 int r;
382
383 va_start(ap, format);
384 r = Vswprintf_CultureInvariant(buffer, size, format, ap);
385 va_end(ap);
386
387 return r;
388 }
389
390 int Vwprintf(
391 const wchar_t* format,
392 va_list ap)
393 {
394 return Vfwprintf(stdout, format, ap);
395 }
396
397 int Vfwprintf(
398 FILE* os,
399 const wchar_t* format,
400 krisbash 1.1 va_list ap)
401 {
402 int r;
403 wchar_t buf[128];
404 wchar_t* fmt;
405
406 memset(&buf, 0, sizeof(buf));
407
408 fmt = WFixupFormat(buf, PAL_COUNT(buf), format);
409
410 if (!fmt)
411 return -1;
412
413 r = vfwprintf(os, fmt, ap);
414
415 if (fmt != buf)
416 SystemFree(fmt);
417
418 return r;
419 }
420
421 krisbash 1.1 _Use_decl_annotations_
422 int Vswprintf(
423 wchar_t* buffer,
424 size_t size,
425 const wchar_t* format,
426 va_list ap)
427 {
428 int r;
429 wchar_t* fmt;
430 wchar_t buf[128];
431
432 memset(&buf, 0, sizeof(buf));
433
434 fmt = WFixupFormat(buf, PAL_COUNT(buf), format);
435
436 if (!fmt)
437 {
438 buffer[0] = '\0';
439 return -1;
440 }
441
442 krisbash 1.1 #ifdef _MSC_VER
443 r = vswprintf_s(buffer, size, fmt, ap);
444 #else
445 r = vswprintf(buffer, size, fmt, ap);
446 #endif
447
448 if (fmt != buf)
449 SystemFree(fmt);
450
451 return r;
452 }
453
454 _Use_decl_annotations_
455 int Vswprintf_CultureInvariant(
456 wchar_t* buffer,
457 size_t size,
458 const wchar_t* format,
459 va_list ap)
460 {
461 /* Ideally we would avoid calling setlocale and use _vswprintf_s_l
462 instead. The problem is that _create_locale is not exported on Win7. */
463 krisbash 1.1
464 int r;
465 #ifdef _MSC_VER
466 wchar_t oldLocale[128];
467 Wcslcpy(oldLocale, _wsetlocale(LC_ALL, NULL), PAL_COUNT(oldLocale));
468 _wsetlocale(LC_ALL, L"C");
469 #else
470 char oldLocale[128];
471 Strlcpy(oldLocale, setlocale(LC_ALL, NULL), PAL_COUNT(oldLocale));
472 setlocale(LC_ALL, "C");
473 #endif
474 r = Vswprintf(buffer, size, format, ap);
475 #ifdef _MSC_VER
476 _wsetlocale(LC_ALL, oldLocale);
477 #else
478 setlocale(LC_ALL, oldLocale);
479 #endif
480 return r;
481 }
482
483 _Use_decl_annotations_
484 krisbash 1.1 int Sscanf_CultureInvariant(
485 const char* buffer,
486 const char* format,
487 ...)
488 {
489 va_list ap;
490 int r;
491
492 va_start(ap, format);
493 r = Vsscanf_CultureInvariant(buffer, format, ap);
494 va_end(ap);
495
496 return r;
497 }
498
499 _Use_decl_annotations_
500 int Vsscanf_CultureInvariant(
501 const char* buffer,
502 const char* format,
503 va_list ap)
504 {
505 krisbash 1.1 int r;
506 char* fmt;
507 char buf[128];
508
509 memset(&buf, 0, sizeof(buf));
510
511 fmt = FixupFormat(buf, PAL_COUNT(buf), format);
512
513 if (!fmt)
514 {
515 return EOF;
516 }
517
518 {
519 /* Ideally we would avoid calling setlocale and use _sscanf_l
520 instead. The problem is that _create_locale is not exported on Win7 */
521 char oldLocale[128];
522 Strlcpy(oldLocale, setlocale(LC_ALL, NULL), PAL_COUNT(oldLocale));
523 setlocale(LC_ALL, "C");
524 #ifdef _MSC_VER
525 {
526 krisbash 1.1 /* no *v*scanf on Windows and some older UNIXes... using a workaround instead */
527 void *args[10] = {0};
528 int numberOfFormatSpecifiers = 0;
529 const char *c;
530
531 for (c = format; c[0] != '\0'; c++)
532 {
533 if (c[0] == '%')
534 {
535 if ((c[1] == '%') || (c[1] == '*'))
536 {
537 c++;
538 }
539 else
540 {
541 if (numberOfFormatSpecifiers >= 10)
542 {
543 r = EOF;
544 goto CleanUp;
545 }
546 args[numberOfFormatSpecifiers] = va_arg(ap, void*);
547 krisbash 1.1 numberOfFormatSpecifiers++;
548 }
549 }
550 }
551
552 r = sscanf_s(
553 buffer, fmt,
554 args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
555 }
556 CleanUp:
557 #else
558 r = vsscanf(buffer, fmt, ap);
559 #endif
560 setlocale(LC_ALL, oldLocale);
561 }
562
563 if (fmt != buf)
564 SystemFree(fmt);
565
566 return r;
567 }
568 krisbash 1.1
569
570 _Use_decl_annotations_
571 int Swscanf_CultureInvariant(
572 const wchar_t* buffer,
573 const wchar_t* format,
574 ...)
575 {
576 va_list ap;
577 int r;
578
579 va_start(ap, format);
580 r = Vswscanf_CultureInvariant(buffer, format, ap);
581 va_end(ap);
582
583 return r;
584 }
585
586 _Use_decl_annotations_
587 int Vswscanf_CultureInvariant(
588 const wchar_t* buffer,
589 krisbash 1.1 const wchar_t* format,
590 va_list ap)
591 {
592 int r;
593 wchar_t* fmt;
594 wchar_t buf[128];
595
596 memset(&buf, 0, sizeof(buf));
597
598 fmt = WFixupFormat(buf, PAL_COUNT(buf), format);
599
600 if (!fmt)
601 {
602 return EOF;
603 }
604
605 {
606 /* TODO/FIXME - ideally we would avoid calling setlocale and use _swscanf_l
607 instead. The problem is that _create_locale is not exported on Win7 */
608 #ifndef CONFIG_HAVE_VSWSCANF
609 # ifdef _MSC_VER
610 krisbash 1.1 wchar_t oldLocale[128];
611 Wcslcpy(oldLocale, _wsetlocale(LC_ALL, NULL), PAL_COUNT(oldLocale));
612 _wsetlocale(LC_ALL, L"C");
613 # else
614 char oldLocale[128];
615 Strlcpy(oldLocale, setlocale(LC_ALL, NULL), sizeof oldLocale);
616 setlocale(LC_ALL, "C");
617 # endif
618 {
619 /* no *v*scanf on Windows... using a workaround instead */
620 void *args[10] = {0};
621 int numberOfFormatSpecifiers = 0;
622 const wchar_t *c;
623
624 for (c = format; c[0] != L'\0'; c++)
625 {
626 if (c[0] == L'%')
627 {
628 if ((c[1] == L'%') || (c[1] == L'*'))
629 {
630 c++;
631 krisbash 1.1 }
632 else
633 {
634 if (numberOfFormatSpecifiers >= 10)
635 {
636 r = EOF;
637 goto CleanUp;
638 }
639 args[numberOfFormatSpecifiers] = va_arg(ap, void*);
640 numberOfFormatSpecifiers++;
641 }
642 }
643 }
644 # ifdef _MSC_VER
645 r = swscanf_s(
646 buffer, fmt,
647 args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
648 # else
649 r = swscanf(
650 buffer, fmt,
651 args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8], args[9]);
652 krisbash 1.1 # endif
653 }
654
655 CleanUp:
656 # ifdef _MSC_VER
657 _wsetlocale(LC_ALL, oldLocale);
658 # else
659 setlocale(LC_ALL, oldLocale);
660 # endif
661 #else
662 char oldLocale[128];
663 Strlcpy(oldLocale, setlocale(LC_ALL, NULL), PAL_COUNT(oldLocale));
664 setlocale(LC_ALL, "C");
665 r = vswscanf(buffer, fmt, ap);
666 setlocale(LC_ALL, oldLocale);
667 #endif
668 }
669
670 if (fmt != buf)
671 SystemFree(fmt);
672
673 krisbash 1.1 return r;
674 }
675
676 PAL_Char* Vstprintf_StrDup(_In_z_ const PAL_Char* templateString, va_list ap)
677 {
678 PAL_Char* resultString = NULL;
679 int resultCharCount;
680 int err;
681 va_list tmpAp;
682
683 #if defined(CONFIG_ENABLE_WCHAR)
684 /* on Linux, if stackBuffer is too small, then
685 1. snprintf returns the number of required characters
686 2. swnprintf returns -1 (arrgh!)
687 for #2 we need to loop to find the required size...
688 */
689 resultCharCount = 16;
690 do
691 {
692 PAL_Char* tmp = (PAL_Char*)PAL_Realloc(
693 resultString, sizeof(PAL_Char) * resultCharCount);
694 krisbash 1.1
695 if (!tmp)
696 {
697 PAL_Free(resultString);
698 resultString = NULL;
699 goto CleanUp;
700 }
701 resultString = tmp;
702
703 PAL_va_copy(tmpAp, ap);
704 err = Vstprintf(resultString, resultCharCount, templateString, tmpAp);
705 va_end(tmpAp);
706 if (err < 0)
707 {
708 if (resultCharCount < ( ((size_t)-1) / (2*sizeof(PAL_Char)) ))
709 {
710 resultCharCount *= 2;
711 continue;
712 }
713 else
714 {
715 krisbash 1.1 PAL_Free(resultString);
716 resultString = NULL;
717 goto CleanUp;
718 }
719 }
720 }
721 while (err < 0);
722 #else /* !defined(CONFIG_ENABLE_WCHAR) || defined(_MSC_VER) */
723 PAL_va_copy(tmpAp, ap);
724 resultCharCount = Vstprintf(NULL, 0, templateString, tmpAp);
725 va_end(tmpAp);
726 if (resultCharCount < 0)
727 {
728 goto CleanUp;
729 }
730 else
731 {
732 resultString = (PAL_Char*)PAL_Malloc(sizeof(PAL_Char) * (resultCharCount + 1));
733
734 if (!resultString)
735 {
736 krisbash 1.1 goto CleanUp;
737 }
738
739 err = Vstprintf(resultString, resultCharCount + 1, templateString, ap);
740 if ((0 <= err) && (err <= resultCharCount))
741 {
742 resultString[resultCharCount] = PAL_T('\0');
743 }
744 else
745 {
746 PAL_Free(resultString);
747 resultString = NULL;
748 goto CleanUp;
749 }
750 }
751 #endif /* ?defined(CONFIG_ENABLE_WCHAR) ?defined(_MSC_VER) */
752 CleanUp:
753 return resultString;
754 }
755
756 PAL_Char* Stprintf_StrDup(_In_z_ const PAL_Char* templateString, ...)
757 krisbash 1.1 {
758 PAL_Char* resultString = NULL;
759 va_list ap;
760
761 va_start(ap, templateString);
762 resultString = Vstprintf_StrDup(templateString, ap);
763 va_end(ap);
764 return resultString;
765 }
766
|