// Copyright (c) 2014, Thomas Goyne // // Permission to use, copy, modify, and distribute this software for any // purpose with or without fee is hereby granted, provided that the above // copyright notice and this permission notice appear in all copies. // // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES // WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR // ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN // ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF // OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. // // Aegisub Project http://www.aegisub.org/ #include #include #include #include #ifdef _MSC_VER #define WCHAR_T_ENC "utf-16le" #else #define WCHAR_T_ENC "utf-32le" #endif template class boost::interprocess::basic_vectorstream; template class boost::interprocess::basic_vectorstream; template class boost::interprocess::basic_vectorbuf; template class boost::interprocess::basic_vectorbuf; namespace { template int actual_len(int max_len, const Char *value) { int len = 0; while (value[len] && (max_len <= 0 || len < max_len)) ++len; return len; } template int actual_len(int max_len, std::basic_string value) { if (max_len > 0 && static_cast(max_len) < value.size()) return max_len; return value.size(); } template void do_write_str(std::basic_ostream& out, const Char *str, int len) { out.write(str, len); } template void convert_and_write(std::basic_ostream& out, const Src *str, int len, const char *src_enc, const char *dst_enc) { Dst buffer[512]; agi::charset::Iconv cd(src_enc, dst_enc); size_t in_len = len * sizeof(Src); const char *in = reinterpret_cast(str); size_t res; do { char *out_buf = reinterpret_cast(buffer); size_t out_len = sizeof(buffer); res = cd(&in, &in_len, &out_buf, &out_len); if (res == 0) cd(nullptr, nullptr, &out_buf, &out_len); out.write(buffer, (sizeof(buffer) - out_len) / sizeof(Dst)); } while (res == (size_t)-1 && errno == E2BIG); } void do_write_str(std::ostream& out, const wchar_t *str, int len) { convert_and_write(out, str, len, WCHAR_T_ENC, "utf-8"); } void do_write_str(std::wostream& out, const char *str, int len) { convert_and_write(out, str, len, "utf-8", WCHAR_T_ENC); } template struct format_parser { agi::format_detail::formatter_state &s; void read_and_append_up_to_next_specifier() { for (std::streamsize len = 0; ; ++len) { // Ran out of format specifiers; not an error due to that // translated strings may not need them all if (!s.fmt[len]) { s.out.write(s.fmt, len); s.fmt += len; return; } if (s.fmt[len] == '%') { if (s.fmt[len + 1] == '%') { s.out.write(s.fmt, len); s.fmt += len + 1; len = 0; continue; } s.out.write(s.fmt, len); s.fmt += len; break; } } } int read_int() { int i = 0; for (; *s.fmt_cur >= '0' && *s.fmt_cur <= '9'; ++s.fmt_cur) i = 10 * i + (*s.fmt_cur - '0'); return i; } void parse_flags() { for (; ; ++s.fmt_cur) { switch (*s.fmt_cur) { // Not supported: ' ' (add a space before positive numers to align with negative) case '#': s.out.setf(std::ios::showpoint | std::ios::showbase); continue; case '0': // overridden by left alignment ('-' flag) if (!(s.out.flags() & std::ios::left)) { // Use internal padding so that numeric values are // formatted correctly, eg -00010 rather than 000-10 s.out.fill('0'); s.out.setf(std::ios::internal, std::ios::adjustfield); } continue; case '-': s.out.fill(' '); s.out.setf(std::ios::left, std::ios::adjustfield); continue; case '+': s.out.setf(std::ios::showpos); continue; } break; } } void parse_width() { if (*s.fmt_cur >= '0' && *s.fmt_cur <= '9') s.width = read_int(); else if (*s.fmt_cur == '*') { s.read_width = true; s.pending = true; ++s.fmt_cur; } } void parse_precision() { if (*s.fmt_cur != '.') return; ++s.fmt_cur; // Ignoring negative precision because it's dumb and pointless if (*s.fmt_cur >= '0' && *s.fmt_cur <= '9') s.precision = read_int(); else if (*s.fmt_cur == '*') { s.read_precision = true; s.pending = true; ++s.fmt_cur; } else s.precision = 0; } void parse_length_modifiers() { // Where "parse" means "skip" since we don't need them for (Char c = *s.fmt_cur; c == 'l' || c == 'h' || c == 'L' || c == 'j' || c == 'z' || c == 't'; c = *++s.fmt_cur); } void parse_format_specifier() { s.width = 0; s.precision = -1; s.out.fill(' '); s.out.unsetf( std::ios::adjustfield | std::ios::basefield | std::ios::boolalpha | std::ios::floatfield | std::ios::showbase | std::ios::showpoint | std::ios::showpos | std::ios::uppercase); // Don't touch fmt until the specifier is fully applied so that if we // have insufficient arguments it'll get passed through to the output s.fmt_cur = s.fmt + 1; parse_flags(); parse_width(); parse_precision(); parse_length_modifiers(); } }; } namespace agi { template void writer::write(std::basic_ostream& out, int max_len, const Char *value) { do_write_str(out, value, actual_len(max_len, value)); } template void writer>::write(std::basic_ostream& out, int max_len, std::basic_string const& value) { do_write_str(out, value.data(), actual_len(max_len, value)); } template struct writer; template struct writer; template struct writer; template struct writer; template struct writer; template struct writer; template struct writer; template struct writer; namespace format_detail { template bool formatter::parse_next() { format_parser parser{*static_cast *>(this)}; parser.read_and_append_up_to_next_specifier(); if (!*this->fmt) return false; parser.parse_format_specifier(); return true; } template Char formatter::next_format() { this->pending = false; if (this->width < 0) { this->out.fill(' '); this->out.setf(std::ios::left, std::ios::adjustfield); this->width = -this->width; } this->out.width(this->width); this->out.precision(this->precision < 0 ? 6 : this->precision); Char c = *this->fmt_cur ? this->fmt_cur[0] : 's'; if (c >= 'A' && c <= 'Z') { this->out.setf(std::ios::uppercase); c += 'a' - 'A'; } switch (c) { case 'c': this->out.setf(std::ios::dec, std::ios::basefield); break; case 'd': case 'i': this->out.setf(std::ios::dec, std::ios::basefield); break; case 'o': this->out.setf(std::ios::oct, std::ios::basefield); break; case 'x': this->out.setf(std::ios::hex, std::ios::basefield); break; case 'u': this->out.setf(std::ios::dec, std::ios::basefield); break; case 'e': this->out.setf(std::ios::scientific, std::ios::floatfield); this->out.setf(std::ios::dec, std::ios::basefield); break; case 'f': this->out.setf(std::ios::fixed, std::ios::floatfield); break; case 'g': this->out.setf(std::ios::dec, std::ios::basefield); this->out.flags(this->out.flags() & ~std::ios::floatfield); break; case 'p': this->out.setf(std::ios::hex, std::ios::basefield); break; default: // s and other this->out.setf(std::ios::boolalpha); break; } this->fmt = *this->fmt_cur ? this->fmt_cur + 1 : this->fmt_cur; return c; } template formatter::~formatter() { // Write remaining formatting string for (std::streamsize len = 0; ; ++len) { if (!this->fmt[len]) { this->out.write(this->fmt, len); return; } if (this->fmt[len] == '%' && this->fmt[len + 1] == '%') { this->out.write(this->fmt, len); this->fmt += len + 1; len = 0; continue; } } } template class formatter; template class formatter; } }