diff options
author | John Wiegley <johnw@newartisans.com> | 2009-10-25 04:37:11 -0400 |
---|---|---|
committer | John Wiegley <johnw@newartisans.com> | 2009-10-25 05:01:47 -0400 |
commit | fc84eeb358f13b7c2b2668917692da26af001901 (patch) | |
tree | cfc4540c1b7805a17425675c1143d7a2d748b42f /src | |
parent | 588f2ef2f51d7bdf209820bfb244034863601939 (diff) | |
download | fork-ledger-fc84eeb358f13b7c2b2668917692da26af001901.tar.gz fork-ledger-fc84eeb358f13b7c2b2668917692da26af001901.tar.bz2 fork-ledger-fc84eeb358f13b7c2b2668917692da26af001901.zip |
Rewrote the way date and time I/O is managed
Diffstat (limited to 'src')
-rw-r--r-- | src/annotate.cc | 2 | ||||
-rw-r--r-- | src/filters.cc | 9 | ||||
-rw-r--r-- | src/generate.cc | 4 | ||||
-rw-r--r-- | src/global.cc | 6 | ||||
-rw-r--r-- | src/quotes.cc | 2 | ||||
-rw-r--r-- | src/report.cc | 10 | ||||
-rw-r--r-- | src/report.h | 14 | ||||
-rw-r--r-- | src/session.cc | 2 | ||||
-rw-r--r-- | src/session.h | 6 | ||||
-rw-r--r-- | src/times.cc | 345 | ||||
-rw-r--r-- | src/times.h | 41 | ||||
-rw-r--r-- | src/value.cc | 17 | ||||
-rw-r--r-- | src/value.h | 2 |
13 files changed, 344 insertions, 116 deletions
diff --git a/src/annotate.cc b/src/annotate.cc index 1ea39b5d..c6084f7f 100644 --- a/src/annotate.cc +++ b/src/annotate.cc @@ -127,7 +127,7 @@ void annotation_t::print(std::ostream& out, bool keep_base) const << '}'; if (date) - out << " [" << format_date(*date, string("%Y/%m/%d")) << ']'; + out << " [" << format_date(*date, FMT_WRITTEN) << ']'; if (tag) out << " (" << *tag << ')'; diff --git a/src/filters.cc b/src/filters.cc index 967e0843..5b087eea 100644 --- a/src/filters.cc +++ b/src/filters.cc @@ -531,15 +531,14 @@ void subtotal_posts::report_subtotal(const char * spec_fmt, std::ostringstream out_date; if (spec_fmt) { - out_date << format_date(*range_finish, string(spec_fmt)); + out_date << format_date(*range_finish, FMT_CUSTOM, spec_fmt); } else if (date_format) { - string fmt = "- "; - fmt += *date_format; - out_date << format_date(*range_finish, string(fmt)); + out_date << "- " << format_date(*range_finish, FMT_CUSTOM, + date_format->c_str()); } else { - out_date << format_date(*range_finish, std::string("- ") + output_date_format); + out_date << "- " << format_date(*range_finish); } xact_temps.push_back(xact_t()); diff --git a/src/generate.cc b/src/generate.cc index 8796f0bf..ebbc3cd7 100644 --- a/src/generate.cc +++ b/src/generate.cc @@ -321,11 +321,11 @@ void generate_posts_iterator::generate_note(std::ostream& out) void generate_posts_iterator::generate_xact(std::ostream& out) { - out << format_date(next_date, string("%Y/%m/%d")); + out << format_date(next_date, FMT_WRITTEN); next_date += gregorian::days(six_gen()); if (truth_gen()) { out << '='; - out << format_date(next_eff_date, string("%Y/%m/%d")); + out << format_date(next_eff_date, FMT_WRITTEN); next_eff_date += gregorian::days(six_gen()); } out << ' '; diff --git a/src/global.cc b/src/global.cc index 24716fae..02c9e79a 100644 --- a/src/global.cc +++ b/src/global.cc @@ -433,8 +433,10 @@ void global_scope_t::normalize_report_options(const string& verb) rep.session.commodity_pool->price_db = none; if (rep.HANDLED(date_format_)) { - output_datetime_format = rep.HANDLER(date_format_).str() + " %H:%M:%S"; - output_date_format = rep.HANDLER(date_format_).str(); + set_date_format(rep.HANDLER(date_format_).str().c_str()); + } + if (rep.HANDLED(datetime_format_)) { + set_datetime_format(rep.HANDLER(datetime_format_).str().c_str()); } if (rep.HANDLED(start_of_week_)) { if (optional<date_time::weekdays> weekday = diff --git a/src/quotes.cc b/src/quotes.cc index d4be462e..797e6dd5 100644 --- a/src/quotes.cc +++ b/src/quotes.cc @@ -86,7 +86,7 @@ commodity_quote_from_script(commodity_t& commodity, std::ios_base::out | std::ios_base::app); #endif database << "P " - << format_datetime(point->when, string("%Y/%m/%d %H:%M:%S")) + << format_datetime(point->when, FMT_WRITTEN) << " " << commodity.symbol() << " " << point->price << std::endl; diff --git a/src/report.cc b/src/report.cc index 661fdfbe..9c8ad8ba 100644 --- a/src/report.cc +++ b/src/report.cc @@ -263,9 +263,12 @@ value_t report_t::fn_join(call_scope_t& scope) value_t report_t::fn_format_date(call_scope_t& scope) { - interactive_t args(scope, "ds"); - return string_value(format_date(args.get<date_t>(0), - args.get<string>(1))); + interactive_t args(scope, "d&s"); + if (args.has(1)) + return string_value(format_date(args.get<date_t>(0), FMT_CUSTOM, + args.get<string>(1).c_str())); + else + return string_value(format_date(args.get<date_t>(0), FMT_PRINTED)); } value_t report_t::fn_ansify_if(call_scope_t& scope) @@ -521,6 +524,7 @@ option_t<report_t> * report_t::lookup_option(const char * p) case 'd': OPT(daily); else OPT(date_format_); + else OPT(datetime_format_); else OPT(depth_); else OPT(deviation); else OPT_(display_); diff --git a/src/report.h b/src/report.h index 71dc4ca2..5921a154 100644 --- a/src/report.h +++ b/src/report.h @@ -218,6 +218,7 @@ public: HANDLER(current).report(out); HANDLER(daily).report(out); HANDLER(date_format_).report(out); + HANDLER(datetime_format_).report(out); HANDLER(depth_).report(out); HANDLER(deviation).report(out); HANDLER(display_).report(out); @@ -412,10 +413,8 @@ public: parent->HANDLER(period_).on(string("--daily"), "daily"); }); - OPTION__(report_t, date_format_, // -y - CTOR(report_t, date_format_) { - on(none, "%y-%b-%d"); - }); + OPTION(report_t, date_format_); + OPTION(report_t, datetime_format_); OPTION_(report_t, depth_, DO_(scope) { interactive_t args(scope, "sl"); @@ -633,9 +632,9 @@ public: OPTION__(report_t, print_format_, CTOR(report_t, print_format_) { on(none, - "%(format_date(xact.date, \"%Y/%m/%d\"))" + "%(xact.date)" "%(!effective & xact.effective_date ?" - " \"=\" + format_date(xact.effective_date, \"%Y/%m/%d\") : \"\")" + " \"=\" + xact.effective_date : \"\")" "%(xact.cleared ? \" *\" : (xact.pending ? \" !\" : \"\"))" "%(code ? \" (\" + code + \")\" :" " \"\") %(payee)%(xact.comment)\n" @@ -673,7 +672,8 @@ public: OPTION__(report_t, register_format_, CTOR(report_t, register_format_) { on(none, - "%(ansify_if(justify(date, date_width), green if color & date > today))" + "%(ansify_if(justify(format_date(date), date_width), green " + " if color & date > today))" " %(ansify_if(justify(truncated(payee, payee_width), payee_width), " " bold if color & !cleared))" " %(ansify_if(justify(truncated(account, account_width, abbrev_len), " diff --git a/src/session.cc b/src/session.cc index f7a8655b..b46d545e 100644 --- a/src/session.cc +++ b/src/session.cc @@ -45,6 +45,7 @@ namespace ledger { void set_session_context(session_t * session) { if (session) { + times_initialize(); amount_t::initialize(session->commodity_pool); // jww (2009-02-04): Is amount_t the right place for parse_conversion to @@ -57,6 +58,7 @@ void set_session_context(session_t * session) else if (! session) { value_t::shutdown(); amount_t::shutdown(); + times_shutdown(); } } diff --git a/src/session.h b/src/session.h index 894c59fa..a51cdba6 100644 --- a/src/session.h +++ b/src/session.h @@ -144,9 +144,9 @@ public: }); OPTION_(session_t, input_date_format_, DO_(args) { - // This changes the global variable inside times.h, which affects the - // basic date parser - input_date_format = args[1].as_string(); + // This changes static variables inside times.h, which affects the basic + // date parser. + set_input_date_format(args[1].as_string().c_str()); }); OPTION(session_t, price_db_); diff --git a/src/times.cc b/src/times.cc index 45f7ed10..a00692b4 100644 --- a/src/times.cc +++ b/src/times.cc @@ -35,55 +35,177 @@ namespace ledger { -date_time::weekdays start_of_week = gregorian::Sunday; -optional<std::string> input_date_format; -std::string output_datetime_format = "%Y-%m-%d %H:%M:%S"; -std::string output_date_format = "%Y-%m-%d"; +date_time::weekdays start_of_week = gregorian::Sunday; + +//#define USE_BOOST_FACETS 1 namespace { - struct date_format_t { - const char * format; - bool has_year; - date_format_t(const char * _format, bool _has_year) - : format(_format), has_year(_has_year) {} - }; + template <typename T, typename InputFacetType, typename OutputFacetType> + class temporal_io_t : public noncopyable + { + const char * fmt_str; +#if defined(USE_BOOST_FACETS) + std::istringstream input_stream; + std::ostringstream output_stream; + InputFacetType * input_facet; + OutputFacetType * output_facet; + std::string temp_string; +#endif - const date_format_t formats[] = { - date_format_t("%m/%d", false), - date_format_t("%Y/%m/%d", true), - date_format_t("%Y/%m", true), - date_format_t("%y/%m/%d", true), - date_format_t("%m.%d", false), - date_format_t("%Y.%m.%d", true), - date_format_t("%Y.%m", true), - date_format_t("%y.%m.%d", true), - date_format_t("%m-%d", false), - date_format_t("%Y-%m-%d", true), - date_format_t("%Y-%m", true), - date_format_t("%y-%m-%d", true) + public: + bool has_year; + bool input; + + temporal_io_t(const char * _fmt_str, bool _input) + : fmt_str(_fmt_str), has_year(icontains(fmt_str, "%y")), + input(_input) { +#if defined(USE_BOOST_FACETS) + if (input) { + input_facet = new InputFacetType(fmt_str); + input_stream.imbue(std::locale(std::locale::classic(), input_facet)); + } else { + output_facet = new OutputFacetType(fmt_str); + output_stream.imbue(std::locale(std::locale::classic(), output_facet)); + } +#endif + } + + void set_format(const char * fmt) { + fmt_str = fmt; + has_year = icontains(fmt_str, "%y"); + +#if defined(USE_BOOST_FACETS) + if (input) + input_facet->format(fmt_str); + else + output_facet->format(fmt_str); +#endif + } + + T parse(const char * str) { + } + + std::string format(const T& when) { +#if defined(USE_BOOST_FACETS) + output_stream.str(temp_string); + output_stream.seekp(std::ios_base::beg); + output_stream.clear(); + output_stream << when; + return output_stream.str(); +#else + std::tm data(to_tm(when)); + char buf[128]; + std::strftime(buf, 127, fmt_str, &data); + return buf; +#endif + } }; - date_t parse_date_mask_routine(const char * date_str, const date_format_t& df, - optional<date_t::year_type> year, bool& saw_year) + template <> + datetime_t temporal_io_t<datetime_t, posix_time::time_input_facet, + posix_time::time_facet> + ::parse(const char * str) { - std::string str(date_str); +#if defined(USE_BOOST_FACETS) + input_stream.seekg(std::ios_base::beg); + input_stream.clear(); + input_stream.str(str); - gregorian::date_input_facet * facet(new gregorian::date_input_facet(df.format)); - std::istringstream sstr(str); - sstr.imbue(std::locale(sstr.getloc(), facet)); + datetime_t when; + input_stream >> when; +#if defined(DEBUG_ON) + if (when.is_not_a_date_time()) + DEBUG("times.parse", "Failed to parse '" << str + << "' using pattern '" << fmt_str << "'"); +#endif - date_t when; - sstr >> when; + if (! when.is_not_a_date_time() && + input_stream.good() && ! input_stream.eof() && + input_stream.peek() != EOF) + return datetime_t(); + return when; +#else + std::tm data; + std::memset(&data, 0, sizeof(std::tm)); + if (strptime(str, fmt_str, &data)) + return posix_time::ptime_from_tm(data); + else + return datetime_t(); +#endif + } - if (! when.is_not_a_date()) { - if (sstr.good() && ! sstr.eof() && sstr.peek() != EOF) + template <> + date_t temporal_io_t<date_t, gregorian::date_input_facet, + gregorian::date_facet> + ::parse(const char * str) + { +#if defined(USE_BOOST_FACETS) + input_stream.seekg(std::ios_base::beg); + input_stream.clear(); + input_stream.str(str); + + date_t when; + input_stream >> when; +#if defined(DEBUG_ON) + if (when.is_not_a_date()) + DEBUG("times.parse", "Failed to parse '" << str + << "' using pattern '" << fmt_str << "'"); +#endif + + if (! when.is_not_a_date() && + input_stream.good() && ! input_stream.eof() && + input_stream.peek() != EOF) return date_t(); + return when; +#else + std::tm data; + std::memset(&data, 0, sizeof(std::tm)); + data.tm_mday = 1; // some formats have no day + if (strptime(str, fmt_str, &data)) + return gregorian::date_from_tm(data); + else + return date_t(); +#endif + } + + typedef temporal_io_t<datetime_t, posix_time::time_input_facet, + posix_time::time_facet> datetime_io_t; + typedef temporal_io_t<date_t, gregorian::date_input_facet, + gregorian::date_facet> date_io_t; + + std::auto_ptr<datetime_io_t> written_datetime_io; + std::auto_ptr<datetime_io_t> printed_datetime_io; + std::auto_ptr<date_io_t> input_date_io; + std::auto_ptr<date_io_t> written_date_io; + std::auto_ptr<date_io_t> printed_date_io; + + std::vector<shared_ptr<date_io_t> > readers; + + date_t parse_date_mask_routine(const char * date_str, date_io_t& io, + optional<date_t::year_type> year, + bool& saw_year) + { + date_t when; + + if (std::strchr(date_str, '/')) { + when = io.parse(date_str); + } else { + char buf[128]; + VERIFY(std::strlen(date_str) < 127); + std::strcpy(buf, date_str); + + for (char * p = buf; *p; p++) + if (*p == '.' || *p == '-') + *p = '/'; + + when = io.parse(buf); + } + if (! when.is_not_a_date()) { DEBUG("times.parse", "Parsed date string: " << date_str); - DEBUG("times.parse", "Parsed result is: " << when); - DEBUG("times.parse", "Format used was: " << df.format); + DEBUG("times.parse", "Parsed result is: " << when); - if (! df.has_year) { + if (! io.has_year) { saw_year = false; when = date_t(year ? *year : CURRENT_DATE().year(), @@ -98,25 +220,24 @@ namespace { return when; } - date_t parse_date_mask(const char * date_str, optional<date_t::year_type> year, - bool& saw_year) + date_t parse_date_mask(const char * date_str, + optional<date_t::year_type> year, bool& saw_year) { - if (input_date_format) { - date_format_t df(input_date_format->c_str(), true); - if (! icontains(*input_date_format, "%y")) - df.has_year = false; - date_t when = parse_date_mask_routine(date_str, df, year, saw_year); + if (input_date_io.get()) { + date_t when = parse_date_mask_routine(date_str, *input_date_io.get(), + year, saw_year); if (! when.is_not_a_date()) return when; } - for (uint8_t i = 0; i < (sizeof(formats) / sizeof(date_format_t)); i++) { - date_t when = parse_date_mask_routine(date_str, formats[i], year, - saw_year); + foreach (shared_ptr<date_io_t>& reader, readers) { + date_t when = parse_date_mask_routine(date_str, *reader.get(), + year, saw_year); if (! when.is_not_a_date()) return when; } + throw_(date_error, _("Invalid date: %1") << date_str); return date_t(); } } @@ -174,16 +295,7 @@ string_to_month_of_year(const std::string& str) datetime_t parse_datetime(const char * str, optional<date_t::year_type>) { - posix_time::time_input_facet * facet - (new posix_time::time_input_facet("%Y/%m/%d %H:%M:%S")); - - std::string temp(str); - std::istringstream sstr(temp); - sstr.imbue(std::locale(sstr.getloc(), facet)); - - datetime_t when; - sstr >> when; - return when; + return written_datetime_io->parse(str); } date_t parse_date(const char * str, optional<date_t::year_type> current_year) @@ -481,7 +593,7 @@ namespace { inline void read_lower_word(std::istream& in, string& word) { in >> word; - for (int i = 0, l = word.length(); i < l; i++) + for (string::size_type i = 0, l = word.length(); i < l; i++) word[i] = static_cast<char>(std::tolower(word[i])); } @@ -662,4 +774,123 @@ void date_interval_t::parse(std::istream& in) } } +namespace { + typedef std::map<std::string, datetime_io_t *> datetime_io_map; + typedef std::map<std::string, date_io_t *> date_io_map; + + datetime_io_map temp_datetime_io; + date_io_map temp_date_io; +} + +std::string format_datetime(const datetime_t& when, + const format_type_t format_type, + const optional<const char *>& format) +{ + if (format_type == FMT_WRITTEN) { + return written_datetime_io->format(when); + } + else if (format_type == FMT_CUSTOM || format) { + datetime_io_map::iterator i = temp_datetime_io.find(*format); + if (i != temp_datetime_io.end()) { + return (*i).second->format(when); + } else { + datetime_io_t * formatter = new datetime_io_t(*format, false); + temp_datetime_io.insert(datetime_io_map::value_type(*format, formatter)); + return formatter->format(when); + } + } + else if (format_type == FMT_PRINTED) { + return printed_datetime_io->format(when); + } + else { + assert(0); + return ""; + } +} + +std::string format_date(const date_t& when, + const format_type_t format_type, + const optional<const char *>& format) +{ + if (format_type == FMT_WRITTEN) { + return written_date_io->format(when); + } + else if (format_type == FMT_CUSTOM || format) { + date_io_map::iterator i = temp_date_io.find(*format); + if (i != temp_date_io.end()) { + return (*i).second->format(when); + } else { + date_io_t * formatter = new date_io_t(*format, false); + temp_date_io.insert(date_io_map::value_type(*format, formatter)); + return formatter->format(when); + } + } + else if (format_type == FMT_PRINTED) { + return printed_date_io->format(when); + } + else { + assert(0); + return ""; + } +} + +namespace { + bool is_initialized = false; +} + +void set_datetime_format(const char * format) +{ + printed_datetime_io->set_format(format); +} + +void set_date_format(const char * format) +{ + printed_date_io->set_format(format); +} + +void set_input_date_format(const char * format) +{ + input_date_io.reset(new date_io_t(format, true)); +} + +void times_initialize() +{ + if (! is_initialized) { + written_datetime_io.reset(new datetime_io_t("%Y/%m/%d %H:%M:%S", false)); + written_date_io.reset(new date_io_t("%Y/%m/%d", false)); + + printed_datetime_io.reset(new datetime_io_t("%y-%b-%d %H:%M:%S", false)); + printed_date_io.reset(new date_io_t("%y-%b-%d", false)); + + readers.push_back(shared_ptr<date_io_t>(new date_io_t("%m/%d", true))); + readers.push_back(shared_ptr<date_io_t>(new date_io_t("%Y/%m/%d", true))); + readers.push_back(shared_ptr<date_io_t>(new date_io_t("%Y/%m", true))); + readers.push_back(shared_ptr<date_io_t>(new date_io_t("%y/%m/%d", true))); + + is_initialized = true; + } +} + +void times_shutdown() +{ + if (is_initialized) { + printed_datetime_io.reset(); + written_datetime_io.reset(); + input_date_io.reset(); + printed_date_io.reset(); + written_date_io.reset(); + + readers.clear(); + + foreach (datetime_io_map::value_type& pair, temp_datetime_io) + checked_delete(pair.second); + temp_datetime_io.clear(); + + foreach (date_io_map::value_type& pair, temp_date_io) + checked_delete(pair.second); + temp_date_io.clear(); + + is_initialized = false; + } +} } // namespace ledger diff --git a/src/times.h b/src/times.h index 247c9393..c77cde1d 100644 --- a/src/times.h +++ b/src/times.h @@ -74,7 +74,6 @@ inline bool is_valid(const date_t& moment) { #define CURRENT_DATE() boost::gregorian::day_clock::universal_day() extern date_time::weekdays start_of_week; -extern optional<std::string> input_date_format; optional<date_time::weekdays> string_to_day_of_week(const std::string& str); @@ -97,33 +96,20 @@ inline date_t parse_date(const std::string& str, return parse_date(str.c_str(), current_year); } -extern std::string output_datetime_format; +enum format_type_t { + FMT_WRITTEN, FMT_PRINTED, FMT_CUSTOM +}; -inline std::string format_datetime(const datetime_t& when, - const optional<std::string>& format = none) -{ - posix_time::time_facet * facet - (new posix_time::time_facet(format ? format->c_str() : - output_datetime_format.c_str())); - std::ostringstream buf; - buf.imbue(std::locale(std::locale::classic(), facet)); - buf << when; - return buf.str(); -} +std::string format_datetime(const datetime_t& when, + const format_type_t format_type = FMT_PRINTED, + const optional<const char *>& format = none); +void set_datetime_format(const char * format); -extern std::string output_date_format; - -inline std::string format_date(const date_t& when, - const optional<std::string>& format = none) -{ - gregorian::date_facet * facet - (new gregorian::date_facet(format ? format->c_str() : - output_date_format.c_str())); - std::ostringstream buf; - buf.imbue(std::locale(std::locale::classic(), facet)); - buf << when; - return buf.str(); -} +std::string format_date(const date_t& when, + const format_type_t format_type = FMT_PRINTED, + const optional<const char *>& format = none); +void set_date_format(const char * format); +void set_input_date_format(const char * format); class date_interval_t : public equality_comparable<date_interval_t> { @@ -207,6 +193,9 @@ public: date_interval_t& operator++(); }; +void times_initialize(); +void times_shutdown(); + std::ostream& operator<<(std::ostream& out, const date_interval_t::duration_t& duration); diff --git a/src/value.cc b/src/value.cc index 0f831cdf..6ce46f5c 100644 --- a/src/value.cc +++ b/src/value.cc @@ -984,7 +984,7 @@ void value_t::in_place_cast(type_t cast_type) set_datetime(datetime_t(as_date(), time_duration(0, 0, 0, 0))); return; case STRING: - set_string(format_date(as_date(), string("%Y-%m-%d"))); + set_string(format_date(as_date(), FMT_WRITTEN)); return; default: break; @@ -996,7 +996,7 @@ void value_t::in_place_cast(type_t cast_type) set_date(as_datetime().date()); return; case STRING: - set_string(format_datetime(as_datetime(), string("%Y-%m-%d %H:%M:%S"))); + set_string(format_datetime(as_datetime(), FMT_WRITTEN)); return; default: break; @@ -1495,16 +1495,17 @@ void value_t::print(std::ostream& out, case DATETIME: if (date_format) - out << format_datetime(as_datetime(), *date_format); + out << format_datetime(as_datetime(), FMT_CUSTOM, + date_format->c_str()); else - out << format_datetime(as_datetime()); + out << format_datetime(as_datetime(), FMT_WRITTEN); break; case DATE: if (date_format) - out << format_date(as_date(), *date_format); + out << format_date(as_date(), FMT_CUSTOM, date_format->c_str()); else - out << format_date(as_date()); + out << format_date(as_date(), FMT_WRITTEN); break; case INTEGER: @@ -1579,10 +1580,10 @@ void value_t::dump(std::ostream& out, const bool relaxed) const break; case DATETIME: - out << '[' << format_datetime(as_datetime()) << ']'; + out << '[' << format_datetime(as_datetime(), FMT_WRITTEN) << ']'; break; case DATE: - out << '[' << format_date(as_date()) << ']'; + out << '[' << format_date(as_date(), FMT_WRITTEN) << ']'; break; case INTEGER: diff --git a/src/value.h b/src/value.h index f6194c86..b40b9d28 100644 --- a/src/value.h +++ b/src/value.h @@ -934,7 +934,7 @@ public: #define NULL_VALUE (value_t()) -inline value_t string_value(const string& str) { +inline value_t string_value(const string& str = "") { return value_t(str, true); } |