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