blob: ce03ab5e0eaddbdb437b63fa809d0146fd998a8d [file] [log] [blame]
Victor Zverovichb076df42012-12-07 08:31:09 -08001/*
Victor Zverovichfaccb4c2012-12-12 07:44:41 -08002 String formatting library for C++
3
4 Copyright (c) 2012, 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.
Victor Zverovichb076df42012-12-07 08:31:09 -080026 */
27
Victor Zverovichd53f2092012-12-16 15:06:31 -080028// Disable useless MSVC warnings.
Victor Zverovichf8c91062012-12-17 15:41:00 -080029#undef _CRT_SECURE_NO_WARNINGS
Victor Zverovichd53f2092012-12-16 15:06:31 -080030#define _CRT_SECURE_NO_WARNINGS
Victor Zverovichc240a122012-12-21 15:02:25 -080031#undef _SCL_SECURE_NO_WARNINGS
32#define _SCL_SECURE_NO_WARNINGS
Victor Zverovichd53f2092012-12-16 15:06:31 -080033
Victor Zverovichfbfedcf2013-01-14 15:16:20 -080034#include "format.h"
35
Victor Zverovich859a4972014-04-30 06:55:21 -070036#include <string.h>
37
Victor Zverovich72f896d2012-12-12 09:17:28 -080038#include <cctype>
Victor Zverovichf28645f2014-04-24 12:37:06 -070039#include <climits>
Victor Zverovich9ff3b972013-09-07 10:15:08 -070040#include <cmath>
Victor Zverovicha684d0c2013-12-27 08:00:10 -080041#include <cstdarg>
Victor Zverovich9ff3b972013-09-07 10:15:08 -070042
Victor Zverovich859a4972014-04-30 06:55:21 -070043#ifdef _WIN32
Victor Zverovichdcd039d2014-05-01 07:09:42 -070044# define WIN32_LEAN_AND_MEAN
Victor Zverovich859a4972014-04-30 06:55:21 -070045# include <windows.h>
Victor Zveroviched2bdba2014-04-30 07:41:54 -070046# undef ERROR
Victor Zverovich859a4972014-04-30 06:55:21 -070047#endif
48
Victor Zverovich447e02c2014-02-15 10:48:34 -080049using fmt::ULongLong;
50
jdale88a9862fd2014-03-11 18:56:24 +000051#if _MSC_VER
52# pragma warning(push)
53# pragma warning(disable: 4127) // conditional expression is constant
54#endif
55
Victor Zverovich9ff3b972013-09-07 10:15:08 -070056namespace {
57
58#ifndef _MSC_VER
59
60inline int SignBit(double value) {
61 // When compiled in C++11 mode signbit is no longer a macro but a function
62 // defined in namespace std and the macro is undefined.
Victor Zverovichf2e06802014-04-10 10:49:55 -070063#ifdef signbit
Victor Zverovich9ff3b972013-09-07 10:15:08 -070064 return signbit(value);
Victor Zverovichf2e06802014-04-10 10:49:55 -070065#else
66 return std::signbit(value);
67#endif
Victor Zverovich9ff3b972013-09-07 10:15:08 -070068}
69
70inline int IsInf(double x) {
71#ifdef isinf
72 return isinf(x);
73#else
74 return std::isinf(x);
75#endif
76}
77
78#define FMT_SNPRINTF snprintf
79
Victor Zverovicha684d0c2013-12-27 08:00:10 -080080#else // _MSC_VER
Victor Zverovich9ff3b972013-09-07 10:15:08 -070081
82inline int SignBit(double value) {
83 if (value < 0) return 1;
84 if (value == value) return 0;
85 int dec = 0, sign = 0;
86 char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail.
87 _ecvt_s(buffer, sizeof(buffer), value, 0, &dec, &sign);
88 return sign;
89}
90
91inline int IsInf(double x) { return !_finite(x); }
92
Victor Zverovicha684d0c2013-12-27 08:00:10 -080093inline int FMT_SNPRINTF(char *buffer, size_t size, const char *format, ...) {
94 va_list args;
95 va_start(args, format);
96 int result = vsnprintf_s(buffer, size, _TRUNCATE, format, args);
97 va_end(args);
98 return result;
99}
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700100
101#endif // _MSC_VER
Victor Zverovich43fe1002014-02-19 14:20:26 -0800102
103const char RESET_COLOR[] = "\x1b[0m";
Victor Zverovichb605b392013-09-09 22:21:40 -0700104}
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700105
Victor Zverovichb605b392013-09-09 22:21:40 -0700106template <typename T>
107int fmt::internal::CharTraits<char>::FormatFloat(
108 char *buffer, std::size_t size, const char *format,
109 unsigned width, int precision, T value) {
110 if (width == 0) {
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700111 return precision < 0 ?
Victor Zverovichb605b392013-09-09 22:21:40 -0700112 FMT_SNPRINTF(buffer, size, format, value) :
113 FMT_SNPRINTF(buffer, size, format, precision, value);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700114 }
Victor Zverovichb605b392013-09-09 22:21:40 -0700115 return precision < 0 ?
116 FMT_SNPRINTF(buffer, size, format, width, value) :
117 FMT_SNPRINTF(buffer, size, format, width, precision, value);
118}
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700119
Victor Zverovichb605b392013-09-09 22:21:40 -0700120template <typename T>
121int fmt::internal::CharTraits<wchar_t>::FormatFloat(
122 wchar_t *buffer, std::size_t size, const wchar_t *format,
123 unsigned width, int precision, T value) {
124 if (width == 0) {
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700125 return precision < 0 ?
Victor Zverovichb605b392013-09-09 22:21:40 -0700126 swprintf(buffer, size, format, value) :
127 swprintf(buffer, size, format, precision, value);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700128 }
Victor Zverovichb605b392013-09-09 22:21:40 -0700129 return precision < 0 ?
130 swprintf(buffer, size, format, width, value) :
131 swprintf(buffer, size, format, width, precision, value);
Victor Zverovich65d47e52013-09-09 06:51:03 -0700132}
Victor Zveroviche8ba9602012-12-12 09:29:50 -0800133
Victor Zverovich65d47e52013-09-09 06:51:03 -0700134const char fmt::internal::DIGITS[] =
Victor Zverovich687301c2013-01-26 16:07:28 -0800135 "0001020304050607080910111213141516171819"
136 "2021222324252627282930313233343536373839"
137 "4041424344454647484950515253545556575859"
138 "6061626364656667686970717273747576777879"
139 "8081828384858687888990919293949596979899";
Victor Zveroviche9b21912014-02-19 12:43:55 -0800140
Victor Zverovichf1d85162014-02-19 13:02:22 -0800141#define FMT_POWERS_OF_10(factor) \
142 factor * 10, \
143 factor * 100, \
144 factor * 1000, \
145 factor * 10000, \
146 factor * 100000, \
147 factor * 1000000, \
148 factor * 10000000, \
149 factor * 100000000, \
150 factor * 1000000000
Victor Zveroviche9b21912014-02-19 12:43:55 -0800151
Victor Zverovichf1d85162014-02-19 13:02:22 -0800152const uint32_t fmt::internal::POWERS_OF_10_32[] = {0, FMT_POWERS_OF_10(1)};
Victor Zveroviche9b21912014-02-19 12:43:55 -0800153const uint64_t fmt::internal::POWERS_OF_10_64[] = {
Victor Zverovich6f0387f2014-02-14 10:36:17 -0800154 0,
Victor Zverovichf1d85162014-02-19 13:02:22 -0800155 FMT_POWERS_OF_10(1),
156 FMT_POWERS_OF_10(ULongLong(1000000000)),
157 // Multiply several constants instead of using a single long long constants
158 // to avoid warnings about C++98 not supporting long long.
Victor Zverovich6f6fe512014-02-15 11:16:44 -0800159 ULongLong(1000000000) * ULongLong(1000000000) * 10
Victor Zverovich6f0387f2014-02-14 10:36:17 -0800160};
Victor Zverovich877abaf2013-01-08 09:56:05 -0800161
Victor Zverovich687301c2013-01-26 16:07:28 -0800162void fmt::internal::ReportUnknownType(char code, const char *type) {
Victor Zverovich877abaf2013-01-08 09:56:05 -0800163 if (std::isprint(static_cast<unsigned char>(code))) {
164 throw fmt::FormatError(fmt::str(
Victor Zverovich687301c2013-01-26 16:07:28 -0800165 fmt::Format("unknown format code '{}' for {}") << code << type));
Victor Zverovich877abaf2013-01-08 09:56:05 -0800166 }
167 throw fmt::FormatError(
Victor Zverovich687301c2013-01-26 16:07:28 -0800168 fmt::str(fmt::Format("unknown format code '\\x{:02x}' for {}")
Victor Zverovich877abaf2013-01-08 09:56:05 -0800169 << static_cast<unsigned>(code) << type));
170}
Victor Zverovich7cae7632013-09-06 20:23:42 -0700171
Victor Zverovichda9aeab2014-04-30 07:23:43 -0700172#ifdef _WIN32
173
174fmt::internal::UTF8ToUTF16::UTF8ToUTF16(fmt::StringRef s) {
175 int length = MultiByteToWideChar(
176 CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, 0, 0);
177 static const char ERROR[] = "cannot convert string from UTF-8 to UTF-16";
178 if (length == 0)
Victor Zverovich9830c522014-05-01 07:20:38 -0700179 ThrowWinError(GetLastError(), ERROR);
Victor Zverovichda9aeab2014-04-30 07:23:43 -0700180 buffer_.resize(length);
181 length = MultiByteToWideChar(
182 CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(), -1, &buffer_[0], length);
183 if (length == 0)
Victor Zverovich9830c522014-05-01 07:20:38 -0700184 ThrowWinError(GetLastError(), ERROR);
Victor Zverovichda9aeab2014-04-30 07:23:43 -0700185}
186
187fmt::internal::UTF16ToUTF8::UTF16ToUTF8(fmt::WStringRef s) {
188 if (int error_code = Convert(s)) {
Victor Zverovich42764e52014-05-01 07:09:08 -0700189 ThrowWinError(GetLastError(),
Victor Zverovichda9aeab2014-04-30 07:23:43 -0700190 "cannot convert string from UTF-16 to UTF-8");
191 }
192}
193
194int fmt::internal::UTF16ToUTF8::Convert(fmt::WStringRef s) {
195 int length = WideCharToMultiByte(CP_UTF8, 0, s.c_str(), -1, 0, 0, 0, 0);
196 if (length == 0)
197 return GetLastError();
198 buffer_.resize(length);
199 length = WideCharToMultiByte(
200 CP_UTF8, 0, s.c_str(), -1, &buffer_[0], length, 0, 0);
201 if (length == 0)
202 return GetLastError();
203 return 0;
204}
205
206#endif
207
Victor Zverovich99e61122014-04-30 11:20:41 -0700208int fmt::internal::StrError(
209 int error_code, char *&buffer, std::size_t buffer_size) {
Victor Zverovich2c6372d2014-04-30 09:42:48 -0700210 assert(buffer != 0 && buffer_size != 0);
Victor Zverovich99e61122014-04-30 11:20:41 -0700211 int result = 0;
Victor Zverovich2c6372d2014-04-30 09:42:48 -0700212#ifdef _GNU_SOURCE
213 char *message = strerror_r(error_code, buffer, buffer_size);
Victor Zverovich99e61122014-04-30 11:20:41 -0700214 // If the buffer is full then the message is probably truncated.
Victor Zverovich2c6372d2014-04-30 09:42:48 -0700215 if (message == buffer && strlen(buffer) == buffer_size - 1)
Victor Zverovich99e61122014-04-30 11:20:41 -0700216 result = ERANGE;
217 buffer = message;
Victor Zverovich2c6372d2014-04-30 09:42:48 -0700218#elif _WIN32
Victor Zverovich99e61122014-04-30 11:20:41 -0700219 result = strerror_s(buffer, buffer_size, error_code);
220 // If the buffer is full then the message is probably truncated.
221 if (result == 0 && std::strlen(buffer) == buffer_size - 1)
222 result = ERANGE;
Victor Zverovich2c6372d2014-04-30 09:42:48 -0700223#else
Victor Zverovich99e61122014-04-30 11:20:41 -0700224 result = strerror_r(error_code, buffer, buffer_size);
225 if (result == -1)
226 result = errno; // glibc versions before 2.13 return result in errno.
Victor Zverovich2c6372d2014-04-30 09:42:48 -0700227#endif
Victor Zverovich99e61122014-04-30 11:20:41 -0700228 return result;
Victor Zverovich859a4972014-04-30 06:55:21 -0700229}
Victor Zverovich7cae7632013-09-06 20:23:42 -0700230
Victor Zverovich53b4c312014-04-30 15:00:41 -0700231void fmt::internal::FormatSystemErrorMessage(
232 fmt::Writer &out, int error_code, fmt::StringRef message) {
233 Array<char, INLINE_BUFFER_SIZE> buffer;
234 buffer.resize(INLINE_BUFFER_SIZE);
235 char *system_message = 0;
236 for (;;) {
237 system_message = &buffer[0];
238 int result = StrError(error_code, system_message, buffer.size());
239 if (result == 0)
240 break;
241 if (result != ERANGE) {
242 // Can't get error message, report error code instead.
243 out << message << ": error code = " << error_code;
244 return;
245 }
246 buffer.resize(buffer.size() * 2);
247 }
248 out << message << ": " << system_message;
249}
250
251#ifdef _WIN32
252void fmt::internal::FormatWinErrorMessage(
253 fmt::Writer &out, int error_code, fmt::StringRef message) {
254 class String {
255 private:
256 LPWSTR str_;
257
258 public:
259 String() : str_() {}
260 ~String() { LocalFree(str_); }
261 LPWSTR *ptr() { return &str_; }
262 LPCWSTR c_str() const { return str_; }
263 };
264 String system_message;
265 if (FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER |
266 FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 0,
267 error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
268 reinterpret_cast<LPWSTR>(system_message.ptr()), 0, 0)) {
269 UTF16ToUTF8 utf8_message;
270 if (!utf8_message.Convert(system_message.c_str())) {
271 out << message << ": " << c_str(utf8_message);
272 return;
273 }
274 }
275 // Can't get error message, report error code instead.
276 out << message << ": error code = " << error_code;
277}
278#endif
279
Victor Zverovich7cae7632013-09-06 20:23:42 -0700280// Fills the padding around the content and returns the pointer to the
281// content area.
282template <typename Char>
Victor Zverovich93e41252013-09-08 13:07:04 -0700283typename fmt::BasicWriter<Char>::CharPtr
284 fmt::BasicWriter<Char>::FillPadding(CharPtr buffer,
285 unsigned total_size, std::size_t content_size, wchar_t fill) {
Victor Zverovich7cae7632013-09-06 20:23:42 -0700286 std::size_t padding = total_size - content_size;
287 std::size_t left_padding = padding / 2;
Victor Zverovich563a5752013-09-08 13:47:06 -0700288 Char fill_char = static_cast<Char>(fill);
289 std::fill_n(buffer, left_padding, fill_char);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700290 buffer += left_padding;
291 CharPtr content = buffer;
Victor Zverovich563a5752013-09-08 13:47:06 -0700292 std::fill_n(buffer + content_size, padding - left_padding, fill_char);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700293 return content;
294}
295
296template <typename Char>
Victor Zverovich7cae7632013-09-06 20:23:42 -0700297typename fmt::BasicWriter<Char>::CharPtr
298 fmt::BasicWriter<Char>::PrepareFilledBuffer(
Victor Zverovichea5dce32014-01-28 12:47:37 -0800299 unsigned size, const AlignSpec &spec, char sign) {
Victor Zverovich7cae7632013-09-06 20:23:42 -0700300 unsigned width = spec.width();
301 if (width <= size) {
302 CharPtr p = GrowBuffer(size);
303 *p = sign;
304 return p + size - 1;
305 }
306 CharPtr p = GrowBuffer(width);
307 CharPtr end = p + width;
308 Alignment align = spec.align();
Victor Zverovicha1bd3352014-01-08 08:17:38 -0800309 // TODO: error if fill is not convertible to Char
Victor Zverovich563a5752013-09-08 13:47:06 -0700310 Char fill = static_cast<Char>(spec.fill());
Victor Zverovich7cae7632013-09-06 20:23:42 -0700311 if (align == ALIGN_LEFT) {
312 *p = sign;
313 p += size;
Victor Zverovich563a5752013-09-08 13:47:06 -0700314 std::fill(p, end, fill);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700315 } else if (align == ALIGN_CENTER) {
Victor Zverovich563a5752013-09-08 13:47:06 -0700316 p = FillPadding(p, width, size, fill);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700317 *p = sign;
318 p += size;
319 } else {
320 if (align == ALIGN_NUMERIC) {
321 if (sign) {
322 *p++ = sign;
323 --size;
324 }
325 } else {
326 *(end - size) = sign;
327 }
Victor Zverovich563a5752013-09-08 13:47:06 -0700328 std::fill(p, end - size, fill);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700329 p = end;
330 }
331 return p - 1;
332}
333
334template <typename Char>
335template <typename T>
336void fmt::BasicWriter<Char>::FormatDouble(
Victor Zverovichea5dce32014-01-28 12:47:37 -0800337 T value, const FormatSpec &spec, int precision) {
Victor Zverovich7cae7632013-09-06 20:23:42 -0700338 // Check type.
339 char type = spec.type();
340 bool upper = false;
341 switch (type) {
342 case 0:
343 type = 'g';
344 break;
345 case 'e': case 'f': case 'g':
346 break;
347 case 'F':
348#ifdef _MSC_VER
349 // MSVC's printf doesn't support 'F'.
350 type = 'f';
351#endif
352 // Fall through.
353 case 'E': case 'G':
354 upper = true;
355 break;
356 default:
357 internal::ReportUnknownType(type, "double");
358 break;
359 }
360
361 char sign = 0;
362 // Use SignBit instead of value < 0 because the latter is always
363 // false for NaN.
jdale88a9862fd2014-03-11 18:56:24 +0000364 if (SignBit(static_cast<double>(value))) {
Victor Zverovich7cae7632013-09-06 20:23:42 -0700365 sign = '-';
366 value = -value;
367 } else if (spec.sign_flag()) {
368 sign = spec.plus_flag() ? '+' : ' ';
369 }
370
371 if (value != value) {
372 // Format NaN ourselves because sprintf's output is not consistent
373 // across platforms.
374 std::size_t size = 4;
375 const char *nan = upper ? " NAN" : " nan";
376 if (!sign) {
377 --size;
378 ++nan;
379 }
380 CharPtr out = FormatString(nan, size, spec);
381 if (sign)
382 *out = sign;
383 return;
384 }
385
jdale88a9862fd2014-03-11 18:56:24 +0000386 if (IsInf(static_cast<double>(value))) {
Victor Zverovich7cae7632013-09-06 20:23:42 -0700387 // Format infinity ourselves because sprintf's output is not consistent
388 // across platforms.
389 std::size_t size = 4;
390 const char *inf = upper ? " INF" : " inf";
391 if (!sign) {
392 --size;
393 ++inf;
394 }
395 CharPtr out = FormatString(inf, size, spec);
396 if (sign)
397 *out = sign;
398 return;
399 }
400
401 std::size_t offset = buffer_.size();
402 unsigned width = spec.width();
403 if (sign) {
404 buffer_.reserve(buffer_.size() + (std::max)(width, 1u));
405 if (width > 0)
406 --width;
407 ++offset;
408 }
409
410 // Build format string.
411 enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
412 Char format[MAX_FORMAT_SIZE];
413 Char *format_ptr = format;
414 *format_ptr++ = '%';
415 unsigned width_for_sprintf = width;
416 if (spec.hash_flag())
417 *format_ptr++ = '#';
418 if (spec.align() == ALIGN_CENTER) {
419 width_for_sprintf = 0;
420 } else {
421 if (spec.align() == ALIGN_LEFT)
422 *format_ptr++ = '-';
423 if (width != 0)
424 *format_ptr++ = '*';
425 }
426 if (precision >= 0) {
427 *format_ptr++ = '.';
428 *format_ptr++ = '*';
429 }
430 if (internal::IsLongDouble<T>::VALUE)
431 *format_ptr++ = 'L';
432 *format_ptr++ = type;
433 *format_ptr = '\0';
434
435 // Format using snprintf.
Victor Zverovich88972f42013-09-08 13:30:14 -0700436 Char fill = static_cast<Char>(spec.fill());
Victor Zverovich7cae7632013-09-06 20:23:42 -0700437 for (;;) {
438 std::size_t size = buffer_.capacity() - offset;
Victor Zverovich33baa8f2014-04-23 18:37:08 -0700439#if _MSC_VER
440 // MSVC's vsnprintf_s doesn't work with zero size, so reserve
441 // space for at least one extra character to make the size non-zero.
442 // Note that the buffer's capacity will increase by more than 1.
443 if (size == 0) {
444 buffer_.reserve(offset + 1);
445 size = buffer_.capacity() - offset;
446 }
447#endif
Victor Zverovich7cae7632013-09-06 20:23:42 -0700448 Char *start = &buffer_[offset];
Victor Zverovichb605b392013-09-09 22:21:40 -0700449 int n = internal::CharTraits<Char>::FormatFloat(
Victor Zverovich7cae7632013-09-06 20:23:42 -0700450 start, size, format, width_for_sprintf, precision, value);
451 if (n >= 0 && offset + n < buffer_.capacity()) {
452 if (sign) {
453 if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) ||
454 *start != ' ') {
455 *(start - 1) = sign;
456 sign = 0;
457 } else {
Victor Zverovich88972f42013-09-08 13:30:14 -0700458 *(start - 1) = fill;
Victor Zverovich7cae7632013-09-06 20:23:42 -0700459 }
460 ++n;
461 }
462 if (spec.align() == ALIGN_CENTER &&
463 spec.width() > static_cast<unsigned>(n)) {
464 unsigned width = spec.width();
465 CharPtr p = GrowBuffer(width);
466 std::copy(p, p + n, p + (width - n) / 2);
Victor Zverovich88972f42013-09-08 13:30:14 -0700467 FillPadding(p, spec.width(), n, fill);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700468 return;
469 }
470 if (spec.fill() != ' ' || sign) {
471 while (*start == ' ')
Victor Zverovich88972f42013-09-08 13:30:14 -0700472 *start++ = fill;
Victor Zverovich7cae7632013-09-06 20:23:42 -0700473 if (sign)
474 *(start - 1) = sign;
475 }
476 GrowBuffer(n);
477 return;
478 }
Victor Zverovichcfeba452014-04-23 18:37:49 -0700479 // If n is negative we ask to increase the capacity by at least 1,
480 // but as std::vector, the buffer grows exponentially.
481 buffer_.reserve(n >= 0 ? offset + n + 1 : buffer_.capacity() + 1);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700482 }
483}
484
485// Throws Exception(message) if format contains '}', otherwise throws
486// FormatError reporting unmatched '{'. The idea is that unmatched '{'
487// should override other errors.
488template <typename Char>
Victor Zveroviche78904b2014-04-23 08:27:50 -0700489void fmt::BasicWriter<Char>::FormatParser::ReportError(
Victor Zverovich7cae7632013-09-06 20:23:42 -0700490 const Char *s, StringRef message) const {
491 for (int num_open_braces = num_open_braces_; *s; ++s) {
492 if (*s == '{') {
493 ++num_open_braces;
494 } else if (*s == '}') {
495 if (--num_open_braces == 0)
496 throw fmt::FormatError(message);
497 }
498 }
499 throw fmt::FormatError("unmatched '{' in format");
500}
501
502// Parses an unsigned integer advancing s to the end of the parsed input.
503// This function assumes that the first character of s is a digit.
504template <typename Char>
Victor Zveroviche78904b2014-04-23 08:27:50 -0700505unsigned fmt::BasicWriter<Char>::FormatParser::ParseUInt(const Char *&s) const {
Victor Zverovich7cae7632013-09-06 20:23:42 -0700506 assert('0' <= *s && *s <= '9');
507 unsigned value = 0;
508 do {
509 unsigned new_value = value * 10 + (*s++ - '0');
510 if (new_value < value) // Check if value wrapped around.
511 ReportError(s, "number is too big in format");
512 value = new_value;
513 } while ('0' <= *s && *s <= '9');
514 return value;
515}
516
517template <typename Char>
Victor Zveroviche78904b2014-04-23 08:27:50 -0700518inline const typename fmt::BasicWriter<Char>::ArgInfo
519 &fmt::BasicWriter<Char>::FormatParser::ParseArgIndex(const Char *&s) {
Victor Zverovich7cae7632013-09-06 20:23:42 -0700520 unsigned arg_index = 0;
521 if (*s < '0' || *s > '9') {
522 if (*s != '}' && *s != ':')
523 ReportError(s, "invalid argument index in format string");
524 if (next_arg_index_ < 0) {
525 ReportError(s,
526 "cannot switch from manual to automatic argument indexing");
527 }
528 arg_index = next_arg_index_++;
529 } else {
530 if (next_arg_index_ > 0) {
531 ReportError(s,
532 "cannot switch from automatic to manual argument indexing");
533 }
534 next_arg_index_ = -1;
535 arg_index = ParseUInt(s);
536 }
Victor Zveroviche78904b2014-04-23 08:27:50 -0700537 if (arg_index >= num_args_)
Victor Zverovich7cae7632013-09-06 20:23:42 -0700538 ReportError(s, "argument index is out of range in format");
Victor Zverovich656a8372014-04-22 08:58:54 -0700539 return args_[arg_index];
Victor Zverovich7cae7632013-09-06 20:23:42 -0700540}
541
542template <typename Char>
Victor Zveroviche78904b2014-04-23 08:27:50 -0700543void fmt::BasicWriter<Char>::FormatParser::CheckSign(
544 const Char *&s, const ArgInfo &arg) {
Victor Zverovichcc6af502013-12-10 08:01:08 -0800545 char sign = static_cast<char>(*s);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700546 if (arg.type > LAST_NUMERIC_TYPE) {
547 ReportError(s,
Victor Zveroviche78904b2014-04-23 08:27:50 -0700548 fmt::Format("format specifier '{}' requires numeric argument") << sign);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700549 }
Victor Zverovicha4e72b42013-11-21 09:11:58 -0800550 if (arg.type == UINT || arg.type == ULONG || arg.type == ULONG_LONG) {
Victor Zverovich7cae7632013-09-06 20:23:42 -0700551 ReportError(s,
Victor Zveroviche78904b2014-04-23 08:27:50 -0700552 fmt::Format("format specifier '{}' requires signed argument") << sign);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700553 }
554 ++s;
555}
556
557template <typename Char>
Victor Zveroviche78904b2014-04-23 08:27:50 -0700558void fmt::BasicWriter<Char>::FormatParser::Format(
559 BasicWriter<Char> &writer, BasicStringRef<Char> format,
560 std::size_t num_args, const ArgInfo *args) {
561 const Char *start = format.c_str();
562 num_args_ = num_args;
563 args_ = args;
Victor Zverovich7cae7632013-09-06 20:23:42 -0700564 next_arg_index_ = 0;
565 const Char *s = start;
Victor Zverovich7cae7632013-09-06 20:23:42 -0700566 while (*s) {
Victor Zverovich0fc73162013-09-07 12:52:52 -0700567 Char c = *s++;
Victor Zverovich7cae7632013-09-06 20:23:42 -0700568 if (c != '{' && c != '}') continue;
569 if (*s == c) {
570 writer.buffer_.append(start, s);
571 start = ++s;
572 continue;
573 }
574 if (c == '}')
575 throw FormatError("unmatched '}' in format");
576 num_open_braces_= 1;
577 writer.buffer_.append(start, s - 1);
578
Victor Zverovich656a8372014-04-22 08:58:54 -0700579 const ArgInfo &arg = ParseArgIndex(s);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700580
Victor Zverovichea5dce32014-01-28 12:47:37 -0800581 FormatSpec spec;
Victor Zverovich7cae7632013-09-06 20:23:42 -0700582 int precision = -1;
583 if (*s == ':') {
584 ++s;
585
586 // Parse fill and alignment.
Victor Zverovich0fc73162013-09-07 12:52:52 -0700587 if (Char c = *s) {
Victor Zverovich7cae7632013-09-06 20:23:42 -0700588 const Char *p = s + 1;
589 spec.align_ = ALIGN_DEFAULT;
590 do {
591 switch (*p) {
592 case '<':
593 spec.align_ = ALIGN_LEFT;
594 break;
595 case '>':
596 spec.align_ = ALIGN_RIGHT;
597 break;
598 case '=':
599 spec.align_ = ALIGN_NUMERIC;
600 break;
601 case '^':
602 spec.align_ = ALIGN_CENTER;
603 break;
604 }
605 if (spec.align_ != ALIGN_DEFAULT) {
606 if (p != s) {
607 if (c == '}') break;
608 if (c == '{')
609 ReportError(s, "invalid fill character '{'");
610 s += 2;
611 spec.fill_ = c;
612 } else ++s;
613 if (spec.align_ == ALIGN_NUMERIC && arg.type > LAST_NUMERIC_TYPE)
614 ReportError(s, "format specifier '=' requires numeric argument");
615 break;
616 }
617 } while (--p >= s);
618 }
619
620 // Parse sign.
621 switch (*s) {
622 case '+':
623 CheckSign(s, arg);
624 spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
625 break;
626 case '-':
627 CheckSign(s, arg);
628 break;
629 case ' ':
630 CheckSign(s, arg);
631 spec.flags_ |= SIGN_FLAG;
632 break;
633 }
634
635 if (*s == '#') {
636 if (arg.type > LAST_NUMERIC_TYPE)
637 ReportError(s, "format specifier '#' requires numeric argument");
638 spec.flags_ |= HASH_FLAG;
639 ++s;
640 }
641
642 // Parse width and zero flag.
643 if ('0' <= *s && *s <= '9') {
644 if (*s == '0') {
645 if (arg.type > LAST_NUMERIC_TYPE)
646 ReportError(s, "format specifier '0' requires numeric argument");
647 spec.align_ = ALIGN_NUMERIC;
648 spec.fill_ = '0';
649 }
650 // Zero may be parsed again as a part of the width, but it is simpler
651 // and more efficient than checking if the next char is a digit.
652 unsigned value = ParseUInt(s);
653 if (value > INT_MAX)
654 ReportError(s, "number is too big in format");
655 spec.width_ = value;
656 }
657
658 // Parse precision.
659 if (*s == '.') {
660 ++s;
661 precision = 0;
662 if ('0' <= *s && *s <= '9') {
663 unsigned value = ParseUInt(s);
664 if (value > INT_MAX)
665 ReportError(s, "number is too big in format");
666 precision = value;
667 } else if (*s == '{') {
668 ++s;
669 ++num_open_braces_;
Victor Zverovich656a8372014-04-22 08:58:54 -0700670 const ArgInfo &precision_arg = ParseArgIndex(s);
Victor Zverovichf406a422013-12-06 07:12:38 -0800671 ULongLong value = 0;
Victor Zverovich7cae7632013-09-06 20:23:42 -0700672 switch (precision_arg.type) {
673 case INT:
674 if (precision_arg.int_value < 0)
675 ReportError(s, "negative precision in format");
676 value = precision_arg.int_value;
677 break;
678 case UINT:
679 value = precision_arg.uint_value;
680 break;
681 case LONG:
682 if (precision_arg.long_value < 0)
683 ReportError(s, "negative precision in format");
684 value = precision_arg.long_value;
685 break;
686 case ULONG:
687 value = precision_arg.ulong_value;
688 break;
Victor Zverovich56f12b72013-11-22 07:45:43 -0800689 case LONG_LONG:
690 if (precision_arg.long_long_value < 0)
691 ReportError(s, "negative precision in format");
692 value = precision_arg.long_long_value;
693 break;
Victor Zverovicha4e72b42013-11-21 09:11:58 -0800694 case ULONG_LONG:
Gregory Czajkowskia65542b2013-11-18 22:58:39 -0800695 value = precision_arg.ulong_long_value;
Victor Zverovicha4e72b42013-11-21 09:11:58 -0800696 break;
Victor Zverovich7cae7632013-09-06 20:23:42 -0700697 default:
698 ReportError(s, "precision is not integer");
699 }
700 if (value > INT_MAX)
701 ReportError(s, "number is too big in format");
Gregory Czajkowskia65542b2013-11-18 22:58:39 -0800702 precision = static_cast<int>(value);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700703 if (*s++ != '}')
704 throw FormatError("unmatched '{' in format");
705 --num_open_braces_;
706 } else {
707 ReportError(s, "missing precision in format");
708 }
709 if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) {
710 ReportError(s,
711 "precision specifier requires floating-point argument");
712 }
713 }
714
715 // Parse type.
716 if (*s != '}' && *s)
Victor Zverovich0fc73162013-09-07 12:52:52 -0700717 spec.type_ = static_cast<char>(*s++);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700718 }
719
720 if (*s++ != '}')
721 throw FormatError("unmatched '{' in format");
722 start = s;
723
724 // Format argument.
725 switch (arg.type) {
726 case INT:
Victor Zverovich03f68852014-04-20 08:46:09 -0700727 writer.FormatInt(arg.int_value, spec);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700728 break;
729 case UINT:
Victor Zverovich03f68852014-04-20 08:46:09 -0700730 writer.FormatInt(arg.uint_value, spec);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700731 break;
732 case LONG:
Victor Zverovich03f68852014-04-20 08:46:09 -0700733 writer.FormatInt(arg.long_value, spec);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700734 break;
735 case ULONG:
Victor Zverovich03f68852014-04-20 08:46:09 -0700736 writer.FormatInt(arg.ulong_value, spec);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700737 break;
Victor Zverovich56f12b72013-11-22 07:45:43 -0800738 case LONG_LONG:
Victor Zverovich03f68852014-04-20 08:46:09 -0700739 writer.FormatInt(arg.long_long_value, spec);
Victor Zverovich56f12b72013-11-22 07:45:43 -0800740 break;
Victor Zverovicha4e72b42013-11-21 09:11:58 -0800741 case ULONG_LONG:
Victor Zverovich03f68852014-04-20 08:46:09 -0700742 writer.FormatInt(arg.ulong_long_value, spec);
Victor Zverovicha4e72b42013-11-21 09:11:58 -0800743 break;
Victor Zverovich7cae7632013-09-06 20:23:42 -0700744 case DOUBLE:
745 writer.FormatDouble(arg.double_value, spec, precision);
746 break;
747 case LONG_DOUBLE:
748 writer.FormatDouble(arg.long_double_value, spec, precision);
749 break;
750 case CHAR: {
751 if (spec.type_ && spec.type_ != 'c')
752 internal::ReportUnknownType(spec.type_, "char");
753 typedef typename BasicWriter<Char>::CharPtr CharPtr;
754 CharPtr out = CharPtr();
755 if (spec.width_ > 1) {
Victor Zverovichc62c4752013-09-08 14:25:22 -0700756 Char fill = static_cast<Char>(spec.fill());
Victor Zverovich7cae7632013-09-06 20:23:42 -0700757 out = writer.GrowBuffer(spec.width_);
758 if (spec.align_ == ALIGN_RIGHT) {
Victor Zverovichc62c4752013-09-08 14:25:22 -0700759 std::fill_n(out, spec.width_ - 1, fill);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700760 out += spec.width_ - 1;
761 } else if (spec.align_ == ALIGN_CENTER) {
Victor Zverovichc62c4752013-09-08 14:25:22 -0700762 out = writer.FillPadding(out, spec.width_, 1, fill);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700763 } else {
Victor Zverovichc62c4752013-09-08 14:25:22 -0700764 std::fill_n(out + 1, spec.width_ - 1, fill);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700765 }
766 } else {
767 out = writer.GrowBuffer(1);
768 }
jdale884cabe162014-03-11 19:03:26 +0000769 *out = static_cast<Char>(arg.int_value);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700770 break;
771 }
772 case STRING: {
773 if (spec.type_ && spec.type_ != 's')
774 internal::ReportUnknownType(spec.type_, "string");
775 const Char *str = arg.string.value;
776 std::size_t size = arg.string.size;
777 if (size == 0) {
778 if (!str)
779 throw FormatError("string pointer is null");
780 if (*str)
781 size = std::char_traits<Char>::length(str);
782 }
783 writer.FormatString(str, size, spec);
784 break;
785 }
786 case POINTER:
787 if (spec.type_ && spec.type_ != 'p')
788 internal::ReportUnknownType(spec.type_, "pointer");
789 spec.flags_= HASH_FLAG;
790 spec.type_ = 'x';
Victor Zverovich03f68852014-04-20 08:46:09 -0700791 writer.FormatInt(reinterpret_cast<uintptr_t>(arg.pointer_value), spec);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700792 break;
793 case CUSTOM:
794 if (spec.type_)
795 internal::ReportUnknownType(spec.type_, "object");
796 arg.custom.format(writer, arg.custom.value, spec);
797 break;
798 default:
799 assert(false);
800 break;
801 }
802 }
803 writer.buffer_.append(start, s);
804}
805
Victor Zverovich859a4972014-04-30 06:55:21 -0700806void fmt::SystemErrorSink::operator()(const fmt::Writer &w) const {
807 Writer message;
Victor Zverovich53b4c312014-04-30 15:00:41 -0700808 internal::FormatSystemErrorMessage(message, error_code_, w.c_str());
Victor Zverovich859a4972014-04-30 06:55:21 -0700809 throw SystemError(message.c_str(), error_code_);
810}
811
Victor Zverovich400812a2014-04-30 12:38:17 -0700812#ifdef _WIN32
813void fmt::WinErrorSink::operator()(const Writer &w) const {
Victor Zverovichf7939862014-04-30 10:18:11 -0700814 Writer message;
Victor Zverovich53b4c312014-04-30 15:00:41 -0700815 internal::FormatWinErrorMessage(message, error_code_, w.c_str());
Victor Zverovichf7939862014-04-30 10:18:11 -0700816 throw SystemError(message.c_str(), error_code_);
817}
Victor Zverovich400812a2014-04-30 12:38:17 -0700818#endif
Victor Zverovichf7939862014-04-30 10:18:11 -0700819
Victor Zverovichd9db8982014-04-28 08:59:29 -0700820void fmt::ANSITerminalSink::operator()(
821 const fmt::BasicWriter<char> &w) const {
Victor Zverovich43fe1002014-02-19 14:20:26 -0800822 char escape[] = "\x1b[30m";
jdale88a9862fd2014-03-11 18:56:24 +0000823 escape[3] = '0' + static_cast<char>(color_);
Victor Zverovichd9db8982014-04-28 08:59:29 -0700824 std::fputs(escape, file_);
825 std::fwrite(w.data(), 1, w.size(), file_);
826 std::fputs(RESET_COLOR, file_);
Victor Zverovich43fe1002014-02-19 14:20:26 -0800827}
Victor Zverovich6968ef32014-02-19 13:51:23 -0800828
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700829// Explicit instantiations for char.
830
Victor Zverovich7cae7632013-09-06 20:23:42 -0700831template void fmt::BasicWriter<char>::FormatDouble<double>(
Victor Zverovichea5dce32014-01-28 12:47:37 -0800832 double value, const FormatSpec &spec, int precision);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700833
Victor Zverovich7cae7632013-09-06 20:23:42 -0700834template void fmt::BasicWriter<char>::FormatDouble<long double>(
Victor Zverovichea5dce32014-01-28 12:47:37 -0800835 long double value, const FormatSpec &spec, int precision);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700836
Victor Zverovich93e41252013-09-08 13:07:04 -0700837template fmt::BasicWriter<char>::CharPtr
838 fmt::BasicWriter<char>::FillPadding(CharPtr buffer,
839 unsigned total_size, std::size_t content_size, wchar_t fill);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700840
Victor Zverovich7cae7632013-09-06 20:23:42 -0700841template fmt::BasicWriter<char>::CharPtr
842 fmt::BasicWriter<char>::PrepareFilledBuffer(
Victor Zverovichea5dce32014-01-28 12:47:37 -0800843 unsigned size, const AlignSpec &spec, char sign);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700844
Victor Zveroviche78904b2014-04-23 08:27:50 -0700845template void fmt::BasicWriter<char>::FormatParser::ReportError(
Victor Zverovich7cae7632013-09-06 20:23:42 -0700846 const char *s, StringRef message) const;
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700847
Victor Zveroviche78904b2014-04-23 08:27:50 -0700848template unsigned fmt::BasicWriter<char>::FormatParser::ParseUInt(
849 const char *&s) const;
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700850
Victor Zveroviche78904b2014-04-23 08:27:50 -0700851template const fmt::BasicWriter<char>::ArgInfo
852 &fmt::BasicWriter<char>::FormatParser::ParseArgIndex(const char *&s);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700853
Victor Zveroviche78904b2014-04-23 08:27:50 -0700854template void fmt::BasicWriter<char>::FormatParser::CheckSign(
Victor Zverovich656a8372014-04-22 08:58:54 -0700855 const char *&s, const ArgInfo &arg);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700856
Victor Zveroviche78904b2014-04-23 08:27:50 -0700857template void fmt::BasicWriter<char>::FormatParser::Format(
858 BasicWriter<char> &writer, BasicStringRef<char> format,
859 std::size_t num_args, const ArgInfo *args);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700860
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700861// Explicit instantiations for wchar_t.
862
Victor Zverovich7cae7632013-09-06 20:23:42 -0700863template void fmt::BasicWriter<wchar_t>::FormatDouble<double>(
Victor Zverovichea5dce32014-01-28 12:47:37 -0800864 double value, const FormatSpec &spec, int precision);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700865
Victor Zverovich7cae7632013-09-06 20:23:42 -0700866template void fmt::BasicWriter<wchar_t>::FormatDouble<long double>(
Victor Zverovichea5dce32014-01-28 12:47:37 -0800867 long double value, const FormatSpec &spec, int precision);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700868
Victor Zverovich7cae7632013-09-06 20:23:42 -0700869template fmt::BasicWriter<wchar_t>::CharPtr
Victor Zverovich93e41252013-09-08 13:07:04 -0700870 fmt::BasicWriter<wchar_t>::FillPadding(CharPtr buffer,
871 unsigned total_size, std::size_t content_size, wchar_t fill);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700872
Victor Zverovich7cae7632013-09-06 20:23:42 -0700873template fmt::BasicWriter<wchar_t>::CharPtr
874 fmt::BasicWriter<wchar_t>::PrepareFilledBuffer(
Victor Zverovichea5dce32014-01-28 12:47:37 -0800875 unsigned size, const AlignSpec &spec, char sign);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700876
Victor Zveroviche78904b2014-04-23 08:27:50 -0700877template void fmt::BasicWriter<wchar_t>::FormatParser::ReportError(
Victor Zverovich7cae7632013-09-06 20:23:42 -0700878 const wchar_t *s, StringRef message) const;
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700879
Victor Zveroviche78904b2014-04-23 08:27:50 -0700880template unsigned fmt::BasicWriter<wchar_t>::FormatParser::ParseUInt(
Victor Zverovich7cae7632013-09-06 20:23:42 -0700881 const wchar_t *&s) const;
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700882
Victor Zveroviche78904b2014-04-23 08:27:50 -0700883template const fmt::BasicWriter<wchar_t>::ArgInfo
884 &fmt::BasicWriter<wchar_t>::FormatParser::ParseArgIndex(const wchar_t *&s);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700885
Victor Zveroviche78904b2014-04-23 08:27:50 -0700886template void fmt::BasicWriter<wchar_t>::FormatParser::CheckSign(
Victor Zverovich656a8372014-04-22 08:58:54 -0700887 const wchar_t *&s, const ArgInfo &arg);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700888
Victor Zveroviche78904b2014-04-23 08:27:50 -0700889template void fmt::BasicWriter<wchar_t>::FormatParser::Format(
890 BasicWriter<wchar_t> &writer, BasicStringRef<wchar_t> format,
891 std::size_t num_args, const ArgInfo *args);
jdale88a9862fd2014-03-11 18:56:24 +0000892
893#if _MSC_VER
894# pragma warning(pop)
895#endif