diff --git a/format.cc b/format.cc index b273dbd2..8466358c 100644 --- a/format.cc +++ b/format.cc @@ -411,246 +411,252 @@ void fmt::BasicFormatter::CheckSign(const Char *&s, const Arg &arg) { template void fmt::BasicFormatter::DoFormat() { const Char *start = format_; + const Char *original = format_; // capture the format string before it is reset format_ = 0; next_arg_index_ = 0; const Char *s = start; BasicWriter &writer = *writer_; - while (*s) { - Char c = *s++; - if (c != '{' && c != '}') continue; - if (*s == c) { - writer.buffer_.append(start, s); - start = ++s; - continue; - } - if (c == '}') - throw FormatError("unmatched '}' in format"); - num_open_braces_= 1; - writer.buffer_.append(start, s - 1); - - const Arg &arg = ParseArgIndex(s); - - FormatSpec spec; - int precision = -1; - if (*s == ':') { - ++s; - - // Parse fill and alignment. - if (Char c = *s) { - const Char *p = s + 1; - spec.align_ = ALIGN_DEFAULT; - do { - switch (*p) { - case '<': - spec.align_ = ALIGN_LEFT; - break; - case '>': - spec.align_ = ALIGN_RIGHT; - break; - case '=': - spec.align_ = ALIGN_NUMERIC; - break; - case '^': - spec.align_ = ALIGN_CENTER; - break; - } - if (spec.align_ != ALIGN_DEFAULT) { - if (p != s) { - if (c == '}') break; - if (c == '{') - ReportError(s, "invalid fill character '{'"); - s += 2; - spec.fill_ = c; - } else ++s; - if (spec.align_ == ALIGN_NUMERIC && arg.type > LAST_NUMERIC_TYPE) - ReportError(s, "format specifier '=' requires numeric argument"); - break; - } - } while (--p >= s); + try { + while (*s) { + Char c = *s++; + if (c != '{' && c != '}') continue; + if (*s == c) { + writer.buffer_.append(start, s); + start = ++s; + continue; } + if (c == '}') + throw FormatError("unmatched '}' in format"); + num_open_braces_= 1; + writer.buffer_.append(start, s - 1); - // Parse sign. - switch (*s) { - case '+': - CheckSign(s, arg); - spec.flags_ |= SIGN_FLAG | PLUS_FLAG; - break; - case '-': - CheckSign(s, arg); - break; - case ' ': - CheckSign(s, arg); - spec.flags_ |= SIGN_FLAG; - break; - } + const Arg &arg = ParseArgIndex(s); - if (*s == '#') { - if (arg.type > LAST_NUMERIC_TYPE) - ReportError(s, "format specifier '#' requires numeric argument"); - spec.flags_ |= HASH_FLAG; + FormatSpec spec; + int precision = -1; + if (*s == ':') { ++s; - } - // Parse width and zero flag. - if ('0' <= *s && *s <= '9') { - if (*s == '0') { - if (arg.type > LAST_NUMERIC_TYPE) - ReportError(s, "format specifier '0' requires numeric argument"); - spec.align_ = ALIGN_NUMERIC; - spec.fill_ = '0'; + // Parse fill and alignment. + if (Char c = *s) { + const Char *p = s + 1; + spec.align_ = ALIGN_DEFAULT; + do { + switch (*p) { + case '<': + spec.align_ = ALIGN_LEFT; + break; + case '>': + spec.align_ = ALIGN_RIGHT; + break; + case '=': + spec.align_ = ALIGN_NUMERIC; + break; + case '^': + spec.align_ = ALIGN_CENTER; + break; + } + if (spec.align_ != ALIGN_DEFAULT) { + if (p != s) { + if (c == '}') break; + if (c == '{') + ReportError(s, "invalid fill character '{'"); + s += 2; + spec.fill_ = c; + } else ++s; + if (spec.align_ == ALIGN_NUMERIC && arg.type > LAST_NUMERIC_TYPE) + ReportError(s, "format specifier '=' requires numeric argument"); + break; + } + } while (--p >= s); } - // Zero may be parsed again as a part of the width, but it is simpler - // and more efficient than checking if the next char is a digit. - unsigned value = ParseUInt(s); - if (value > INT_MAX) - ReportError(s, "number is too big in format"); - spec.width_ = value; - } - // Parse precision. - if (*s == '.') { - ++s; - precision = 0; + // Parse sign. + switch (*s) { + case '+': + CheckSign(s, arg); + spec.flags_ |= SIGN_FLAG | PLUS_FLAG; + break; + case '-': + CheckSign(s, arg); + break; + case ' ': + CheckSign(s, arg); + spec.flags_ |= SIGN_FLAG; + break; + } + + if (*s == '#') { + if (arg.type > LAST_NUMERIC_TYPE) + ReportError(s, "format specifier '#' requires numeric argument"); + spec.flags_ |= HASH_FLAG; + ++s; + } + + // Parse width and zero flag. if ('0' <= *s && *s <= '9') { + if (*s == '0') { + if (arg.type > LAST_NUMERIC_TYPE) + ReportError(s, "format specifier '0' requires numeric argument"); + spec.align_ = ALIGN_NUMERIC; + spec.fill_ = '0'; + } + // Zero may be parsed again as a part of the width, but it is simpler + // and more efficient than checking if the next char is a digit. unsigned value = ParseUInt(s); if (value > INT_MAX) ReportError(s, "number is too big in format"); - precision = value; - } else if (*s == '{') { + spec.width_ = value; + } + + // Parse precision. + if (*s == '.') { ++s; - ++num_open_braces_; - const Arg &precision_arg = ParseArgIndex(s); - ULongLong value = 0; - switch (precision_arg.type) { - case INT: - if (precision_arg.int_value < 0) - ReportError(s, "negative precision in format"); - value = precision_arg.int_value; - break; - case UINT: - value = precision_arg.uint_value; - break; - case LONG: - if (precision_arg.long_value < 0) - ReportError(s, "negative precision in format"); - value = precision_arg.long_value; - break; - case ULONG: - value = precision_arg.ulong_value; - break; - case LONG_LONG: - if (precision_arg.long_long_value < 0) - ReportError(s, "negative precision in format"); - value = precision_arg.long_long_value; - break; - case ULONG_LONG: - value = precision_arg.ulong_long_value; - break; - default: - ReportError(s, "precision is not integer"); + precision = 0; + if ('0' <= *s && *s <= '9') { + unsigned value = ParseUInt(s); + if (value > INT_MAX) + ReportError(s, "number is too big in format"); + precision = value; + } else if (*s == '{') { + ++s; + ++num_open_braces_; + const Arg &precision_arg = ParseArgIndex(s); + ULongLong value = 0; + switch (precision_arg.type) { + case INT: + if (precision_arg.int_value < 0) + ReportError(s, "negative precision in format"); + value = precision_arg.int_value; + break; + case UINT: + value = precision_arg.uint_value; + break; + case LONG: + if (precision_arg.long_value < 0) + ReportError(s, "negative precision in format"); + value = precision_arg.long_value; + break; + case ULONG: + value = precision_arg.ulong_value; + break; + case LONG_LONG: + if (precision_arg.long_long_value < 0) + ReportError(s, "negative precision in format"); + value = precision_arg.long_long_value; + break; + case ULONG_LONG: + value = precision_arg.ulong_long_value; + break; + default: + ReportError(s, "precision is not integer"); + } + if (value > INT_MAX) + ReportError(s, "number is too big in format"); + precision = static_cast(value); + if (*s++ != '}') + throw FormatError("unmatched '{' in format"); + --num_open_braces_; + } else { + ReportError(s, "missing precision in format"); + } + if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) { + ReportError(s, + "precision specifier requires floating-point argument"); + } + } + + // Parse type. + if (*s != '}' && *s) + spec.type_ = static_cast(*s++); + } + + if (*s++ != '}') + throw FormatError("unmatched '{' in format"); + start = s; + + // Format argument. + switch (arg.type) { + case INT: + writer.FormatInt(arg.int_value, spec); + break; + case UINT: + writer.FormatInt(arg.uint_value, spec); + break; + case LONG: + writer.FormatInt(arg.long_value, spec); + break; + case ULONG: + writer.FormatInt(arg.ulong_value, spec); + break; + case LONG_LONG: + writer.FormatInt(arg.long_long_value, spec); + break; + case ULONG_LONG: + writer.FormatInt(arg.ulong_long_value, spec); + break; + case DOUBLE: + writer.FormatDouble(arg.double_value, spec, precision); + break; + case LONG_DOUBLE: + writer.FormatDouble(arg.long_double_value, spec, precision); + break; + case CHAR: { + if (spec.type_ && spec.type_ != 'c') + internal::ReportUnknownType(spec.type_, "char"); + typedef typename BasicWriter::CharPtr CharPtr; + CharPtr out = CharPtr(); + if (spec.width_ > 1) { + Char fill = static_cast(spec.fill()); + out = writer.GrowBuffer(spec.width_); + if (spec.align_ == ALIGN_RIGHT) { + std::fill_n(out, spec.width_ - 1, fill); + out += spec.width_ - 1; + } else if (spec.align_ == ALIGN_CENTER) { + out = writer.FillPadding(out, spec.width_, 1, fill); + } else { + std::fill_n(out + 1, spec.width_ - 1, fill); } - if (value > INT_MAX) - ReportError(s, "number is too big in format"); - precision = static_cast(value); - if (*s++ != '}') - throw FormatError("unmatched '{' in format"); - --num_open_braces_; } else { - ReportError(s, "missing precision in format"); + out = writer.GrowBuffer(1); } - if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) { - ReportError(s, - "precision specifier requires floating-point argument"); + *out = arg.int_value; + break; + } + case STRING: { + if (spec.type_ && spec.type_ != 's') + internal::ReportUnknownType(spec.type_, "string"); + const Char *str = arg.string.value; + std::size_t size = arg.string.size; + if (size == 0) { + if (!str) + throw FormatError("string pointer is null"); + if (*str) + size = std::char_traits::length(str); } + writer.FormatString(str, size, spec); + break; } - - // Parse type. - if (*s != '}' && *s) - spec.type_ = static_cast(*s++); - } - - if (*s++ != '}') - throw FormatError("unmatched '{' in format"); - start = s; - - // Format argument. - switch (arg.type) { - case INT: - writer.FormatInt(arg.int_value, spec); - break; - case UINT: - writer.FormatInt(arg.uint_value, spec); - break; - case LONG: - writer.FormatInt(arg.long_value, spec); - break; - case ULONG: - writer.FormatInt(arg.ulong_value, spec); - break; - case LONG_LONG: - writer.FormatInt(arg.long_long_value, spec); - break; - case ULONG_LONG: - writer.FormatInt(arg.ulong_long_value, spec); - break; - case DOUBLE: - writer.FormatDouble(arg.double_value, spec, precision); - break; - case LONG_DOUBLE: - writer.FormatDouble(arg.long_double_value, spec, precision); - break; - case CHAR: { - if (spec.type_ && spec.type_ != 'c') - internal::ReportUnknownType(spec.type_, "char"); - typedef typename BasicWriter::CharPtr CharPtr; - CharPtr out = CharPtr(); - if (spec.width_ > 1) { - Char fill = static_cast(spec.fill()); - out = writer.GrowBuffer(spec.width_); - if (spec.align_ == ALIGN_RIGHT) { - std::fill_n(out, spec.width_ - 1, fill); - out += spec.width_ - 1; - } else if (spec.align_ == ALIGN_CENTER) { - out = writer.FillPadding(out, spec.width_, 1, fill); - } else { - std::fill_n(out + 1, spec.width_ - 1, fill); - } - } else { - out = writer.GrowBuffer(1); + case POINTER: + if (spec.type_ && spec.type_ != 'p') + internal::ReportUnknownType(spec.type_, "pointer"); + spec.flags_= HASH_FLAG; + spec.type_ = 'x'; + writer.FormatInt(reinterpret_cast(arg.pointer_value), spec); + break; + case CUSTOM: + if (spec.type_) + internal::ReportUnknownType(spec.type_, "object"); + arg.custom.format(writer, arg.custom.value, spec); + break; + default: + assert(false); + break; } - *out = arg.int_value; - break; - } - case STRING: { - if (spec.type_ && spec.type_ != 's') - internal::ReportUnknownType(spec.type_, "string"); - const Char *str = arg.string.value; - std::size_t size = arg.string.size; - if (size == 0) { - if (!str) - throw FormatError("string pointer is null"); - if (*str) - size = std::char_traits::length(str); - } - writer.FormatString(str, size, spec); - break; - } - case POINTER: - if (spec.type_ && spec.type_ != 'p') - internal::ReportUnknownType(spec.type_, "pointer"); - spec.flags_= HASH_FLAG; - spec.type_ = 'x'; - writer.FormatInt(reinterpret_cast(arg.pointer_value), spec); - break; - case CUSTOM: - if (spec.type_) - internal::ReportUnknownType(spec.type_, "object"); - arg.custom.format(writer, arg.custom.value, spec); - break; - default: - assert(false); - break; - } + } + } catch (const FormatError &e) { + // rethrow FormatError with the format string pointed to by start + throw BasicFormatError(e.what(), original); } writer.buffer_.append(start, s); } @@ -687,6 +693,18 @@ template void fmt::BasicFormatter::CheckSign( template void fmt::BasicFormatter::DoFormat(); +#ifdef FMT_FORMAT_STRING_IN_ERRORS +std::string fmt::FormatErrorMessage(const std::string &message, const char *format) { + fmt::Writer w; + w << "error: " << message << " while parsing format string " << format; + return w.str(); +} +#else +std::string fmt::FormatErrorMessage(const std::string &message, const char *format) { + return message; +} +#endif + // Explicit instantiations for wchar_t. template void fmt::BasicWriter::FormatDouble( @@ -719,3 +737,21 @@ template void fmt::BasicFormatter::CheckSign( const wchar_t *&s, const Arg &arg); template void fmt::BasicFormatter::DoFormat(); + +#ifdef FMT_FORMAT_STRING_IN_ERRORS +std::string fmt::FormatErrorMessage(const std::string &message, const wchar_t *format) { + + int size = FMT_SNPRINTF(NULL, 0, "%ls", format); + char* buf = (char*)malloc(size+1); + FMT_SNPRINTF(buf, size+1, "%ls", format); + + fmt::Writer w; + w << "error: " << message << " while parsing format string " << buf; + free(buf); + return w.str(); +} +#else +std::string fmt::FormatErrorMessage(const std::string &message, const wchar_t *format) { + return message; +} +#endif diff --git a/format.h b/format.h index 2ac0bdf2..786049f4 100644 --- a/format.h +++ b/format.h @@ -315,6 +315,20 @@ class FormatError : public std::runtime_error { : std::runtime_error(message) {} }; +std::string FormatErrorMessage(const std::string &message, const char *format); +std::string FormatErrorMessage(const std::string &message, const wchar_t *format); + +template +class BasicFormatError : public std::runtime_error { +private: + std::basic_string format_; +public: + explicit BasicFormatError(const std::string &message, const Char *format) + : std::runtime_error(fmt::FormatErrorMessage(message, format)), format_(format){} + virtual ~BasicFormatError() throw() {} + const Char *format() const { return format_.c_str(); } +}; + enum Alignment { ALIGN_DEFAULT, ALIGN_LEFT, ALIGN_RIGHT, ALIGN_CENTER, ALIGN_NUMERIC }; @@ -402,6 +416,11 @@ class IntFormatter : public SpecT { */ IntFormatter > bin(int value); +/** + Returns an integer formatter that formats the value in base 2. + */ +IntFormatter > binu(int value); + /** Returns an integer formatter that formats the value in base 8. */ @@ -439,7 +458,9 @@ IntFormatter > pad( inline IntFormatter > bin(TYPE value) { \ return IntFormatter >(value, TypeSpec<'b'>()); \ } \ - \ +inline IntFormatter > binu(TYPE value) { \ + return IntFormatter >(value, TypeSpec<'B'>()); \ +} \ inline IntFormatter > oct(TYPE value) { \ return IntFormatter >(value, TypeSpec<'o'>()); \ } \