1/*
2 * Copyright Andrey Semashev 2022.
3 * Distributed under the Boost Software License, Version 1.0.
4 * (See accompanying file LICENSE_1_0.txt or copy at
5 * http://www.boost.org/LICENSE_1_0.txt)
6 */
7/*!
8 * \file snprintf.hpp
9 * \author Andrey Semashev
10 * \date 06.12.2022
11 *
12 * \brief The header provides more portable definition of snprintf and vsnprintf,
13 * as well as \c wchar_t counterparts.
14 */
15
16#ifndef BOOST_CORE_SNPRINTF_HPP_INCLUDED_
17#define BOOST_CORE_SNPRINTF_HPP_INCLUDED_
18
19#include <stdio.h>
20#include <wchar.h>
21#include <boost/config.hpp>
22
23#ifdef BOOST_HAS_PRAGMA_ONCE
24#pragma once
25#endif
26
27#if defined(__MINGW32__)
28
29#include <cstddef>
30#include <cstdarg>
31#if !defined(__MINGW64_VERSION_MAJOR)
32#include <climits>
33#endif
34
35// MinGW32 and MinGW-w64 provide their own snprintf implementations that are compliant with the C standard.
36#define BOOST_CORE_DETAIL_MINGW_SNPRINTF
37
38#elif (defined(BOOST_MSSTL_VERSION) && BOOST_MSSTL_VERSION < 140)
39
40#include <cstddef>
41#include <cstdarg>
42#include <climits>
43
44// MSVC snprintfs are not conforming but they are good enough for typical use cases.
45#define BOOST_CORE_DETAIL_MSVC_LEGACY_SNPRINTF
46
47#endif
48
49namespace boost {
50
51namespace core {
52
53#if defined(BOOST_CORE_DETAIL_MINGW_SNPRINTF) || defined(BOOST_CORE_DETAIL_MSVC_LEGACY_SNPRINTF)
54
55#if defined(BOOST_CORE_DETAIL_MINGW_SNPRINTF)
56
57inline int vsnprintf(char* buf, std::size_t size, const char* format, std::va_list args)
58{
59 return __mingw_vsnprintf(buf, size, format, args);
60}
61
62inline int vswprintf(wchar_t* buf, std::size_t size, const wchar_t* format, std::va_list args)
63{
64#if defined(__MINGW64_VERSION_MAJOR)
65 int res = __mingw_vsnwprintf(buf, size, format, args);
66 // __mingw_vsnwprintf returns the number of characters to be printed, but (v)swprintf is expected to return -1 on truncation
67 if (static_cast< unsigned int >(res) >= size)
68 res = -1;
69 return res;
70#else
71 // Legacy MinGW32 does not provide __mingw_vsnwprintf, so use _vsnwprintf from MSVC CRT
72 if (BOOST_UNLIKELY(size == 0u || size > static_cast< std::size_t >(INT_MAX)))
73 return -1;
74
75 int res = _vsnwprintf(buf, size, format, args);
76 // (v)swprintf is expected to return -1 on truncation, so we only need to ensure the output is null-terminated
77 if (static_cast< unsigned int >(res) >= size)
78 {
79 buf[size - 1u] = L'\0';
80 res = -1;
81 }
82
83 return res;
84#endif
85}
86
87#elif defined(BOOST_CORE_DETAIL_MSVC_LEGACY_SNPRINTF)
88
89#if defined(_MSC_VER)
90#pragma warning(push)
91// '_vsnprintf': This function or variable may be unsafe. Consider using _vsnprintf_s instead.
92#pragma warning(disable: 4996)
93#endif
94
95inline int vsnprintf(char* buf, std::size_t size, const char* format, std::va_list args)
96{
97 if (BOOST_UNLIKELY(size == 0u))
98 return 0;
99 if (BOOST_UNLIKELY(size > static_cast< std::size_t >(INT_MAX)))
100 return -1;
101
102 buf[size - 1u] = '\0';
103 int res = _vsnprintf(buf, size, format, args);
104 if (static_cast< unsigned int >(res) >= size)
105 {
106 // _vsnprintf returns -1 if the output was truncated and in case of other errors.
107 // Detect truncation by checking whether the output buffer was written over entirely.
108 if (buf[size - 1u] != '\0')
109 {
110 buf[size - 1u] = '\0';
111 res = static_cast< int >(size);
112 }
113 }
114
115 return res;
116}
117
118inline int vswprintf(wchar_t* buf, std::size_t size, const wchar_t* format, std::va_list args)
119{
120 if (BOOST_UNLIKELY(size == 0u || size > static_cast< std::size_t >(INT_MAX)))
121 return -1;
122
123 int res = _vsnwprintf(buf, size, format, args);
124 // (v)swprintf is expected to return -1 on truncation, so we only need to ensure the output is null-terminated
125 if (static_cast< unsigned int >(res) >= size)
126 {
127 buf[size - 1u] = L'\0';
128 res = -1;
129 }
130
131 return res;
132}
133
134#if defined(_MSC_VER)
135#pragma warning(pop)
136#endif
137
138#endif
139
140inline int snprintf(char* buf, std::size_t size, const char* format, ...)
141{
142 std::va_list args;
143 va_start(args, format);
144 int res = vsnprintf(buf, size, format, args);
145 va_end(args);
146 return res;
147}
148
149inline int swprintf(wchar_t* buf, std::size_t size, const wchar_t* format, ...)
150{
151 std::va_list args;
152 va_start(args, format);
153 int res = vswprintf(buf, size, format, args);
154 va_end(args);
155 return res;
156}
157
158#else // defined(BOOST_CORE_DETAIL_MINGW_SNPRINTF) || defined(BOOST_CORE_DETAIL_MSVC_LEGACY_SNPRINTF)
159
160// Standard-conforming compilers already have the correct snprintfs
161using ::snprintf;
162using ::vsnprintf;
163
164using ::swprintf;
165using ::vswprintf;
166
167#endif // defined(BOOST_CORE_DETAIL_MINGW_SNPRINTF) || defined(BOOST_CORE_DETAIL_MSVC_LEGACY_SNPRINTF)
168
169} // namespace core
170
171} // namespace boost
172
173#endif // BOOST_CORE_SNPRINTF_HPP_INCLUDED_
174

source code of boost/libs/core/include/boost/core/snprintf.hpp