• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  Formatting library for C++
3 
4  Copyright (c) 2012 - 2016, Victor Zverovich
5  All rights reserved.
6 
7  Redistribution and use in source and binary forms, with or without
8  modification, are permitted provided that the following conditions are met:
9 
10  1. Redistributions of source code must retain the above copyright notice, this
11     list of conditions and the following disclaimer.
12  2. Redistributions in binary form must reproduce the above copyright notice,
13     this list of conditions and the following disclaimer in the documentation
14     and/or other materials provided with the distribution.
15 
16  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17  ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include "format.h"
29 
30 #include <string.h>
31 
32 #include <cctype>
33 #include <cerrno>
34 #include <climits>
35 #include <cmath>
36 #include <cstdarg>
37 #include <cstddef>  // for std::ptrdiff_t
38 
39 #if defined(_WIN32) && defined(__MINGW32__)
40 # include <cstring>
41 #endif
42 
43 #if FMT_USE_WINDOWS_H
44 # if defined(NOMINMAX) || defined(FMT_WIN_MINMAX)
45 #  include <windows.h>
46 # else
47 #  define NOMINMAX
48 #  include <windows.h>
49 #  undef NOMINMAX
50 # endif
51 #endif
52 
53 using fmt::internal::Arg;
54 
55 #if FMT_EXCEPTIONS
56 # define FMT_TRY try
57 # define FMT_CATCH(x) catch (x)
58 #else
59 # define FMT_TRY if (true)
60 # define FMT_CATCH(x) if (false)
61 #endif
62 
63 #ifdef _MSC_VER
64 # pragma warning(push)
65 # pragma warning(disable: 4127)  // conditional expression is constant
66 # pragma warning(disable: 4702)  // unreachable code
67 // Disable deprecation warning for strerror. The latter is not called but
68 // MSVC fails to detect it.
69 # pragma warning(disable: 4996)
70 #endif
71 
72 // Dummy implementations of strerror_r and strerror_s called if corresponding
73 // system functions are not available.
strerror_r(int,char *,...)74 static inline fmt::internal::Null<> strerror_r(int, char *, ...) {
75   return fmt::internal::Null<>();
76 }
strerror_s(char *,std::size_t,...)77 static inline fmt::internal::Null<> strerror_s(char *, std::size_t, ...) {
78   return fmt::internal::Null<>();
79 }
80 
81 namespace fmt {
82 
~RuntimeError()83 FMT_FUNC internal::RuntimeError::~RuntimeError() FMT_DTOR_NOEXCEPT {}
~FormatError()84 FMT_FUNC FormatError::~FormatError() FMT_DTOR_NOEXCEPT {}
~SystemError()85 FMT_FUNC SystemError::~SystemError() FMT_DTOR_NOEXCEPT {}
86 
87 namespace {
88 
89 #ifndef _MSC_VER
90 # define FMT_SNPRINTF snprintf
91 #else  // _MSC_VER
92 inline int fmt_snprintf(char *buffer, size_t size, const char *format, ...) {
93   va_list args;
94   va_start(args, format);
95   int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
96   va_end(args);
97   return result;
98 }
99 # define FMT_SNPRINTF fmt_snprintf
100 #endif  // _MSC_VER
101 
102 #if defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
103 # define FMT_SWPRINTF snwprintf
104 #else
105 # define FMT_SWPRINTF swprintf
106 #endif // defined(_WIN32) && defined(__MINGW32__) && !defined(__NO_ISOCEXT)
107 
108 const char RESET_COLOR[] = "\x1b[0m";
109 
110 typedef void (*FormatFunc)(Writer &, int, StringRef);
111 
112 // Portable thread-safe version of strerror.
113 // Sets buffer to point to a string describing the error code.
114 // This can be either a pointer to a string stored in buffer,
115 // or a pointer to some static immutable string.
116 // Returns one of the following values:
117 //   0      - success
118 //   ERANGE - buffer is not large enough to store the error message
119 //   other  - failure
120 // Buffer should be at least of size 1.
safe_strerror(int error_code,char * & buffer,std::size_t buffer_size)121 int safe_strerror(
122     int error_code, char *&buffer, std::size_t buffer_size) FMT_NOEXCEPT {
123   FMT_ASSERT(buffer != 0 && buffer_size != 0, "invalid buffer");
124 
125   class StrError {
126    private:
127     int error_code_;
128     char *&buffer_;
129     std::size_t buffer_size_;
130 
131     // A noop assignment operator to avoid bogus warnings.
132     void operator=(const StrError &) {}
133 
134     // Handle the result of XSI-compliant version of strerror_r.
135     int handle(int result) {
136       // glibc versions before 2.13 return result in errno.
137       return result == -1 ? errno : result;
138     }
139 
140     // Handle the result of GNU-specific version of strerror_r.
141     int handle(char *message) {
142       // If the buffer is full then the message is probably truncated.
143       if (message == buffer_ && strlen(buffer_) == buffer_size_ - 1)
144         return ERANGE;
145       buffer_ = message;
146       return 0;
147     }
148 
149     // Handle the case when strerror_r is not available.
150     int handle(internal::Null<>) {
151       return fallback(strerror_s(buffer_, buffer_size_, error_code_));
152     }
153 
154     // Fallback to strerror_s when strerror_r is not available.
155     int fallback(int result) {
156       // If the buffer is full then the message is probably truncated.
157       return result == 0 && strlen(buffer_) == buffer_size_ - 1 ?
158             ERANGE : result;
159     }
160 
161     // Fallback to strerror if strerror_r and strerror_s are not available.
162     int fallback(internal::Null<>) {
163       errno = 0;
164       buffer_ = strerror(error_code_);
165       return errno;
166     }
167 
168    public:
169     StrError(int err_code, char *&buf, std::size_t buf_size)
170       : error_code_(err_code), buffer_(buf), buffer_size_(buf_size) {}
171 
172     int run() {
173       // Suppress a warning about unused strerror_r.
174       strerror_r(0, FMT_NULL, "");
175       return handle(strerror_r(error_code_, buffer_, buffer_size_));
176     }
177   };
178   return StrError(error_code, buffer, buffer_size).run();
179 }
180 
format_error_code(Writer & out,int error_code,StringRef message)181 void format_error_code(Writer &out, int error_code,
182                        StringRef message) FMT_NOEXCEPT {
183   // Report error code making sure that the output fits into
184   // INLINE_BUFFER_SIZE to avoid dynamic memory allocation and potential
185   // bad_alloc.
186   out.clear();
187   static const char SEP[] = ": ";
188   static const char ERROR_STR[] = "error ";
189   // Subtract 2 to account for terminating null characters in SEP and ERROR_STR.
190   std::size_t error_code_size = sizeof(SEP) + sizeof(ERROR_STR) - 2;
191   typedef internal::IntTraits<int>::MainType MainType;
192   MainType abs_value = static_cast<MainType>(error_code);
193   if (internal::is_negative(error_code)) {
194     abs_value = 0 - abs_value;
195     ++error_code_size;
196   }
197   error_code_size += internal::count_digits(abs_value);
198   if (message.size() <= internal::INLINE_BUFFER_SIZE - error_code_size)
199     out << message << SEP;
200   out << ERROR_STR << error_code;
201   assert(out.size() <= internal::INLINE_BUFFER_SIZE);
202 }
203 
report_error(FormatFunc func,int error_code,StringRef message)204 void report_error(FormatFunc func, int error_code,
205                   StringRef message) FMT_NOEXCEPT {
206   MemoryWriter full_message;
207   func(full_message, error_code, message);
208   // Use Writer::data instead of Writer::c_str to avoid potential memory
209   // allocation.
210   std::fwrite(full_message.data(), full_message.size(), 1, stderr);
211   std::fputc('\n', stderr);
212 }
213 }  // namespace
214 
215 namespace internal {
216 
217 // This method is used to preserve binary compatibility with fmt 3.0.
218 // It can be removed in 4.0.
format_system_error(Writer & out,int error_code,StringRef message)219 FMT_FUNC void format_system_error(
220   Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
221   fmt::format_system_error(out, error_code, message);
222 }
223 }  // namespace internal
224 
init(int err_code,CStringRef format_str,ArgList args)225 FMT_FUNC void SystemError::init(
226     int err_code, CStringRef format_str, ArgList args) {
227   error_code_ = err_code;
228   MemoryWriter w;
229   format_system_error(w, err_code, format(format_str, args));
230   std::runtime_error &base = *this;
231   base = std::runtime_error(w.str());
232 }
233 
234 template <typename T>
format_float(char * buffer,std::size_t size,const char * format,unsigned width,int precision,T value)235 int internal::CharTraits<char>::format_float(
236     char *buffer, std::size_t size, const char *format,
237     unsigned width, int precision, T value) {
238   if (width == 0) {
239     return precision < 0 ?
240         FMT_SNPRINTF(buffer, size, format, value) :
241         FMT_SNPRINTF(buffer, size, format, precision, value);
242   }
243   return precision < 0 ?
244       FMT_SNPRINTF(buffer, size, format, width, value) :
245       FMT_SNPRINTF(buffer, size, format, width, precision, value);
246 }
247 
248 template <typename T>
format_float(wchar_t * buffer,std::size_t size,const wchar_t * format,unsigned width,int precision,T value)249 int internal::CharTraits<wchar_t>::format_float(
250     wchar_t *buffer, std::size_t size, const wchar_t *format,
251     unsigned width, int precision, T value) {
252   if (width == 0) {
253     return precision < 0 ?
254         FMT_SWPRINTF(buffer, size, format, value) :
255         FMT_SWPRINTF(buffer, size, format, precision, value);
256   }
257   return precision < 0 ?
258       FMT_SWPRINTF(buffer, size, format, width, value) :
259       FMT_SWPRINTF(buffer, size, format, width, precision, value);
260 }
261 
262 template <typename T>
263 const char internal::BasicData<T>::DIGITS[] =
264     "0001020304050607080910111213141516171819"
265     "2021222324252627282930313233343536373839"
266     "4041424344454647484950515253545556575859"
267     "6061626364656667686970717273747576777879"
268     "8081828384858687888990919293949596979899";
269 
270 #define FMT_POWERS_OF_10(factor) \
271   factor * 10, \
272   factor * 100, \
273   factor * 1000, \
274   factor * 10000, \
275   factor * 100000, \
276   factor * 1000000, \
277   factor * 10000000, \
278   factor * 100000000, \
279   factor * 1000000000
280 
281 template <typename T>
282 const uint32_t internal::BasicData<T>::POWERS_OF_10_32[] = {
283   0, FMT_POWERS_OF_10(1)
284 };
285 
286 template <typename T>
287 const uint64_t internal::BasicData<T>::POWERS_OF_10_64[] = {
288   0,
289   FMT_POWERS_OF_10(1),
290   FMT_POWERS_OF_10(ULongLong(1000000000)),
291   // Multiply several constants instead of using a single long long constant
292   // to avoid warnings about C++98 not supporting long long.
293   ULongLong(1000000000) * ULongLong(1000000000) * 10
294 };
295 
report_unknown_type(char code,const char * type)296 FMT_FUNC void internal::report_unknown_type(char code, const char *type) {
297   (void)type;
298   if (std::isprint(static_cast<unsigned char>(code))) {
299     FMT_THROW(FormatError(
300         format("unknown format code '{}' for {}", code, type)));
301   }
302   FMT_THROW(FormatError(
303       format("unknown format code '\\x{:02x}' for {}",
304         static_cast<unsigned>(code), type)));
305 }
306 
307 #if FMT_USE_WINDOWS_H
308 
UTF8ToUTF16(StringRef s)309 FMT_FUNC internal::UTF8ToUTF16::UTF8ToUTF16(StringRef s) {
310   static const char ERROR_MSG[] = "cannot convert string from UTF-8 to UTF-16";
311   if (s.size() > INT_MAX)
312     FMT_THROW(WindowsError(ERROR_INVALID_PARAMETER, ERROR_MSG));
313   int s_size = static_cast<int>(s.size());
314   int length = MultiByteToWideChar(
315       CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, FMT_NULL, 0);
316   if (length == 0)
317     FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
318   buffer_.resize(length + 1);
319   length = MultiByteToWideChar(
320     CP_UTF8, MB_ERR_INVALID_CHARS, s.data(), s_size, &buffer_[0], length);
321   if (length == 0)
322     FMT_THROW(WindowsError(GetLastError(), ERROR_MSG));
323   buffer_[length] = 0;
324 }
325 
UTF16ToUTF8(WStringRef s)326 FMT_FUNC internal::UTF16ToUTF8::UTF16ToUTF8(WStringRef s) {
327   if (int error_code = convert(s)) {
328     FMT_THROW(WindowsError(error_code,
329         "cannot convert string from UTF-16 to UTF-8"));
330   }
331 }
332 
convert(WStringRef s)333 FMT_FUNC int internal::UTF16ToUTF8::convert(WStringRef s) {
334   if (s.size() > INT_MAX)
335     return ERROR_INVALID_PARAMETER;
336   int s_size = static_cast<int>(s.size());
337   int length = WideCharToMultiByte(
338     CP_UTF8, 0, s.data(), s_size, FMT_NULL, 0, FMT_NULL, FMT_NULL);
339   if (length == 0)
340     return GetLastError();
341   buffer_.resize(length + 1);
342   length = WideCharToMultiByte(
343     CP_UTF8, 0, s.data(), s_size, &buffer_[0], length, FMT_NULL, FMT_NULL);
344   if (length == 0)
345     return GetLastError();
346   buffer_[length] = 0;
347   return 0;
348 }
349 
init(int err_code,CStringRef format_str,ArgList args)350 FMT_FUNC void WindowsError::init(
351     int err_code, CStringRef format_str, ArgList args) {
352   error_code_ = err_code;
353   MemoryWriter w;
354   internal::format_windows_error(w, err_code, format(format_str, args));
355   std::runtime_error &base = *this;
356   base = std::runtime_error(w.str());
357 }
358 
format_windows_error(Writer & out,int error_code,StringRef message)359 FMT_FUNC void internal::format_windows_error(
360     Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
361   FMT_TRY {
362     MemoryBuffer<wchar_t, INLINE_BUFFER_SIZE> buffer;
363     buffer.resize(INLINE_BUFFER_SIZE);
364     for (;;) {
365       wchar_t *system_message = &buffer[0];
366       int result = FormatMessageW(
367         FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
368         FMT_NULL, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
369         system_message, static_cast<uint32_t>(buffer.size()), FMT_NULL);
370       if (result != 0) {
371         UTF16ToUTF8 utf8_message;
372         if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
373           out << message << ": " << utf8_message;
374           return;
375         }
376         break;
377       }
378       if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
379         break;  // Can't get error message, report error code instead.
380       buffer.resize(buffer.size() * 2);
381     }
382   } FMT_CATCH(...) {}
383   fmt::format_error_code(out, error_code, message);  // 'fmt::' is for bcc32.
384 }
385 
386 #endif  // FMT_USE_WINDOWS_H
387 
format_system_error(Writer & out,int error_code,StringRef message)388 FMT_FUNC void format_system_error(
389     Writer &out, int error_code, StringRef message) FMT_NOEXCEPT {
390   FMT_TRY {
391     internal::MemoryBuffer<char, internal::INLINE_BUFFER_SIZE> buffer;
392     buffer.resize(internal::INLINE_BUFFER_SIZE);
393     for (;;) {
394       char *system_message = &buffer[0];
395       int result = safe_strerror(error_code, system_message, buffer.size());
396       if (result == 0) {
397         out << message << ": " << system_message;
398         return;
399       }
400       if (result != ERANGE)
401         break;  // Can't get error message, report error code instead.
402       buffer.resize(buffer.size() * 2);
403     }
404   } FMT_CATCH(...) {}
405   fmt::format_error_code(out, error_code, message);  // 'fmt::' is for bcc32.
406 }
407 
408 template <typename Char>
init(const ArgList & args)409 void internal::ArgMap<Char>::init(const ArgList &args) {
410   if (!map_.empty())
411     return;
412   typedef internal::NamedArg<Char> NamedArg;
413   const NamedArg *named_arg = FMT_NULL;
414   bool use_values =
415       args.type(ArgList::MAX_PACKED_ARGS - 1) == internal::Arg::NONE;
416   if (use_values) {
417     for (unsigned i = 0;/*nothing*/; ++i) {
418       internal::Arg::Type arg_type = args.type(i);
419       switch (arg_type) {
420       case internal::Arg::NONE:
421         return;
422       case internal::Arg::NAMED_ARG:
423         named_arg = static_cast<const NamedArg*>(args.values_[i].pointer);
424         map_.push_back(Pair(named_arg->name, *named_arg));
425         break;
426       default:
427         /*nothing*/;
428       }
429     }
430     return;
431   }
432   for (unsigned i = 0; i != ArgList::MAX_PACKED_ARGS; ++i) {
433     internal::Arg::Type arg_type = args.type(i);
434     if (arg_type == internal::Arg::NAMED_ARG) {
435       named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
436       map_.push_back(Pair(named_arg->name, *named_arg));
437     }
438   }
439   for (unsigned i = ArgList::MAX_PACKED_ARGS;/*nothing*/; ++i) {
440     switch (args.args_[i].type) {
441     case internal::Arg::NONE:
442       return;
443     case internal::Arg::NAMED_ARG:
444       named_arg = static_cast<const NamedArg*>(args.args_[i].pointer);
445       map_.push_back(Pair(named_arg->name, *named_arg));
446       break;
447     default:
448       /*nothing*/;
449     }
450   }
451 }
452 
453 template <typename Char>
grow(std::size_t)454 void internal::FixedBuffer<Char>::grow(std::size_t) {
455   FMT_THROW(std::runtime_error("buffer overflow"));
456 }
457 
do_get_arg(unsigned arg_index,const char * & error)458 FMT_FUNC Arg internal::FormatterBase::do_get_arg(
459     unsigned arg_index, const char *&error) {
460   Arg arg = args_[arg_index];
461   switch (arg.type) {
462   case Arg::NONE:
463     error = "argument index out of range";
464     break;
465   case Arg::NAMED_ARG:
466     arg = *static_cast<const internal::Arg*>(arg.pointer);
467     break;
468   default:
469     /*nothing*/;
470   }
471   return arg;
472 }
473 
report_system_error(int error_code,fmt::StringRef message)474 FMT_FUNC void report_system_error(
475     int error_code, fmt::StringRef message) FMT_NOEXCEPT {
476   // 'fmt::' is for bcc32.
477   report_error(format_system_error, error_code, message);
478 }
479 
480 #if FMT_USE_WINDOWS_H
report_windows_error(int error_code,fmt::StringRef message)481 FMT_FUNC void report_windows_error(
482     int error_code, fmt::StringRef message) FMT_NOEXCEPT {
483   // 'fmt::' is for bcc32.
484   report_error(internal::format_windows_error, error_code, message);
485 }
486 #endif
487 
print(std::FILE * f,CStringRef format_str,ArgList args)488 FMT_FUNC void print(std::FILE *f, CStringRef format_str, ArgList args) {
489   MemoryWriter w;
490   w.write(format_str, args);
491   std::fwrite(w.data(), 1, w.size(), f);
492 }
493 
print(CStringRef format_str,ArgList args)494 FMT_FUNC void print(CStringRef format_str, ArgList args) {
495   print(stdout, format_str, args);
496 }
497 
print_colored(Color c,CStringRef format,ArgList args)498 FMT_FUNC void print_colored(Color c, CStringRef format, ArgList args) {
499   char escape[] = "\x1b[30m";
500   escape[3] = static_cast<char>('0' + c);
501   std::fputs(escape, stdout);
502   print(format, args);
503   std::fputs(RESET_COLOR, stdout);
504 }
505 
506 #ifndef FMT_HEADER_ONLY
507 
508 template struct internal::BasicData<void>;
509 
510 // Explicit instantiations for char.
511 
512 template void internal::FixedBuffer<char>::grow(std::size_t);
513 
514 template void internal::ArgMap<char>::init(const ArgList &args);
515 
516 template int internal::CharTraits<char>::format_float(
517     char *buffer, std::size_t size, const char *format,
518     unsigned width, int precision, double value);
519 
520 template int internal::CharTraits<char>::format_float(
521     char *buffer, std::size_t size, const char *format,
522     unsigned width, int precision, long double value);
523 
524 // Explicit instantiations for wchar_t.
525 
526 template void internal::FixedBuffer<wchar_t>::grow(std::size_t);
527 
528 template void internal::ArgMap<wchar_t>::init(const ArgList &args);
529 
530 template int internal::CharTraits<wchar_t>::format_float(
531     wchar_t *buffer, std::size_t size, const wchar_t *format,
532     unsigned width, int precision, double value);
533 
534 template int internal::CharTraits<wchar_t>::format_float(
535     wchar_t *buffer, std::size_t size, const wchar_t *format,
536     unsigned width, int precision, long double value);
537 
538 #endif  // FMT_HEADER_ONLY
539 
540 }  // namespace fmt
541 
542 #ifdef _MSC_VER
543 # pragma warning(pop)
544 #endif
545