// Copyright (C) 2010-2017, Vaclav Haisman. All rights reserved. // // Redistribution and use in source and binary forms, with or without modifica- // tion, are permitted provided that the following conditions are met: // // 1. Redistributions of source code must retain the above copyright notice, // this list of conditions and the following disclaimer. // // 2. Redistributions in binary form must reproduce the above copyright notice, // this list of conditions and the following disclaimer in the documentation // and/or other materials provided with the distribution. // // THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, // INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND // FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE // APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- // DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON // ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include #include #include #include #include #if defined (UNICODE) #include #endif #if defined (LOG4CPLUS_HAVE_STDARG_H) # include #endif #if defined (LOG4CPLUS_HAVE_WCHAR_H) # include #endif #if defined (LOG4CPLUS_HAVE_STDIO_H) # include #endif #if defined (LOG4CPLUS_WITH_UNIT_TESTS) #include #endif namespace log4cplus { namespace helpers { std::size_t const START_BUF_SIZE = 512; #if defined (_WIN32) char const NULL_FILE[] = "NUL"; #else char const NULL_FILE[] = "/dev/null"; #endif namespace { #if defined (LOG4CPLUS_USE_POOR_MANS_SNPRINTF) static inline int vftprintf (std::FILE * file, tchar const * fmt, std::va_list args) LOG4CPLUS_FORMAT_ATTRIBUTE (__printf__, 2, 0); static inline int vftprintf (std::FILE * file, tchar const * fmt, std::va_list args) { #if defined (UNICODE) # if defined (LOG4CPLUS_HAVE_VFWPRINTF_S) return vfwprintf_s (file, fmt, args); # else return std::vfwprintf (file, fmt, args); # endif #else # if defined (LOG4CPLUS_HAVE_VFPRINTF_S) return vfprintf_s (file, fmt, args); # else return std::vfprintf (file, fmt, args); # endif #endif } static inline int vstprintf (tchar * dest, std::size_t dest_size, tchar const * fmt, std::va_list args) LOG4CPLUS_FORMAT_ATTRIBUTE (__printf__, 3, 0); static inline int vstprintf (tchar * dest, std::size_t dest_size, tchar const * fmt, std::va_list args) { int ret; #if defined (UNICODE) # if defined (LOG4CPLUS_HAVE_VSWPRINTF_S) ret = vswprintf_s (dest, dest_size, fmt, args); # else ret = std::vswprintf (dest, dest_size, fmt, args); # endif #else # if defined (LOG4CPLUS_HAVE_VSPRINTF_S) ret = vsprintf_s (dest, dest_size, fmt, args); # else ret = std::vsprintf (dest, fmt, args); # endif #endif if (ret >= 0) assert (static_cast(ret) <= dest_size); return ret; } #else /* ?defined (LOG4CPLUS_USE_POOR_MANS_SNPRINTF) */ static inline int vsntprintf (tchar * dest, std::size_t dest_size, tchar const * fmt, std::va_list args) LOG4CPLUS_FORMAT_ATTRIBUTE (__printf__, 3, 0); static inline int vsntprintf (tchar * dest, std::size_t dest_size, tchar const * fmt, std::va_list args) { int ret; #if defined (UNICODE) # if defined (LOG4CPLUS_HAVE__VSWPRINTF_P) ret = _vswprintf_p (dest, dest_size, fmt, args); # elif defined (LOG4CPLUS_HAVE__VSNWPRINTF_S) && defined (_TRUNCATE) ret = _vsnwprintf_s (dest, dest_size, _TRUNCATE, fmt, args); # elif defined (LOG4CPLUS_HAVE_VSNWPRINTF) ret = vsnwprintf (dest, dest_size, fmt, args); # elif defined (LOG4CPLUS_HAVE__VSNWPRINTF) ret = _vsnwprintf (dest, dest_size, fmt, args); # else using namespace std; ret = vswprintf (dest, dest_size, fmt, args); # endif #else # if defined (LOG4CPLUS_HAVE__VSPRINTF_P) ret = _vsprintf_p (dest, dest_size, fmt, args); # elif defined (LOG4CPLUS_HAVE_VSNPRINTF_S) && defined (_TRUNCATE) ret = vsnprintf_s (dest, dest_size, _TRUNCATE, fmt, args); # elif defined (LOG4CPLUS_HAVE__VSNPRINTF_S) && defined (_TRUNCATE) ret = _vsnprintf_s (dest, dest_size, _TRUNCATE, fmt, args); # elif defined (LOG4CPLUS_HAVE_VSNPRINTF) ret = vsnprintf (dest, dest_size, fmt, args); # elif defined (LOG4CPLUS_HAVE__VSNPRINTF) ret = _vsnprintf (dest, dest_size, fmt, args); # else # error "None of vsnprintf_s, _vsnprintf_s, vsnprintf or _vsnprintf is available." # endif #endif return ret; } #endif } snprintf_buf::snprintf_buf () : buf (START_BUF_SIZE) { } tchar const * snprintf_buf::print (tchar const * fmt, ...) { assert (fmt); tchar const * str = nullptr; int ret = 0; std::va_list args; do { va_start (args, fmt); ret = print_va_list (str, fmt, args); va_end (args); } while (ret == -1); return str; } int snprintf_buf::print_va_list (tchar const * & str, tchar const * fmt, std::va_list args) { int printed; std::size_t const fmt_len = std::char_traits::length (fmt); std::size_t buf_size = buf.size (); std::size_t const output_estimate = fmt_len + fmt_len / 2 + 1; if (output_estimate > buf_size) buf.resize (buf_size = output_estimate); #if defined (LOG4CPLUS_USE_POOR_MANS_SNPRINTF) std::FILE * & fnull = internal::get_ptd ()->fnull; if (! fnull) { fnull = std::fopen (NULL_FILE, "wb"); if (! fnull) LogLog::getLogLog ()->error ( LOG4CPLUS_TEXT ("Could not open NULL_FILE."), true); } printed = vftprintf (fnull, fmt, args); if (printed == -1) LogLog::getLogLog ()->error ( LOG4CPLUS_TEXT ("Error printing into NULL_FILE."), true); buf.resize (printed + 1); int sprinted = vstprintf (&buf[0], buf.size (), fmt, args); if (sprinted == -1 || sprinted >= buf.size ()) LogLog::getLogLog ()->error ( LOG4CPLUS_TEXT ("Error printing into string."), true); assert (printed == sprinted); buf[sprinted] = 0; #else printed = vsntprintf (&buf[0], buf_size - 1, fmt, args); if (printed == -1) { #if defined (EILSEQ) if (errno == EILSEQ) { LogLog::getLogLog ()->error ( LOG4CPLUS_TEXT ("Character conversion error when printing")); // Return zero to terminate the outer loop in // snprintf_buf::print(). return 0; } #endif buf_size *= 2; buf.resize (buf_size); } else if (printed >= static_cast(buf_size - 1)) { buf_size = printed + 2; buf.resize (buf_size); printed = -1; } else buf[printed] = 0; #endif str = &buf[0]; return printed; } #if defined (LOG4CPLUS_WITH_UNIT_TESTS) CATCH_TEST_CASE ("snprintf_buf", "[snprintf_buf]") { snprintf_buf buf; CATCH_SECTION ("ints") { tchar const * const result = buf.print (LOG4CPLUS_TEXT ("%d %d %d"), 1, 2, 3); std::size_t const len = std::char_traits::length (result); CATCH_REQUIRE (result); CATCH_REQUIRE ( std::char_traits::compare (result, LOG4CPLUS_TEXT ("1 2 3"), len) == 0); } } #endif } } // namespace log4cplus { namespace helpers