[build] some clang-tidy fixes

pull/968/head
Timothy Stack 2 years ago
parent eb42ef6e77
commit 6fff9d60f5

@ -4,6 +4,8 @@
Checks: "*,\ Checks: "*,\
-google-readability-todo,\ -google-readability-todo,\
-altera-unroll-loops,\ -altera-unroll-loops,\
-altera-id-dependent-backward-branch,\
-altera-struct-pack-align,\
-fuchsia-*,\ -fuchsia-*,\
fuchsia-multiple-inheritance,\ fuchsia-multiple-inheritance,\
-llvm-header-guard,\ -llvm-header-guard,\
@ -50,15 +52,15 @@ CheckOptions:
- key: 'readability-identifier-naming.ClassCase' - key: 'readability-identifier-naming.ClassCase'
value: 'lower_case' value: 'lower_case'
- key: 'readability-identifier-naming.ClassConstantCase' - key: 'readability-identifier-naming.ClassConstantCase'
value: 'lower_case' value: 'UPPER_CASE'
- key: 'readability-identifier-naming.ClassMemberCase' - key: 'readability-identifier-naming.ClassMemberCase'
value: 'lower_case' value: 'lower_case'
- key: 'readability-identifier-naming.ClassMethodCase' - key: 'readability-identifier-naming.ClassMethodCase'
value: 'lower_case' value: 'lower_case'
- key: 'readability-identifier-naming.ConstantCase' - key: 'readability-identifier-naming.ConstantCase'
value: 'lower_case' value: 'UPPER_CASE'
- key: 'readability-identifier-naming.ConstantMemberCase' - key: 'readability-identifier-naming.ConstantMemberCase'
value: 'lower_case' value: 'UPPER_CASE'
- key: 'readability-identifier-naming.ConstantParameterCase' - key: 'readability-identifier-naming.ConstantParameterCase'
value: 'lower_case' value: 'lower_case'
- key: 'readability-identifier-naming.ConstantPointerParameterCase' - key: 'readability-identifier-naming.ConstantPointerParameterCase'
@ -76,7 +78,7 @@ CheckOptions:
- key: 'readability-identifier-naming.FunctionCase' - key: 'readability-identifier-naming.FunctionCase'
value: 'lower_case' value: 'lower_case'
- key: 'readability-identifier-naming.GlobalConstantCase' - key: 'readability-identifier-naming.GlobalConstantCase'
value: 'lower_case' value: 'UPPER_CASE'
- key: 'readability-identifier-naming.GlobalConstantPointerCase' - key: 'readability-identifier-naming.GlobalConstantPointerCase'
value: 'lower_case' value: 'lower_case'
- key: 'readability-identifier-naming.GlobalFunctionCase' - key: 'readability-identifier-naming.GlobalFunctionCase'
@ -111,14 +113,10 @@ CheckOptions:
value: 'lower_case' value: 'lower_case'
- key: 'readability-identifier-naming.PrivateMemberCase' - key: 'readability-identifier-naming.PrivateMemberCase'
value: 'lower_case' value: 'lower_case'
- key: 'readability-identifier-naming.PrivateMemberPrefix'
value: 'm_'
- key: 'readability-identifier-naming.PrivateMethodCase' - key: 'readability-identifier-naming.PrivateMethodCase'
value: 'lower_case' value: 'lower_case'
- key: 'readability-identifier-naming.ProtectedMemberCase' - key: 'readability-identifier-naming.ProtectedMemberCase'
value: 'lower_case' value: 'lower_case'
- key: 'readability-identifier-naming.ProtectedMemberPrefix'
value: 'm_'
- key: 'readability-identifier-naming.ProtectedMethodCase' - key: 'readability-identifier-naming.ProtectedMethodCase'
value: 'lower_case' value: 'lower_case'
- key: 'readability-identifier-naming.PublicMemberCase' - key: 'readability-identifier-naming.PublicMemberCase'
@ -128,7 +126,7 @@ CheckOptions:
- key: 'readability-identifier-naming.ScopedEnumConstantCase' - key: 'readability-identifier-naming.ScopedEnumConstantCase'
value: 'lower_case' value: 'lower_case'
- key: 'readability-identifier-naming.StaticConstantCase' - key: 'readability-identifier-naming.StaticConstantCase'
value: 'lower_case' value: 'UPPER_CASE'
- key: 'readability-identifier-naming.StaticVariableCase' - key: 'readability-identifier-naming.StaticVariableCase'
value: 'lower_case' value: 'lower_case'
- key: 'readability-identifier-naming.StructCase' - key: 'readability-identifier-naming.StructCase'
@ -151,4 +149,8 @@ CheckOptions:
value: 'lower_case' value: 'lower_case'
- key: 'readability-identifier-naming.VirtualMethodCase' - key: 'readability-identifier-naming.VirtualMethodCase'
value: 'lower_case' value: 'lower_case'
- key: 'readability-identifier-length.MinimumVariableNameLength'
value: '2'
- key: 'readability-identifier-length.MinimumParameterNameLength'
value: '2'
... ...

@ -3,6 +3,15 @@ lnav v0.10.2:
* Add initial support for pcap(3) files using tshark(1). * Add initial support for pcap(3) files using tshark(1).
* Add format for UniFi gateway. * Add format for UniFi gateway.
Breaking Changes:
* Added a 'language' column to the lnav_view_filters table that
specifies the language of the 'pattern' column, either 'regex'
or 'sql'.
Fixes:
* Toggling enabled/disabled filters when there is a SQL expression
no longer causes a crash.
lnav v0.10.1: lnav v0.10.1:
Features: Features:
* Added ":show-only-this-file" command that hides all files except the * Added ":show-only-this-file" command that hides all files except the

@ -1,6 +1,6 @@
# aminclude_static.am generated automatically by Autoconf # aminclude_static.am generated automatically by Autoconf
# from AX_AM_MACROS_STATIC on Fri Mar 4 21:53:04 PST 2022 # from AX_AM_MACROS_STATIC on Tue Mar 22 21:37:32 PDT 2022
# Code coverage # Code coverage

@ -211,6 +211,7 @@ add_library(
cppfmt STATIC cppfmt STATIC
fmtlib/format.cc fmtlib/format.cc
fmtlib/os.cc fmtlib/os.cc
fmtlib/fmt/args.h
fmtlib/fmt/chrono.h fmtlib/fmt/chrono.h
fmtlib/fmt/color.h fmtlib/fmt/color.h
fmtlib/fmt/compile.h fmtlib/fmt/compile.h
@ -220,10 +221,10 @@ add_library(
fmtlib/fmt/locale.h fmtlib/fmt/locale.h
fmtlib/fmt/os.h fmtlib/fmt/os.h
fmtlib/fmt/ostream.h fmtlib/fmt/ostream.h
fmtlib/fmt/posix.h
fmtlib/fmt/printf.h fmtlib/fmt/printf.h
fmtlib/fmt/ranges.h fmtlib/fmt/ranges.h
fmtlib/fmt/time.h) fmtlib/fmt/xchar.h
)
target_include_directories(cppfmt PUBLIC fmtlib) target_include_directories(cppfmt PUBLIC fmtlib)
add_library(lnavfileio STATIC add_library(lnavfileio STATIC
@ -461,6 +462,7 @@ target_link_libraries(
tailercommon tailercommon
logfmt logfmt
yajlpp yajlpp
cppfmt
${lnav_LIBS}) ${lnav_LIBS})
target_compile_definitions(diag PRIVATE SQLITE_OMIT_LOAD_EXTENSION) target_compile_definitions(diag PRIVATE SQLITE_OMIT_LOAD_EXTENSION)

@ -96,7 +96,8 @@ all_logs_vtab::extract(std::shared_ptr<logfile> lf,
values.emplace_back(this->alv_msg_meta, tsb.tsb_ref); values.emplace_back(this->alv_msg_meta, tsb.tsb_ref);
this->alv_schema_manager.invalidate_refs(); this->alv_schema_manager.invalidate_refs();
dp.dp_schema_id.to_string(this->alv_schema_buffer.data()); this->alv_schema_buffer.clear();
dp.dp_schema_id.to_string(std::back_inserter(this->alv_schema_buffer));
shared_buffer_ref schema_ref; shared_buffer_ref schema_ref;
schema_ref.share(this->alv_schema_manager, schema_ref.share(this->alv_schema_manager,
this->alv_schema_buffer.data(), this->alv_schema_buffer.data(),

@ -58,7 +58,8 @@ private:
logline_value_meta alv_msg_meta; logline_value_meta alv_msg_meta;
logline_value_meta alv_schema_meta; logline_value_meta alv_schema_meta;
shared_buffer alv_schema_manager; shared_buffer alv_schema_manager;
std::array<char, data_parser::schema_id_t::STRING_SIZE> alv_schema_buffer{}; fmt::basic_memory_buffer<char, data_parser::schema_id_t::STRING_SIZE>
alv_schema_buffer;
}; };
#endif // LNAV_ALL_LOGS_VTAB_HH #endif // LNAV_ALL_LOGS_VTAB_HH

@ -272,3 +272,34 @@ attr_line_t::apply_hide()
} }
} }
} }
line_range
line_range::intersection(const line_range& other) const
{
int actual_end;
if (this->lr_end == -1) {
actual_end = other.lr_end;
} else if (other.lr_end == -1) {
actual_end = this->lr_end;
} else {
actual_end = std::min(this->lr_end, other.lr_end);
}
return line_range{std::max(this->lr_start, other.lr_start), actual_end};
}
line_range&
line_range::shift(int32_t start, int32_t amount)
{
if (this->lr_start >= start) {
this->lr_start = std::max(0, this->lr_start + amount);
}
if (this->lr_end != -1 && start <= this->lr_end) {
this->lr_end += amount;
if (this->lr_end < this->lr_start) {
this->lr_end = this->lr_start;
}
}
return *this;
}

@ -77,34 +77,9 @@ struct line_range {
return this->contains(other.lr_start) || this->contains(other.lr_end); return this->contains(other.lr_start) || this->contains(other.lr_end);
}; };
line_range intersection(const struct line_range& other) const line_range intersection(const struct line_range& other) const;
{
int actual_end;
if (this->lr_end == -1) {
actual_end = other.lr_end;
} else if (other.lr_end == -1) {
actual_end = this->lr_end;
} else {
actual_end = std::min(this->lr_end, other.lr_end);
}
return line_range{std::max(this->lr_start, other.lr_start), actual_end};
};
line_range& shift(int32_t start, int32_t amount)
{
if (this->lr_start >= start) {
this->lr_start = std::max(0, this->lr_start + amount);
}
if (this->lr_end != -1 && start <= this->lr_end) {
this->lr_end += amount;
if (this->lr_end < this->lr_start) {
this->lr_end = this->lr_start;
}
}
return *this; line_range& shift(int32_t start, int32_t amount);
};
void ltrim(const char* str) void ltrim(const char* str)
{ {
@ -220,7 +195,7 @@ struct string_attr {
intern_string_t to_string() const intern_string_t to_string() const
{ {
return intern_string_t((const intern_string*) this->sa_value.sav_ptr); return {(const intern_string*) this->sa_value.sav_ptr};
}; };
struct line_range sa_range; struct line_range sa_range;
@ -230,7 +205,7 @@ struct string_attr {
}; };
/** A map of line ranges to attributes for that range. */ /** A map of line ranges to attributes for that range. */
typedef std::vector<string_attr> string_attrs_t; using string_attrs_t = std::vector<string_attr>;
inline string_attrs_t::const_iterator inline string_attrs_t::const_iterator
find_string_attr(const string_attrs_t& sa, find_string_attr(const string_attrs_t& sa,
@ -296,11 +271,11 @@ find_string_attr(string_attrs_t& sa, const struct line_range& lr)
inline string_attrs_t::const_iterator inline string_attrs_t::const_iterator
find_string_attr(const string_attrs_t& sa, size_t near) find_string_attr(const string_attrs_t& sa, size_t near)
{ {
string_attrs_t::const_iterator iter, nearest = sa.end(); auto nearest = sa.end();
ssize_t last_diff = INT_MAX; ssize_t last_diff = INT_MAX;
for (iter = sa.begin(); iter != sa.end(); ++iter) { for (auto iter = sa.begin(); iter != sa.end(); ++iter) {
auto& lr = iter->sa_range; const auto& lr = iter->sa_range;
if (!lr.is_valid() || !lr.contains(near)) { if (!lr.is_valid() || !lr.contains(near)) {
continue; continue;
@ -320,11 +295,11 @@ template<typename T>
inline string_attrs_t::const_iterator inline string_attrs_t::const_iterator
rfind_string_attr_if(const string_attrs_t& sa, ssize_t near, T predicate) rfind_string_attr_if(const string_attrs_t& sa, ssize_t near, T predicate)
{ {
string_attrs_t::const_iterator iter, nearest = sa.end(); auto nearest = sa.end();
ssize_t last_diff = INT_MAX; ssize_t last_diff = INT_MAX;
for (iter = sa.begin(); iter != sa.end(); ++iter) { for (auto iter = sa.begin(); iter != sa.end(); ++iter) {
auto& lr = iter->sa_range; const auto& lr = iter->sa_range;
if (lr.lr_start > near) { if (lr.lr_start > near) {
continue; continue;
@ -387,8 +362,6 @@ shift_string_attrs(string_attrs_t& sa, int32_t start, int32_t amount)
} }
struct text_wrap_settings { struct text_wrap_settings {
text_wrap_settings() : tws_indent(2), tws_width(80){};
text_wrap_settings& with_indent(int indent) text_wrap_settings& with_indent(int indent)
{ {
this->tws_indent = indent; this->tws_indent = indent;
@ -401,8 +374,8 @@ struct text_wrap_settings {
return *this; return *this;
}; };
int tws_indent; int tws_indent{2};
int tws_width; int tws_width{80};
}; };
/** /**

@ -32,10 +32,10 @@
#ifndef lnav_date_time_scanner_hh #ifndef lnav_date_time_scanner_hh
#define lnav_date_time_scanner_hh #define lnav_date_time_scanner_hh
#include <ctime>
#include <string> #include <string>
#include <sys/types.h> #include <sys/types.h>
#include <time.h>
#include "time_util.hh" #include "time_util.hh"

@ -56,7 +56,7 @@ public:
bool empty() const bool empty() const
{ {
return fr_size == 0; return this->fr_size == 0;
} }
}; };

@ -42,11 +42,13 @@ template<>
struct formatter<network::locality> { struct formatter<network::locality> {
constexpr auto parse(format_parse_context& ctx) constexpr auto parse(format_parse_context& ctx)
{ {
auto it = ctx.begin(), end = ctx.end(); const auto it = ctx.begin();
const auto end = ctx.end();
// Check if reached the end of the range: // Check if reached the end of the range:
if (it != end && *it != '}') if (it != end && *it != '}') {
throw format_error("invalid format"); throw format_error("invalid format");
}
// Return an iterator past the end of the parsed range: // Return an iterator past the end of the parsed range:
return it; return it;
@ -71,11 +73,13 @@ template<>
struct formatter<network::path> { struct formatter<network::path> {
constexpr auto parse(format_parse_context& ctx) constexpr auto parse(format_parse_context& ctx)
{ {
auto it = ctx.begin(), end = ctx.end(); const auto it = ctx.begin();
const auto end = ctx.end();
// Check if reached the end of the range: // Check if reached the end of the range:
if (it != end && *it != '}') if (it != end && *it != '}') {
throw format_error("invalid format"); throw format_error("invalid format");
}
// Return an iterator past the end of the parsed range: // Return an iterator past the end of the parsed range:
return it; return it;

@ -51,9 +51,6 @@ point::as_time_ago() const
{ {
struct timeval current_time struct timeval current_time
= this->p_recent_point.value_or(current_timeval()); = this->p_recent_point.value_or(current_timeval());
const char* fmt;
char buffer[64];
int amount;
if (this->p_convert_to_local) { if (this->p_convert_to_local) {
current_time.tv_sec = convert_log_time_to_local(current_time.tv_sec); current_time.tv_sec = convert_log_time_to_local(current_time.tv_sec);
@ -63,34 +60,36 @@ point::as_time_ago() const
= std::chrono::seconds(current_time.tv_sec - this->p_past_point.tv_sec); = std::chrono::seconds(current_time.tv_sec - this->p_past_point.tv_sec);
if (delta < 0s) { if (delta < 0s) {
return "in the future"; return "in the future";
} else if (delta < 1min) { }
if (delta < 1min) {
return "just now"; return "just now";
} else if (delta < 2min) { }
if (delta < 2min) {
return "one minute ago"; return "one minute ago";
} else if (delta < 1h) { }
fmt = "%d minutes ago"; if (delta < 1h) {
amount return fmt::format(
= std::chrono::duration_cast<std::chrono::minutes>(delta).count(); FMT_STRING("{} minutes ago"),
} else if (delta < 2h) { std::chrono::duration_cast<std::chrono::minutes>(delta).count());
}
if (delta < 2h) {
return "one hour ago"; return "one hour ago";
} else if (delta < 24h) { }
fmt = "%d hours ago"; if (delta < 24h) {
amount = std::chrono::duration_cast<std::chrono::hours>(delta).count(); return fmt::format(
} else if (delta < 48h) { FMT_STRING("{} hours ago"),
std::chrono::duration_cast<std::chrono::hours>(delta).count());
}
if (delta < 48h) {
return "one day ago"; return "one day ago";
} else if (delta < 365 * 24h) { }
fmt = "%d days ago"; if (delta < 365 * 24h) {
amount = delta / 24h; return fmt::format(FMT_STRING("{} days ago"), delta / 24h);
} else if (delta < (2 * 365 * 24h)) { }
if (delta < (2 * 365 * 24h)) {
return "over a year ago"; return "over a year ago";
} else {
fmt = "over %d years ago";
amount = delta / (365 * 24h);
} }
return fmt::format(FMT_STRING("over {} years ago"), delta / (365 * 24h));
snprintf(buffer, sizeof(buffer), fmt, amount);
return std::string(buffer);
} }
std::string std::string
@ -149,7 +148,7 @@ duration::to_string() const
{0, "%lld%s", "d"}, {0, "%lld%s", "d"},
}; };
auto* curr_interval = intervals; const auto* curr_interval = intervals;
auto usecs = std::chrono::duration_cast<std::chrono::microseconds>( auto usecs = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::seconds(this->d_timeval.tv_sec)) std::chrono::seconds(this->d_timeval.tv_sec))
+ std::chrono::microseconds(this->d_timeval.tv_usec); + std::chrono::microseconds(this->d_timeval.tv_usec);

@ -140,7 +140,7 @@ struct string_fragment {
bool startswith(const char* prefix) const bool startswith(const char* prefix) const
{ {
auto iter = this->begin(); const auto* iter = this->begin();
while (*prefix != '\0' && *prefix == *iter && iter < this->end()) { while (*prefix != '\0' && *prefix == *iter && iter < this->end()) {
prefix += 1; prefix += 1;

@ -55,8 +55,8 @@ struct path {
locality p_locality; locality p_locality;
std::string p_path; std::string p_path;
path(locality l, std::string path) path(locality loc, std::string path)
: p_locality(std::move(l)), p_path(std::move(path)) : p_locality(std::move(loc)), p_path(std::move(path))
{ {
} }

@ -37,6 +37,7 @@
#include <sys/types.h> #include <sys/types.h>
#include "base/lnav_log.hh" #include "base/lnav_log.hh"
#include "fmt/format.h"
template<size_t COUNT, typename T = unsigned char> template<size_t COUNT, typename T = unsigned char>
struct byte_array { struct byte_array {
@ -70,21 +71,21 @@ struct byte_array {
memset(this->ba_data, 0, BYTE_COUNT); memset(this->ba_data, 0, BYTE_COUNT);
}; };
void to_string(char* buffer) const template<typename OutputIt>
void to_string(OutputIt out) const
{ {
require(buffer != nullptr);
for (size_t lpc = 0; lpc < BYTE_COUNT; lpc++) { for (size_t lpc = 0; lpc < BYTE_COUNT; lpc++) {
snprintf(&buffer[lpc * 2], 3, "%02x", this->ba_data[lpc]); fmt::format_to(out, FMT_STRING("{:02x}"), this->ba_data[lpc]);
} }
}; }
std::string to_string() const std::string to_string() const
{ {
char buffer[STRING_SIZE]; std::string retval;
this->to_string(buffer); retval.reserve(STRING_SIZE);
return std::string(buffer); this->to_string(std::back_inserter(retval));
return retval;
} }
const unsigned char* in() const const unsigned char* in() const

@ -39,7 +39,7 @@
#include "config.h" #include "config.h"
#include "log_level.hh" #include "log_level.hh"
#define MAX_ADDR_LEN 128 constexpr int MAX_ADDR_LEN = 128;
static int static int
try_inet_pton(int p_len, const char* p, char* n) try_inet_pton(int p_len, const char* p, char* n)

@ -67,12 +67,10 @@ column_namer::existing_name(const std::string& in_name) const
std::string std::string
column_namer::add_column(const std::string& in_name) column_namer::add_column(const std::string& in_name)
{ {
std::string base_name = in_name, retval; auto base_name = in_name;
size_t buf_size; std::string retval;
int num = 0; int num = 0;
buf_size = in_name.length() + 64;
char buffer[buf_size];
if (in_name.empty()) { if (in_name.empty()) {
base_name = "col"; base_name = "col";
} }
@ -82,8 +80,7 @@ column_namer::add_column(const std::string& in_name)
auto counter_iter = this->cn_name_counters.find(retval); auto counter_iter = this->cn_name_counters.find(retval);
if (counter_iter != this->cn_name_counters.end()) { if (counter_iter != this->cn_name_counters.end()) {
num = ++counter_iter->second; num = ++counter_iter->second;
snprintf(buffer, buf_size, "%s_%d", base_name.c_str(), num); retval = fmt::format(FMT_STRING("{}_{}"), base_name, num);
retval = buffer;
} }
while (this->existing_name(retval)) { while (this->existing_name(retval)) {
@ -92,8 +89,7 @@ column_namer::add_column(const std::string& in_name)
} }
log_debug("column name already exists: %s", retval.c_str()); log_debug("column name already exists: %s", retval.c_str());
snprintf(buffer, buf_size, "%s_%d", base_name.c_str(), num); retval = fmt::format(FMT_STRING("{}_{}"), base_name, num);
retval = buffer;
num += 1; num += 1;
} }

@ -38,15 +38,13 @@
class column_namer { class column_namer {
public: public:
column_namer() : cn_builtin_names({"col"}) {}
bool existing_name(const std::string& in_name) const; bool existing_name(const std::string& in_name) const;
std::string add_column(const std::string& in_name); std::string add_column(const std::string& in_name);
std::vector<std::string> cn_builtin_names; std::vector<std::string> cn_builtin_names{"col"};
std::vector<std::string> cn_names; std::vector<std::string> cn_names{};
std::unordered_map<std::string, int> cn_name_counters; std::unordered_map<std::string, int> cn_name_counters{};
}; };
#endif #endif

@ -818,14 +818,10 @@ pipe_callback(exec_context& ec, const string& cmdline, auto_fd& fd)
.expect("Cannot create temporary file for callback") .expect("Cannot create temporary file for callback")
.second); .second);
static int exec_count = 0; static int exec_count = 0;
char desc[128];
lnav_data.ld_pipers.push_back(pp); lnav_data.ld_pipers.push_back(pp);
snprintf(desc, auto desc = fmt::format(
sizeof(desc), FMT_STRING("[{}] Output of {}"), exec_count++, cmdline);
"[%d] Output of %s",
exec_count++,
cmdline.c_str());
lnav_data.ld_active_files.fc_file_names[desc] lnav_data.ld_active_files.fc_file_names[desc]
.with_fd(pp->get_fd()) .with_fd(pp->get_fd())
.with_include_in_session(false) .with_include_in_session(false)

File diff suppressed because it is too large Load Diff

@ -102,9 +102,9 @@ public:
struct header_meta { struct header_meta {
explicit header_meta(std::string name) explicit header_meta(std::string name)
: hm_name(std::move(name)), hm_column_type(SQLITE3_TEXT), : hm_name(std::move(name)), hm_column_type(SQLITE3_TEXT),
hm_graphable(false), hm_log_time(false), hm_column_size(0){ hm_graphable(false), hm_log_time(false), hm_column_size(0)
{
}; }
bool operator==(const std::string& name) const bool operator==(const std::string& name) const
{ {

@ -103,10 +103,7 @@ map_elements_to_json2(yajl_gen gen,
= dp.get_element_string(iter.e_sub_elements->front()); = dp.get_element_string(iter.e_sub_elements->front());
if (key_str.empty()) { if (key_str.empty()) {
char buffer[32]; key_str = fmt::format(FMT_STRING("col_{}"), col);
snprintf(buffer, sizeof(buffer), "col_%d", col);
key_str = buffer;
col += 1; col += 1;
} }
root_map.gen(key_str); root_map.gen(key_str);

@ -40,7 +40,7 @@ using namespace std;
extern char** environ; extern char** environ;
const char* ENVIRON_CREATE_STMT = R"( const char* const ENVIRON_CREATE_STMT = R"(
-- Access lnav's environment variables through this table. -- Access lnav's environment variables through this table.
CREATE TABLE environ ( CREATE TABLE environ (
name text PRIMARY KEY, name text PRIMARY KEY,

@ -34,6 +34,6 @@
int register_environ_vtab(sqlite3* db); int register_environ_vtab(sqlite3* db);
extern const char* ENVIRON_CREATE_STMT; extern const char* const ENVIRON_CREATE_STMT;
#endif #endif

@ -332,16 +332,14 @@ field_overlay_source::build_field_lines(const listview_curses& lv)
orig_tv.tv_usec / 1000, orig_tv.tv_usec / 1000,
'T'); 'T');
if (offset_tv.tv_sec || offset_tv.tv_usec) { if (offset_tv.tv_sec || offset_tv.tv_usec) {
char offset_str[32];
time_str.append(" Pre-adjust Time: "); time_str.append(" Pre-adjust Time: ");
time_str.append(old_timestamp); time_str.append(old_timestamp);
snprintf(offset_str, fmt::format_to(std::back_inserter(time_str),
sizeof(offset_str), FMT_STRING(" Offset: {:+}.{:03}"),
" Offset: %+d.%03d", offset_tv.tv_sec,
(int) offset_tv.tv_sec, std::chrono::duration_cast<std::chrono::milliseconds>(
(int) (offset_tv.tv_usec / 1000)); std::chrono::microseconds(offset_tv.tv_usec))
time_str.append(offset_str); .count());
} }
if ((!this->fos_contexts.empty() && this->fos_contexts.top().c_show) if ((!this->fos_contexts.empty() && this->fos_contexts.top().c_show)

@ -128,17 +128,17 @@ file_collection::regenerate_unique_file_names()
} }
for (const auto& pair : this->fc_other_files) { for (const auto& pair : this->fc_other_files) {
switch (pair.second.ofd_format) { switch (pair.second.ofd_format) {
case file_format_t::FF_UNKNOWN: case file_format_t::UNKNOWN:
case file_format_t::FF_ARCHIVE: case file_format_t::ARCHIVE:
case file_format_t::FF_PCAP: case file_format_t::PCAP:
case file_format_t::FF_SQLITE_DB: { case file_format_t::SQLITE_DB: {
auto bn = ghc::filesystem::path(pair.first).filename().string(); auto bn = ghc::filesystem::path(pair.first).filename().string();
if (bn.length() > this->fc_largest_path_length) { if (bn.length() > this->fc_largest_path_length) {
this->fc_largest_path_length = bn.length(); this->fc_largest_path_length = bn.length();
} }
break; break;
} }
case file_format_t::FF_REMOTE: { case file_format_t::REMOTE: {
if (pair.first.length() > this->fc_largest_path_length) { if (pair.first.length() > this->fc_largest_path_length) {
this->fc_largest_path_length = pair.first.length(); this->fc_largest_path_length = pair.first.length();
} }
@ -306,11 +306,11 @@ file_collection::watch_logfile(const std::string& filename,
loo.loo_file_format = ff; loo.loo_file_format = ff;
switch (ff) { switch (ff) {
case file_format_t::FF_SQLITE_DB: case file_format_t::SQLITE_DB:
retval.fc_other_files[filename].ofd_format = ff; retval.fc_other_files[filename].ofd_format = ff;
break; break;
case file_format_t::FF_PCAP: { case file_format_t::PCAP: {
auto res = pcap_manager::convert(filename); auto res = pcap_manager::convert(filename);
if (res.isOk()) { if (res.isOk()) {
@ -363,7 +363,7 @@ file_collection::watch_logfile(const std::string& filename,
break; break;
} }
case file_format_t::FF_ARCHIVE: { case file_format_t::ARCHIVE: {
nonstd::optional< nonstd::optional<
std::list<archive_manager::extract_progress>::iterator> std::list<archive_manager::extract_progress>::iterator>
prog_iter_opt; prog_iter_opt;
@ -516,7 +516,7 @@ file_collection::expand_filename(
isc::to<tailer::looper&, services::remote_tailer_t>().send( isc::to<tailer::looper&, services::remote_tailer_t>().send(
[=](auto& tlooper) { tlooper.add_remote(rp, loo); }); [=](auto& tlooper) { tlooper.add_remote(rp, loo); });
retval.fc_other_files[path] = file_format_t::FF_REMOTE; retval.fc_other_files[path] = file_format_t::REMOTE;
{ {
this->fc_progress->writeAccess() this->fc_progress->writeAccess()
->sp_tailers[fmt::format("{}", rp.home())] ->sp_tailers[fmt::format("{}", rp.home())]

@ -61,7 +61,7 @@ struct other_file_descriptor {
file_format_t ofd_format; file_format_t ofd_format;
std::string ofd_description; std::string ofd_description;
other_file_descriptor(file_format_t format = file_format_t::FF_UNKNOWN, other_file_descriptor(file_format_t format = file_format_t::UNKNOWN,
std::string description = "") std::string description = "")
: ofd_format(format), ofd_description(std::move(description)) : ofd_format(format), ofd_description(std::move(description))
{ {

@ -93,10 +93,10 @@ file_format_t
detect_file_format(const ghc::filesystem::path& filename) detect_file_format(const ghc::filesystem::path& filename)
{ {
if (archive_manager::is_archive(filename)) { if (archive_manager::is_archive(filename)) {
return file_format_t::FF_ARCHIVE; return file_format_t::ARCHIVE;
} }
file_format_t retval = file_format_t::FF_UNKNOWN; file_format_t retval = file_format_t::UNKNOWN;
auto_fd fd; auto_fd fd;
if ((fd = lnav::filesystem::openp(filename, O_RDONLY)) != -1) { if ((fd = lnav::filesystem::openp(filename, O_RDONLY)) != -1) {
@ -108,9 +108,9 @@ detect_file_format(const ghc::filesystem::path& filename)
auto header_frag = string_fragment(buffer, 0, rc); auto header_frag = string_fragment(buffer, 0, rc);
if (header_frag.startswith(SQLITE3_HEADER)) { if (header_frag.startswith(SQLITE3_HEADER)) {
retval = file_format_t::FF_SQLITE_DB; retval = file_format_t::SQLITE_DB;
} else if (rc > 24 && is_pcap_header(buffer)) { } else if (rc > 24 && is_pcap_header(buffer)) {
retval = file_format_t::FF_PCAP; retval = file_format_t::PCAP;
} }
} }
} }

@ -36,11 +36,11 @@
#include "ghc/filesystem.hpp" #include "ghc/filesystem.hpp"
enum class file_format_t : int { enum class file_format_t : int {
FF_UNKNOWN, UNKNOWN,
FF_SQLITE_DB, SQLITE_DB,
FF_ARCHIVE, ARCHIVE,
FF_PCAP, PCAP,
FF_REMOTE, REMOTE,
}; };
file_format_t detect_file_format(const ghc::filesystem::path& filename); file_format_t detect_file_format(const ghc::filesystem::path& filename);
@ -53,16 +53,16 @@ struct formatter<file_format_t> : formatter<string_view> {
{ {
string_view name = "unknown"; string_view name = "unknown";
switch (ff) { switch (ff) {
case file_format_t::FF_SQLITE_DB: case file_format_t::SQLITE_DB:
name = "\U0001F5C2 SQLite DB"; name = "\U0001F5C2 SQLite DB";
break; break;
case file_format_t::FF_ARCHIVE: case file_format_t::ARCHIVE:
name = "\U0001F5C4 Archive"; name = "\U0001F5C4 Archive";
break; break;
case file_format_t::FF_PCAP: case file_format_t::PCAP:
name = "\U0001F5A5 Pcap"; name = "\U0001F5A5 Pcap";
break; break;
case file_format_t::FF_REMOTE: case file_format_t::REMOTE:
name = "\U0001F5A5 Remote"; name = "\U0001F5A5 Remote";
break; break;
default: default:

@ -39,8 +39,6 @@ class files_sub_source
public: public:
files_sub_source(); files_sub_source();
~files_sub_source() override = default;
bool list_input_handle_key(listview_curses& lv, int ch) override; bool list_input_handle_key(listview_curses& lv, int ch) override;
void list_input_handle_scroll_out(listview_curses& lv) override; void list_input_handle_scroll_out(listview_curses& lv) override;
@ -62,8 +60,6 @@ public:
int line, int line,
line_flags_t raw) override; line_flags_t raw) override;
bool fss_editing{false};
bool fss_filter_state{false};
size_t fss_last_line_len{0}; size_t fss_last_line_len{0};
}; };

@ -74,3 +74,39 @@ line_filter_observer::logline_eof(const logfile& lf)
iter->end_of_message(this->lfo_filter_state); iter->end_of_message(this->lfo_filter_state);
} }
} }
size_t
line_filter_observer::get_min_count(size_t max) const
{
size_t retval = max;
for (auto& filter : this->lfo_filter_stack) {
if (filter->lf_deleted) {
continue;
}
retval = std::min(
retval,
this->lfo_filter_state.tfs_filter_count[filter->get_index()]);
}
return retval;
}
void
line_filter_observer::clear_deleted_filter_state()
{
uint32_t used_mask = 0;
log_debug("filter stack %p", &this->lfo_filter_stack);
for (auto& filter : this->lfo_filter_stack) {
if (filter->lf_deleted) {
log_debug("skipping deleted %p %d %d",
filter.get(),
filter->get_index(),
filter->get_lang());
continue;
}
used_mask |= (1UL << filter->get_index());
}
this->lfo_filter_state.clear_deleted_filter_state(used_mask);
}

@ -38,23 +38,23 @@
class line_filter_observer : public logline_observer { class line_filter_observer : public logline_observer {
public: public:
line_filter_observer(filter_stack& fs, std::shared_ptr<logfile> lf) line_filter_observer(filter_stack& fs, std::shared_ptr<logfile> lf)
: lfo_filter_stack(fs), lfo_filter_state(lf){ : lfo_filter_stack(fs), lfo_filter_state(lf)
{
}; }
void logline_restart(const logfile& lf, file_size_t rollback_size) void logline_restart(const logfile& lf, file_size_t rollback_size) override
{ {
for (auto& filter : this->lfo_filter_stack) { for (auto& filter : this->lfo_filter_stack) {
filter->revert_to_last(this->lfo_filter_state, rollback_size); filter->revert_to_last(this->lfo_filter_state, rollback_size);
} }
}; }
void logline_new_lines(const logfile& lf, void logline_new_lines(const logfile& lf,
logfile::const_iterator ll_begin, logfile::const_iterator ll_begin,
logfile::const_iterator ll_end, logfile::const_iterator ll_end,
shared_buffer_ref& sbr); shared_buffer_ref& sbr) override;
void logline_eof(const logfile& lf); void logline_eof(const logfile& lf) override;
bool excluded(uint32_t filter_in_mask, bool excluded(uint32_t filter_in_mask,
uint32_t filter_out_mask, uint32_t filter_out_mask,
@ -65,37 +65,11 @@ public:
bool filtered_out bool filtered_out
= (this->lfo_filter_state.tfs_mask[offset] & filter_out_mask) != 0; = (this->lfo_filter_state.tfs_mask[offset] & filter_out_mask) != 0;
return !filtered_in || filtered_out; return !filtered_in || filtered_out;
}; }
size_t get_min_count(size_t max) const
{
size_t retval = max;
for (auto& filter : this->lfo_filter_stack) { size_t get_min_count(size_t max) const;
if (filter->lf_deleted) {
continue;
}
retval = std::min(
retval,
this->lfo_filter_state.tfs_filter_count[filter->get_index()]);
}
return retval; void clear_deleted_filter_state();
};
void clear_deleted_filter_state()
{
uint32_t used_mask = 0;
for (auto& filter : this->lfo_filter_stack) {
if (filter->lf_deleted) {
log_debug("skipping deleted %d", filter->get_index());
continue;
}
used_mask |= (1UL << filter->get_index());
}
this->lfo_filter_state.clear_deleted_filter_state(used_mask);
};
filter_stack& lfo_filter_stack; filter_stack& lfo_filter_stack;
logfile_filter_state lfo_filter_state; logfile_filter_state lfo_filter_state;

@ -310,7 +310,6 @@ filter_sub_source::text_value_for_line(textview_curses& tc,
text_sub_source* tss = top_view->get_sub_source(); text_sub_source* tss = top_view->get_sub_source();
filter_stack& fs = tss->get_filters(); filter_stack& fs = tss->get_filters();
shared_ptr<text_filter> tf = *(fs.begin() + line); shared_ptr<text_filter> tf = *(fs.begin() + line);
char hits[32];
value_out = " "; value_out = " ";
switch (tf->get_type()) { switch (tf->get_type()) {
@ -330,14 +329,13 @@ filter_sub_source::text_value_for_line(textview_curses& tc,
} }
if (this->fss_editing && line == tc.get_selection()) { if (this->fss_editing && line == tc.get_selection()) {
snprintf(hits, sizeof(hits), "%9s hits | ", "-"); fmt::format_to(
std::back_inserter(value_out), FMT_STRING("{:>9} hits | "), "-");
} else { } else {
snprintf(hits, fmt::format_to(std::back_inserter(value_out),
sizeof(hits), FMT_STRING("{:>9L} hits | "),
"%'9d hits | ", tss->get_filtered_count_for(tf->get_index()));
tss->get_filtered_count_for(tf->get_index()));
} }
value_out.append(hits);
value_out.append(tf->get_id()); value_out.append(tf->get_id());
} }
@ -384,6 +382,24 @@ filter_sub_source::text_attrs_for_line(textview_curses& tc,
value_out.emplace_back( value_out.emplace_back(
line_range{0, -1}, &view_curses::VC_ROLE, view_colors::VCR_FOCUSED); line_range{0, -1}, &view_curses::VC_ROLE, view_colors::VCR_FOCUSED);
} }
attr_line_t content{tf->get_id()};
auto& content_attrs = content.get_attrs();
switch (tf->get_lang()) {
case filter_lang_t::REGEX:
readline_regex_highlighter(content, content.length());
break;
case filter_lang_t::SQL:
readline_sqlite_highlighter(content, content.length());
break;
case filter_lang_t::NONE:
break;
}
shift_string_attrs(content_attrs, 0, 25);
value_out.insert(
value_out.end(), content_attrs.begin(), content_attrs.end());
} }
size_t size_t

@ -1,5 +1,6 @@
noinst_HEADERS = \ noinst_HEADERS = \
fmt/args.h \
fmt/chrono.h \ fmt/chrono.h \
fmt/color.h \ fmt/color.h \
fmt/compile.h \ fmt/compile.h \
@ -9,10 +10,9 @@ noinst_HEADERS = \
fmt/locale.h \ fmt/locale.h \
fmt/os.h \ fmt/os.h \
fmt/ostream.h \ fmt/ostream.h \
fmt/posix.h \
fmt/printf.h \ fmt/printf.h \
fmt/ranges.h \ fmt/ranges.h \
fmt/time.h fmt/xchar.h
noinst_LIBRARIES = libcppfmt.a noinst_LIBRARIES = libcppfmt.a

@ -0,0 +1,234 @@
// Formatting library for C++ - dynamic format arguments
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_ARGS_H_
#define FMT_ARGS_H_
#include <functional> // std::reference_wrapper
#include <memory> // std::unique_ptr
#include <vector>
#include "core.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T> struct is_reference_wrapper : std::false_type {};
template <typename T>
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
template <typename T> const T& unwrap(const T& v) { return v; }
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
return static_cast<const T&>(v);
}
class dynamic_arg_list {
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
// templates it doesn't complain about inability to deduce single translation
// unit for placing vtable. So storage_node_base is made a fake template.
template <typename = void> struct node {
virtual ~node() = default;
std::unique_ptr<node<>> next;
};
template <typename T> struct typed_node : node<> {
T value;
template <typename Arg>
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
template <typename Char>
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
: value(arg.data(), arg.size()) {}
};
std::unique_ptr<node<>> head_;
public:
template <typename T, typename Arg> const T& push(const Arg& arg) {
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
auto& value = new_node->value;
new_node->next = std::move(head_);
head_ = std::move(new_node);
return value;
}
};
} // namespace detail
/**
\rst
A dynamic version of `fmt::format_arg_store`.
It's equipped with a storage to potentially temporary objects which lifetimes
could be shorter than the format arguments object.
It can be implicitly converted into `~fmt::basic_format_args` for passing
into type-erased formatting functions such as `~fmt::vformat`.
\endrst
*/
template <typename Context>
class dynamic_format_arg_store
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround a GCC template argument substitution bug.
: public basic_format_args<Context>
#endif
{
private:
using char_type = typename Context::char_type;
template <typename T> struct need_copy {
static constexpr detail::type mapped_type =
detail::mapped_type_constant<T, Context>::value;
enum {
value = !(detail::is_reference_wrapper<T>::value ||
std::is_same<T, basic_string_view<char_type>>::value ||
std::is_same<T, detail::std_string_view<char_type>>::value ||
(mapped_type != detail::type::cstring_type &&
mapped_type != detail::type::string_type &&
mapped_type != detail::type::custom_type))
};
};
template <typename T>
using stored_type = conditional_t<detail::is_string<T>::value &&
!has_formatter<T, Context>::value &&
!detail::is_reference_wrapper<T>::value,
std::basic_string<char_type>, T>;
// Storage of basic_format_arg must be contiguous.
std::vector<basic_format_arg<Context>> data_;
std::vector<detail::named_arg_info<char_type>> named_info_;
// Storage of arguments not fitting into basic_format_arg must grow
// without relocation because items in data_ refer to it.
detail::dynamic_arg_list dynamic_args_;
friend class basic_format_args<Context>;
unsigned long long get_types() const {
return detail::is_unpacked_bit | data_.size() |
(named_info_.empty()
? 0ULL
: static_cast<unsigned long long>(detail::has_named_args_bit));
}
const basic_format_arg<Context>* data() const {
return named_info_.empty() ? data_.data() : data_.data() + 1;
}
template <typename T> void emplace_arg(const T& arg) {
data_.emplace_back(detail::make_arg<Context>(arg));
}
template <typename T>
void emplace_arg(const detail::named_arg<char_type, T>& arg) {
if (named_info_.empty()) {
constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
data_.insert(data_.begin(), {zero_ptr, 0});
}
data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
data->pop_back();
};
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
guard{&data_, pop_one};
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
guard.release();
}
public:
constexpr dynamic_format_arg_store() = default;
/**
\rst
Adds an argument into the dynamic store for later passing to a formatting
function.
Note that custom types and string types (but not string views) are copied
into the store dynamically allocating memory if necessary.
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
store.push_back(42);
store.push_back("abc");
store.push_back(1.5f);
std::string result = fmt::vformat("{} and {} and {}", store);
\endrst
*/
template <typename T> void push_back(const T& arg) {
if (detail::const_check(need_copy<T>::value))
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
else
emplace_arg(detail::unwrap(arg));
}
/**
\rst
Adds a reference to the argument into the dynamic store for later passing to
a formatting function.
**Example**::
fmt::dynamic_format_arg_store<fmt::format_context> store;
char band[] = "Rolling Stones";
store.push_back(std::cref(band));
band[9] = 'c'; // Changing str affects the output.
std::string result = fmt::vformat("{}", store);
// result == "Rolling Scones"
\endrst
*/
template <typename T> void push_back(std::reference_wrapper<T> arg) {
static_assert(
need_copy<T>::value,
"objects of built-in types and string views are always copied");
emplace_arg(arg.get());
}
/**
Adds named argument into the dynamic store for later passing to a formatting
function. ``std::reference_wrapper`` is supported to avoid copying of the
argument. The name is always copied into the store.
*/
template <typename T>
void push_back(const detail::named_arg<char_type, T>& arg) {
const char_type* arg_name =
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
if (detail::const_check(need_copy<T>::value)) {
emplace_arg(
fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
} else {
emplace_arg(fmt::arg(arg_name, arg.value));
}
}
/** Erase all elements from the store */
void clear() {
data_.clear();
named_info_.clear();
dynamic_args_ = detail::dynamic_arg_list();
}
/**
\rst
Reserves space to store at least *new_cap* arguments including
*new_cap_named* named arguments.
\endrst
*/
void reserve(size_t new_cap, size_t new_cap_named) {
FMT_ASSERT(new_cap >= new_cap_named,
"Set of arguments includes set of named arguments");
data_.reserve(new_cap);
named_info_.reserve(new_cap_named);
}
};
FMT_END_NAMESPACE
#endif // FMT_ARGS_H_

File diff suppressed because it is too large Load Diff

@ -10,7 +10,15 @@
#include "format.h" #include "format.h"
// __declspec(deprecated) is broken in some MSVC versions.
#if FMT_MSC_VER
# define FMT_DEPRECATED_NONMSVC
#else
# define FMT_DEPRECATED_NONMSVC FMT_DEPRECATED
#endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
enum class color : uint32_t { enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255) alice_blue = 0xF0F8FF, // rgb(240,248,255)
@ -177,9 +185,13 @@ enum class terminal_color : uint8_t {
enum class emphasis : uint8_t { enum class emphasis : uint8_t {
bold = 1, bold = 1,
italic = 1 << 1, faint = 1 << 1,
underline = 1 << 2, italic = 1 << 2,
strikethrough = 1 << 3 underline = 1 << 3,
blink = 1 << 4,
reverse = 1 << 5,
conceal = 1 << 6,
strikethrough = 1 << 7,
}; };
// rgb is a struct for red, green and blue colors. // rgb is a struct for red, green and blue colors.
@ -198,7 +210,7 @@ struct rgb {
uint8_t b; uint8_t b;
}; };
namespace detail { FMT_BEGIN_DETAIL_NAMESPACE
// color is a struct of either a rgb color or a terminal color. // color is a struct of either a rgb color or a terminal color.
struct color_type { struct color_type {
@ -221,9 +233,10 @@ struct color_type {
uint32_t rgb_color; uint32_t rgb_color;
} value; } value;
}; };
} // namespace detail
// Experimental text formatting support. FMT_END_DETAIL_NAMESPACE
/** A text style consisting of foreground and background colors and emphasis. */
class text_style { class text_style {
public: public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
@ -260,33 +273,14 @@ class text_style {
return lhs |= rhs; return lhs |= rhs;
} }
FMT_CONSTEXPR text_style& operator&=(const text_style& rhs) { FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=(
if (!set_foreground_color) { const text_style& rhs) {
set_foreground_color = rhs.set_foreground_color; return and_assign(rhs);
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
}
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
static_cast<uint8_t>(rhs.ems));
return *this;
} }
friend FMT_CONSTEXPR text_style operator&(text_style lhs, FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style
const text_style& rhs) { operator&(text_style lhs, const text_style& rhs) {
return lhs &= rhs; return lhs.and_assign(rhs);
} }
FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT { FMT_CONSTEXPR bool has_foreground() const FMT_NOEXCEPT {
@ -326,8 +320,34 @@ class text_style {
} }
} }
// DEPRECATED!
FMT_CONSTEXPR text_style& and_assign(const text_style& rhs) {
if (!set_foreground_color) {
set_foreground_color = rhs.set_foreground_color;
foreground_color = rhs.foreground_color;
} else if (rhs.set_foreground_color) {
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
}
if (!set_background_color) {
set_background_color = rhs.set_background_color;
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
FMT_THROW(format_error("can't AND a terminal color"));
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
}
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) &
static_cast<uint8_t>(rhs.ems));
return *this;
}
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground) friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
FMT_NOEXCEPT; FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background) friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
FMT_NOEXCEPT; FMT_NOEXCEPT;
@ -338,19 +358,22 @@ class text_style {
emphasis ems; emphasis ems;
}; };
FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT { /** Creates a text style from the foreground (text) color. */
return text_style(/*is_foreground=*/true, foreground); FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
return text_style(true, foreground);
} }
FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT { /** Creates a text style from the background color. */
return text_style(/*is_foreground=*/false, background); FMT_CONSTEXPR inline text_style bg(detail::color_type background) FMT_NOEXCEPT {
return text_style(false, background);
} }
FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT { FMT_CONSTEXPR inline text_style operator|(emphasis lhs,
emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs; return text_style(lhs) | rhs;
} }
namespace detail { FMT_BEGIN_DETAIL_NAMESPACE
template <typename Char> struct ansi_color_escape { template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color, FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
@ -358,7 +381,7 @@ template <typename Char> struct ansi_color_escape {
// If we have a terminal color, we need to output another escape code // If we have a terminal color, we need to output another escape code
// sequence. // sequence.
if (!text_color.is_rgb) { if (!text_color.is_rgb) {
bool is_background = esc == detail::data::background_color; bool is_background = esc == string_view("\x1b[48;2;");
uint32_t value = text_color.value.term_color; uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with // Background ASCII codes are the same as the foreground ones but with
// 10 more. // 10 more.
@ -390,16 +413,18 @@ template <typename Char> struct ansi_color_escape {
buffer[19] = static_cast<Char>(0); buffer[19] = static_cast<Char>(0);
} }
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT { FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
uint8_t em_codes[4] = {}; uint8_t em_codes[num_emphases] = {};
uint8_t em_bits = static_cast<uint8_t>(em); if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1; if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3; if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3;
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4; if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4;
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough)) if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5;
em_codes[3] = 9; if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7;
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
size_t index = 0; size_t index = 0;
for (int i = 0; i < 4; ++i) { for (size_t i = 0; i < num_emphases; ++i) {
if (!em_codes[i]) continue; if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b'); buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('['); buffer[index++] = static_cast<Char>('[');
@ -411,12 +436,13 @@ template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; } FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; } FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT { FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const FMT_NOEXCEPT {
return buffer + std::char_traits<Char>::length(buffer); return buffer + std::char_traits<Char>::length(buffer);
} }
private: private:
Char buffer[7u + 3u * 4u + 1u]; static constexpr size_t num_emphases = 8;
Char buffer[7u + 3u * num_emphases + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out, static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) FMT_NOEXCEPT { char delimiter) FMT_NOEXCEPT {
@ -425,18 +451,22 @@ template <typename Char> struct ansi_color_escape {
out[2] = static_cast<Char>('0' + c % 10); out[2] = static_cast<Char>('0' + c % 10);
out[3] = static_cast<Char>(delimiter); out[3] = static_cast<Char>(delimiter);
} }
static FMT_CONSTEXPR bool has_emphasis(emphasis em,
emphasis mask) FMT_NOEXCEPT {
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
}
}; };
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color( FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
detail::color_type foreground) FMT_NOEXCEPT { detail::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, detail::data::foreground_color); return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
} }
template <typename Char> template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color( FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
detail::color_type background) FMT_NOEXCEPT { detail::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, detail::data::background_color); return ansi_color_escape<Char>(background, "\x1b[48;2;");
} }
template <typename Char> template <typename Char>
@ -455,18 +485,17 @@ inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
} }
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT { template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
fputs(detail::data::reset_color, stream); fputs("\x1b[0m", stream);
} }
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT { template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(detail::data::wreset_color, stream); fputs(L"\x1b[0m", stream);
} }
template <typename Char> template <typename Char>
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT { inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
const char* begin = data::reset_color; auto reset_color = string_view("\x1b[0m");
const char* end = begin + sizeof(data::reset_color) - 1; buffer.append(reset_color.begin(), reset_color.end());
buffer.append(begin, end);
} }
template <typename Char> template <typename Char>
@ -489,10 +518,11 @@ void vformat_to(buffer<Char>& buf, const text_style& ts,
auto background = detail::make_background_color<Char>(ts.get_background()); auto background = detail::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end()); buf.append(background.begin(), background.end());
} }
detail::vformat_to(buf, format_str, args); detail::vformat_to(buf, format_str, args, {});
if (has_style) detail::reset_color<Char>(buf); if (has_style) detail::reset_color<Char>(buf);
} }
} // namespace detail
FMT_END_DETAIL_NAMESPACE
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format, void vprint(std::FILE* f, const text_style& ts, const S& format,
@ -523,11 +553,15 @@ void print(std::FILE* f, const text_style& ts, const S& format_str,
} }
/** /**
\rst
Formats a string and prints it to stdout using ANSI escape sequences to Formats a string and prints it to stdout using ANSI escape sequences to
specify text formatting. specify text formatting.
Example:
**Example**::
fmt::print(fmt::emphasis::bold | fg(fmt::color::red), fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23); "Elapsed time: {0:.2f} seconds", 1.23);
\endrst
*/ */
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)> FMT_ENABLE_IF(detail::is_string<S>::value)>
@ -559,8 +593,8 @@ inline std::basic_string<Char> vformat(
template <typename S, typename... Args, typename Char = char_t<S>> template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const text_style& ts, const S& format_str, inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
const Args&... args) { const Args&... args) {
return vformat(ts, to_string_view(format_str), return fmt::vformat(ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...)); fmt::make_args_checked<Args...>(format_str, args...));
} }
/** /**
@ -571,7 +605,7 @@ template <typename OutputIt, typename Char,
OutputIt vformat_to( OutputIt vformat_to(
OutputIt out, const text_style& ts, basic_string_view<Char> format_str, OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out)); auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, ts, format_str, args); detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf); return detail::get_iterator(buf);
} }
@ -598,6 +632,7 @@ inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
fmt::make_args_checked<Args...>(format_str, args...)); fmt::make_args_checked<Args...>(format_str, args...));
} }
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_COLOR_H_ #endif // FMT_COLOR_H_

@ -8,362 +8,178 @@
#ifndef FMT_COMPILE_H_ #ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_ #define FMT_COMPILE_H_
#include <vector>
#include "format.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
// A compile-time string which is compiled into fast formatting code. // An output iterator that counts the number of objects written to it and
class compiled_string {}; // discards them.
class counting_iterator {
template <typename S> private:
struct is_compiled_string : std::is_base_of<compiled_string, S> {}; size_t count_;
/**
\rst
Converts a string literal *s* into a format string that will be parsed at
compile time and converted into efficient formatting code. Requires C++17
``constexpr if`` compiler support.
**Example**::
// Converts 42 into std::string using the most efficient method and no
// runtime format string processing.
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
*/
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string)
template <typename T, typename... Tail>
const T& first(const T& value, const Tail&...) {
return value;
}
// Part of a compiled format string. It can be either literal text or a
// replacement field.
template <typename Char> struct format_part {
enum class kind { arg_index, arg_name, text, replacement };
struct replacement { public:
arg_ref<Char> arg_id; using iterator_category = std::output_iterator_tag;
dynamic_format_specs<Char> specs; using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
using _Unchecked_type = counting_iterator; // Mark iterator as checked.
struct value_type {
template <typename T> void operator=(const T&) {}
}; };
kind part_kind; counting_iterator() : count_(0) {}
union value {
int arg_index;
basic_string_view<Char> str;
replacement repl;
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
FMT_CONSTEXPR value(replacement r) : repl(r) {}
} val;
// Position past the end of the argument id.
const Char* arg_id_end = nullptr;
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {}) size_t count() const { return count_; }
: part_kind(k), val(v) {}
static FMT_CONSTEXPR format_part make_arg_index(int index) { counting_iterator& operator++() {
return format_part(kind::arg_index, index); ++count_;
} return *this;
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
return format_part(kind::arg_name, name);
} }
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) { counting_iterator operator++(int) {
return format_part(kind::text, text); auto it = *this;
++*this;
return it;
} }
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
return format_part(kind::replacement, repl); friend counting_iterator operator+(counting_iterator it, difference_type n) {
it.count_ += static_cast<size_t>(n);
return it;
} }
value_type operator*() const { return {}; }
}; };
template <typename Char> struct part_counter { template <typename Char, typename InputIt>
unsigned num_parts = 0; inline counting_iterator copy_str(InputIt begin, InputIt end,
counting_iterator it) {
return it + (end - begin);
}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) { template <typename OutputIt> class truncating_iterator_base {
if (begin != end) ++num_parts; protected:
} OutputIt out_;
size_t limit_;
size_t count_ = 0;
FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; } truncating_iterator_base() : out_(), limit_(0) {}
FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; }
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
return ++num_parts, 0;
}
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {} truncating_iterator_base(OutputIt out, size_t limit)
: out_(out), limit_(limit) {}
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned brace_counter = 0;
for (; begin != end; ++begin) {
if (*begin == '{') {
++brace_counter;
} else if (*begin == '}') {
if (brace_counter == 0u) break;
--brace_counter;
}
}
return begin;
}
FMT_CONSTEXPR void on_error(const char*) {} public:
using iterator_category = std::output_iterator_tag;
using value_type = typename std::iterator_traits<OutputIt>::value_type;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
using _Unchecked_type =
truncating_iterator_base; // Mark iterator as checked.
OutputIt base() const { return out_; }
size_t count() const { return count_; }
}; };
// Counts the number of parts in a format string. // An output iterator that truncates the output and counts the number of objects
template <typename Char> // written to it.
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) { template <typename OutputIt,
part_counter<Char> counter; typename Enable = typename std::is_void<
parse_format_string<true>(format_str, counter); typename std::iterator_traits<OutputIt>::value_type>::type>
return counter.num_parts; class truncating_iterator;
}
template <typename Char, typename PartHandler>
class format_string_compiler : public error_handler {
private:
using part = format_part<Char>;
PartHandler handler_; template <typename OutputIt>
part part_; class truncating_iterator<OutputIt, std::false_type>
basic_string_view<Char> format_str_; : public truncating_iterator_base<OutputIt> {
basic_format_parse_context<Char> parse_context_; mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
public: public:
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str, using value_type = typename truncating_iterator_base<OutputIt>::value_type;
PartHandler handler)
: handler_(handler),
format_str_(format_str),
parse_context_(format_str) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end)
handler_(part::make_text({begin, to_unsigned(end - begin)}));
}
FMT_CONSTEXPR int on_arg_id() { truncating_iterator() = default;
part_ = part::make_arg_index(parse_context_.next_arg_id());
return 0;
}
FMT_CONSTEXPR int on_arg_id(int id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
return 0;
}
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) { truncating_iterator(OutputIt out, size_t limit)
part_ = part::make_arg_name(id); : truncating_iterator_base<OutputIt>(out, limit) {}
return 0;
}
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) { truncating_iterator& operator++() {
part_.arg_id_end = ptr; if (this->count_++ < this->limit_) ++this->out_;
handler_(part_); return *this;
} }
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin, truncating_iterator operator++(int) {
const Char* end) { auto it = *this;
auto repl = typename part::replacement(); ++*this;
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
repl.specs, parse_context_);
auto it = parse_format_specs(begin, end, handler);
if (*it != '}') on_error("missing '}' in format string");
repl.arg_id = part_.part_kind == part::kind::arg_index
? arg_ref<Char>(part_.val.arg_index)
: arg_ref<Char>(part_.val.str);
auto part = part::make_replacement(repl);
part.arg_id_end = begin;
handler_(part);
return it; return it;
} }
};
// Compiles a format string and invokes handler(part) for each parsed part.
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
PartHandler handler) {
parse_format_string<IS_CONSTEXPR>(
format_str,
format_string_compiler<Char, PartHandler>(format_str, handler));
}
template <typename OutputIt, typename Context, typename Id> value_type& operator*() const {
void format_arg( return this->count_ < this->limit_ ? *this->out_ : blackhole_;
basic_format_parse_context<typename Context::char_type>& parse_ctx,
Context& ctx, Id arg_id) {
ctx.advance_to(visit_format_arg(
arg_formatter<OutputIt, typename Context::char_type>(ctx, &parse_ctx),
ctx.arg(arg_id)));
}
// vformat_to is defined in a subnamespace to prevent ADL.
namespace cf {
template <typename Context, typename OutputIt, typename CompiledFormat>
auto vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) -> typename Context::iterator {
using char_type = typename Context::char_type;
basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_));
Context ctx(out, args);
const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts);
++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
using format_part_t = format_part<char_type>;
switch (part.part_kind) {
case format_part_t::kind::text: {
const auto text = value.str;
auto output = ctx.out();
auto&& it = reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
break;
}
case format_part_t::kind::arg_index:
advance_to(parse_ctx, part.arg_id_end);
detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index);
break;
case format_part_t::kind::arg_name:
advance_to(parse_ctx, part.arg_id_end);
detail::format_arg<OutputIt>(parse_ctx, ctx, value.str);
break;
case format_part_t::kind::replacement: {
const auto& arg_id_value = value.repl.arg_id.val;
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.name);
auto specs = value.repl.specs;
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
handle_dynamic_spec<precision_checker>(specs.precision,
specs.precision_ref, ctx);
error_handler h;
numeric_specs_checker<error_handler> checker(h, arg.type());
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(
visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>(
ctx, nullptr, &specs),
arg));
break;
}
}
} }
return ctx.out(); };
}
} // namespace cf
struct basic_compiled_format {};
template <typename S, typename = void> template <typename OutputIt>
struct compiled_format_base : basic_compiled_format { class truncating_iterator<OutputIt, std::true_type>
using char_type = char_t<S>; : public truncating_iterator_base<OutputIt> {
using parts_container = std::vector<detail::format_part<char_type>>; public:
truncating_iterator() = default;
parts_container compiled_parts; truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
explicit compiled_format_base(basic_string_view<char_type> format_str) { template <typename T> truncating_iterator& operator=(T val) {
compile_format_string<false>(format_str, if (this->count_++ < this->limit_) *this->out_++ = val;
[this](const format_part<char_type>& part) { return *this;
compiled_parts.push_back(part);
});
} }
const parts_container& parts() const { return compiled_parts; } truncating_iterator& operator++() { return *this; }
}; truncating_iterator& operator++(int) { return *this; }
truncating_iterator& operator*() { return *this; }
template <typename Char, unsigned N> struct format_part_array {
format_part<Char> data[N] = {};
FMT_CONSTEXPR format_part_array() = default;
}; };
template <typename Char, unsigned N> // A compile-time string which is compiled into fast formatting code.
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts( class compiled_string {};
basic_string_view<Char> format_str) {
format_part_array<Char, N> parts;
unsigned counter = 0;
// This is not a lambda for compatibility with older compilers.
struct {
format_part<Char>* parts;
unsigned* counter;
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
parts[(*counter)++] = part;
}
} collector{parts.data, &counter};
compile_format_string<true>(format_str, collector);
if (counter < N) {
parts.data[counter] =
format_part<Char>::make_text(basic_string_view<Char>());
}
return parts;
}
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
template <typename S> template <typename S>
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>> struct is_compiled_string : std::is_base_of<compiled_string, S> {};
: basic_compiled_format {
using char_type = char_t<S>;
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {} /**
\rst
Converts a string literal *s* into a format string that will be parsed at
compile time and converted into efficient formatting code. Requires C++17
``constexpr if`` compiler support.
// Workaround for old compilers. Format string compilation will not be **Example**::
// performed there anyway.
#if FMT_USE_CONSTEXPR // Converts 42 into std::string using the most efficient method and no
static FMT_CONSTEXPR_DECL const unsigned num_format_parts = // runtime format string processing.
constexpr_max(count_parts(to_string_view(S())), 1u); std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
*/
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
# define FMT_COMPILE(s) \
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
#else #else
static const unsigned num_format_parts = 1; # define FMT_COMPILE(s) FMT_STRING(s)
#endif #endif
using parts_container = format_part<char_type>[num_format_parts]; #if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
template <typename Char, size_t N,
const parts_container& parts() const { fmt::detail_exported::fixed_string<Char, N> Str>
static FMT_CONSTEXPR_DECL const auto compiled_parts = struct udl_compiled_string : compiled_string {
compile_to_parts<char_type, num_format_parts>( using char_type = Char;
detail::to_string_view(S())); constexpr operator basic_string_view<char_type>() const {
return compiled_parts.data; return {Str.data, N - 1};
} }
}; };
#endif
template <typename S, typename... Args> template <typename T, typename... Tail>
class compiled_format : private compiled_format_base<S> { const T& first(const T& value, const Tail&...) {
public: return value;
using typename compiled_format_base<S>::char_type; }
private:
basic_string_view<char_type> format_str_;
template <typename Context, typename OutputIt, typename CompiledFormat>
friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) ->
typename Context::iterator;
public:
compiled_format() = delete;
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
: compiled_format_base<S>(format_str), format_str_(format_str) {}
};
#ifdef __cpp_if_constexpr #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename... Args> struct type_list {}; template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...]. // Returns a reference to the argument at index N from [first, rest...].
@ -374,13 +190,20 @@ constexpr const auto& get([[maybe_unused]] const T& first,
if constexpr (N == 0) if constexpr (N == 0)
return first; return first;
else else
return get<N - 1>(rest...); return detail::get<N - 1>(rest...);
}
template <typename Char, typename... Args>
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
type_list<Args...>) {
return get_arg_index_by_name<Args...>(name);
} }
template <int N, typename> struct get_type_impl; template <int N, typename> struct get_type_impl;
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> { template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
using type = remove_cvref_t<decltype(get<N>(std::declval<Args>()...))>; using type =
remove_cvref_t<decltype(detail::get<N>(std::declval<Args>()...))>;
}; };
template <int N, typename T> template <int N, typename T>
@ -393,7 +216,7 @@ template <typename Char> struct text {
using char_type = Char; using char_type = Char;
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const { constexpr OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, data); return write<Char>(out, data);
} }
}; };
@ -412,11 +235,22 @@ template <typename Char> struct code_unit {
using char_type = Char; using char_type = Char;
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const { constexpr OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, value); return write<Char>(out, value);
} }
}; };
// This ensures that the argument type is convertible to `const T&`.
template <typename T, int N, typename... Args>
constexpr const T& get_arg_checked(const Args&... args) {
const auto& arg = detail::get<N>(args...);
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
return arg.value;
} else {
return arg;
}
}
template <typename Char> template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {}; struct is_compiled_format<code_unit<Char>> : std::true_type {};
@ -425,29 +259,58 @@ template <typename Char, typename T, int N> struct field {
using char_type = Char; using char_type = Char;
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const { constexpr OutputIt format(OutputIt out, const Args&... args) const {
// This ensures that the argument type is convertile to `const T&`. return write<Char>(out, get_arg_checked<T, N>(args...));
const T& arg = get<N>(args...);
return write<Char>(out, arg);
} }
}; };
template <typename Char, typename T, int N> template <typename Char, typename T, int N>
struct is_compiled_format<field<Char, T, N>> : std::true_type {}; struct is_compiled_format<field<Char, T, N>> : std::true_type {};
// A replacement field that refers to argument with name.
template <typename Char> struct runtime_named_field {
using char_type = Char;
basic_string_view<Char> name;
template <typename OutputIt, typename T>
constexpr static bool try_format_argument(
OutputIt& out,
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
if (arg_name == arg.name) {
out = write<Char>(out, arg.value);
return true;
}
}
return false;
}
template <typename OutputIt, typename... Args>
constexpr OutputIt format(OutputIt out, const Args&... args) const {
bool found = (try_format_argument(out, name, args) || ...);
if (!found) {
FMT_THROW(format_error("argument with specified name is not found"));
}
return out;
}
};
template <typename Char>
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
// A replacement field that refers to argument N and has format specifiers. // A replacement field that refers to argument N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field { template <typename Char, typename T, int N> struct spec_field {
using char_type = Char; using char_type = Char;
mutable formatter<T, Char> fmt; formatter<T, Char> fmt;
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const { constexpr FMT_INLINE OutputIt format(OutputIt out,
// This ensures that the argument type is convertile to `const T&`. const Args&... args) const {
const T& arg = get<N>(args...);
const auto& vargs = const auto& vargs =
make_format_args<basic_format_context<OutputIt, Char>>(args...); fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs); basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(arg, ctx); return fmt.format(get_arg_checked<T, N>(args...), ctx);
} }
}; };
@ -460,7 +323,7 @@ template <typename L, typename R> struct concat {
using char_type = typename L::char_type; using char_type = typename L::char_type;
template <typename OutputIt, typename... Args> template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const { constexpr OutputIt format(OutputIt out, const Args&... args) const {
out = lhs.format(out, args...); out = lhs.format(out, args...);
return rhs.format(out, args...); return rhs.format(out, args...);
} }
@ -508,14 +371,79 @@ template <typename T, typename Char> struct parse_specs_result {
int next_arg_id; int next_arg_id;
}; };
constexpr int manual_indexing_id = -1;
template <typename T, typename Char> template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str, constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int arg_id) { size_t pos, int next_arg_id) {
str.remove_prefix(pos); str.remove_prefix(pos);
auto ctx = basic_format_parse_context<Char>(str, {}, arg_id + 1); auto ctx = basic_format_parse_context<Char>(str, {}, next_arg_id);
auto f = formatter<T, Char>(); auto f = formatter<T, Char>();
auto end = f.parse(ctx); auto end = f.parse(ctx);
return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()}; return {f, pos + fmt::detail::to_unsigned(end - str.data()) + 1,
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
}
template <typename Char> struct arg_id_handler {
arg_ref<Char> arg_id;
constexpr int operator()() {
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
return 0;
}
constexpr int operator()(int id) {
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr int operator()(basic_string_view<Char> id) {
arg_id = arg_ref<Char>(id);
return 0;
}
constexpr void on_error(const char* message) {
FMT_THROW(format_error(message));
}
};
template <typename Char> struct parse_arg_id_result {
arg_ref<Char> arg_id;
const Char* arg_id_end;
};
template <int ID, typename Char>
constexpr auto parse_arg_id(const Char* begin, const Char* end) {
auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
auto arg_id_end = parse_arg_id(begin, end, handler);
return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
}
template <typename T, typename Enable = void> struct field_type {
using type = remove_cvref_t<T>;
};
template <typename T>
struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
using type = remove_cvref_t<decltype(T::value)>;
};
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
typename S>
constexpr auto parse_replacement_field_then_tail(S format_str) {
using char_type = typename S::char_type;
constexpr auto str = basic_string_view<char_type>(format_str);
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
if constexpr (c == '}') {
return parse_tail<Args, END_POS + 1, NEXT_ID>(
field<char_type, typename field_type<T>::type, ARG_INDEX>(),
format_str);
} else if constexpr (c == ':') {
constexpr auto result = parse_specs<typename field_type<T>::type>(
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
return parse_tail<Args, result.end, result.next_arg_id>(
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
result.fmt},
format_str);
}
} }
// Compiles a non-empty format string and returns the compiled representation // Compiles a non-empty format string and returns the compiled representation
@ -523,27 +451,59 @@ constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
template <typename Args, size_t POS, int ID, typename S> template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str) { constexpr auto compile_format_string(S format_str) {
using char_type = typename S::char_type; using char_type = typename S::char_type;
constexpr basic_string_view<char_type> str = format_str; constexpr auto str = basic_string_view<char_type>(format_str);
if constexpr (str[POS] == '{') { if constexpr (str[POS] == '{') {
if (POS + 1 == str.size()) if constexpr (POS + 1 == str.size())
throw format_error("unmatched '{' in format string"); FMT_THROW(format_error("unmatched '{' in format string"));
if constexpr (str[POS + 1] == '{') { if constexpr (str[POS + 1] == '{') {
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else if constexpr (str[POS + 1] == '}') { } else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
using type = get_type<ID, Args>; static_assert(ID != manual_indexing_id,
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(), "cannot switch from manual to automatic argument indexing");
format_str); constexpr auto next_id =
} else if constexpr (str[POS + 1] == ':') { ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
using type = get_type<ID, Args>; return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
constexpr auto result = parse_specs<type>(str, POS + 2, ID); POS + 1, ID, next_id>(
return parse_tail<Args, result.end, result.next_arg_id>( format_str);
spec_field<char_type, type, ID>{result.fmt}, format_str);
} else { } else {
return unknown_format(); constexpr auto arg_id_result =
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
constexpr char_type c =
arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
static_assert(c == '}' || c == ':', "missing '}' in format string");
if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
static_assert(
ID == manual_indexing_id || ID == 0,
"cannot switch from automatic to manual argument indexing");
constexpr auto arg_index = arg_id_result.arg_id.val.index;
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
Args, arg_id_end_pos,
arg_index, manual_indexing_id>(
format_str);
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
constexpr auto arg_index =
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
if constexpr (arg_index != invalid_arg_index) {
constexpr auto next_id =
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
return parse_replacement_field_then_tail<
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
arg_index, next_id>(format_str);
} else {
if constexpr (c == '}') {
return parse_tail<Args, arg_id_end_pos + 1, ID>(
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
format_str);
} else if constexpr (c == ':') {
return unknown_format(); // no type info for specs parsing
}
}
}
} }
} else if constexpr (str[POS] == '}') { } else if constexpr (str[POS] == '}') {
if (POS + 1 == str.size()) if constexpr (POS + 1 == str.size())
throw format_error("unmatched '}' in format string"); FMT_THROW(format_error("unmatched '}' in format string"));
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str); return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
} else { } else {
constexpr auto end = parse_text(str, POS + 1); constexpr auto end = parse_text(str, POS + 1);
@ -558,144 +518,125 @@ constexpr auto compile_format_string(S format_str) {
} }
template <typename... Args, typename S, template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value || FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) { constexpr auto compile(S format_str) {
constexpr basic_string_view<typename S::char_type> str = format_str; constexpr auto str = basic_string_view<typename S::char_type>(format_str);
if constexpr (str.size() == 0) { if constexpr (str.size() == 0) {
return detail::make_text(str, 0, 0); return detail::make_text(str, 0, 0);
} else { } else {
constexpr auto result = constexpr auto result =
detail::compile_format_string<detail::type_list<Args...>, 0, 0>( detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
format_str); format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(result)>, return result;
detail::unknown_format>()) {
return detail::compiled_format<S, Args...>(to_string_view(format_str));
} else {
return result;
}
} }
} }
#else #endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value)>
constexpr auto compile(S format_str) -> detail::compiled_format<S, Args...> {
return detail::compiled_format<S, Args...>(to_string_view(format_str));
}
#endif // __cpp_if_constexpr
// Compiles the format string which must be a string literal.
template <typename... Args, typename Char, size_t N>
auto compile(const Char (&format_str)[N])
-> detail::compiled_format<const Char*, Args...> {
return detail::compiled_format<const Char*, Args...>(
basic_string_view<Char>(format_str, N - 1));
}
} // namespace detail } // namespace detail
// DEPRECATED! use FMT_COMPILE instead. FMT_MODULE_EXPORT_BEGIN
template <typename... Args>
FMT_DEPRECATED auto compile(const Args&... args)
-> decltype(detail::compile(args...)) {
return detail::compile(args...);
}
#if FMT_USE_CONSTEXPR #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
# ifdef __cpp_if_constexpr
template <typename CompiledFormat, typename... Args, template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type, typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)> FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf, FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
const Args&... args) { const Args&... args) {
basic_memory_buffer<Char> buffer; auto s = std::basic_string<Char>();
cf.format(detail::buffer_appender<Char>(buffer), args...); cf.format(std::back_inserter(s), args...);
return to_string(buffer); return s;
} }
template <typename OutputIt, typename CompiledFormat, typename... Args, template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)> FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf, constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) { const Args&... args) {
return cf.format(out, args...); return cf.format(out, args...);
} }
# endif // __cpp_if_constexpr
#endif // FMT_USE_CONSTEXPR
template <typename CompiledFormat, typename... Args,
typename Char = typename CompiledFormat::char_type,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
std::basic_string<Char> format(const CompiledFormat& cf, const Args&... args) {
basic_memory_buffer<Char> buffer;
using context = buffer_context<Char>;
detail::cf::vformat_to<context>(detail::buffer_appender<Char>(buffer), cf,
make_format_args<context>(args...));
return to_string(buffer);
}
template <typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
FMT_INLINE std::basic_string<typename S::char_type> format(const S&, FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
Args&&... args) { Args&&... args) {
#ifdef __cpp_if_constexpr
if constexpr (std::is_same<typename S::char_type, char>::value) { if constexpr (std::is_same<typename S::char_type, char>::value) {
constexpr basic_string_view<typename S::char_type> str = S(); constexpr auto str = basic_string_view<typename S::char_type>(S());
if (str.size() == 2 && str[0] == '{' && str[1] == '}') if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
return fmt::to_string(detail::first(args...)); const auto& first = detail::first(args...);
if constexpr (detail::is_named_arg<
remove_cvref_t<decltype(first)>>::value) {
return fmt::to_string(first.value);
} else {
return fmt::to_string(first);
}
}
} }
#endif
constexpr auto compiled = detail::compile<Args...>(S()); constexpr auto compiled = detail::compile<Args...>(S());
return format(compiled, std::forward<Args>(args)...); if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
} detail::unknown_format>()) {
return format(static_cast<basic_string_view<typename S::char_type>>(S()),
template <typename OutputIt, typename CompiledFormat, typename... Args, std::forward<Args>(args)...);
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format, } else {
CompiledFormat>::value)> return format(compiled, std::forward<Args>(args)...);
OutputIt format_to(OutputIt out, const CompiledFormat& cf, }
const Args&... args) {
using char_type = typename CompiledFormat::char_type;
using context = format_context_t<OutputIt, char_type>;
return detail::cf::vformat_to<context>(out, cf,
make_format_args<context>(args...));
} }
template <typename OutputIt, typename S, typename... Args, template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
OutputIt format_to(OutputIt out, const S&, const Args&... args) { FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<Args...>(S()); constexpr auto compiled = detail::compile<Args...>(S());
return format_to(out, compiled, args...); if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return format_to(out,
static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else {
return format_to(out, compiled, std::forward<Args>(args)...);
}
} }
#endif
template <typename OutputIt, typename CompiledFormat, typename... Args> template <typename OutputIt, typename S, typename... Args,
auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf, FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
const Args&... args) -> format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
typename std::enable_if< const S& format_str, Args&&... args) {
detail::is_output_iterator<OutputIt, auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), format_str,
typename CompiledFormat::char_type>::value && std::forward<Args>(args)...);
std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value,
format_to_n_result<OutputIt>>::type {
auto it =
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
return {it.base(), it.count()}; return {it.base(), it.count()};
} }
template <typename OutputIt, typename S, typename... Args, template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)> FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S&, size_t formatted_size(const S& format_str, const Args&... args) {
const Args&... args) { return format_to(detail::counting_iterator(), format_str, args...).count();
constexpr auto compiled = detail::compile<Args...>(S());
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), compiled,
args...);
return {it.base(), it.count()};
} }
template <typename CompiledFormat, typename... Args> template <typename S, typename... Args,
size_t formatted_size(const CompiledFormat& cf, const Args&... args) { FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
return format_to(detail::counting_iterator(), cf, args...).count(); void print(std::FILE* f, const S& format_str, const Args&... args) {
memory_buffer buffer;
format_to(std::back_inserter(buffer), format_str, args...);
detail::print(f, {buffer.data(), buffer.size()});
} }
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
void print(const S& format_str, const Args&... args) {
print(stdout, format_str, args...);
}
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
inline namespace literals {
template <detail_exported::fixed_string Str>
constexpr detail::udl_compiled_string<
remove_cvref_t<decltype(Str.data[0])>,
sizeof(Str.data) / sizeof(decltype(Str.data[0])), Str>
operator""_cf() {
return {};
}
} // namespace literals
#endif
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_COMPILE_H_ #endif // FMT_COMPILE_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -1,64 +1,2 @@
// Formatting library for C++ - std::locale support #include "xchar.h"
// #warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_LOCALE_H_
#define FMT_LOCALE_H_
#include <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename Char>
std::basic_string<Char> vformat(
const std::locale& loc, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer;
detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
return fmt::to_string(buffer);
}
} // namespace detail
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
return detail::vformat(loc, to_string_view(format_str), args);
}
template <typename S, typename... Args, typename Char = char_t<S>>
inline std::basic_string<Char> format(const std::locale& loc,
const S& format_str, Args&&... args) {
return detail::vformat(loc, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
template <typename S, typename OutputIt, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
inline OutputIt vformat_to(
OutputIt out, const std::locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
decltype(detail::get_buffer<Char>(out)) buf(detail::get_buffer_init(out));
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
return detail::get_iterator(buf);
}
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value>
inline auto format_to(OutputIt out, const std::locale& loc,
const S& format_str, Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to(out, loc, to_string_view(format_str), vargs);
}
FMT_END_NAMESPACE
#endif // FMT_LOCALE_H_

@ -8,16 +8,12 @@
#ifndef FMT_OS_H_ #ifndef FMT_OS_H_
#define FMT_OS_H_ #define FMT_OS_H_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <cerrno> #include <cerrno>
#include <clocale> // for locale_t #include <clocale> // locale_t
#include <cstddef> #include <cstddef>
#include <cstdio> #include <cstdio>
#include <cstdlib> // for strtod_l #include <cstdlib> // strtod_l
#include <system_error> // std::system_error
#if defined __APPLE__ || defined(__FreeBSD__) #if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X # include <xlocale.h> // for LC_NUMERIC_MASK on OS X
@ -25,17 +21,20 @@
#include "format.h" #include "format.h"
#ifndef FMT_USE_FCNTL
// UWP doesn't provide _pipe. // UWP doesn't provide _pipe.
#if FMT_HAS_INCLUDE("winapifamily.h") # if FMT_HAS_INCLUDE("winapifamily.h")
# include <winapifamily.h> # include <winapifamily.h>
#endif # endif
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \ # if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
defined(__linux__)) && \ defined(__linux__)) && \
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP)) (!defined(WINAPI_FAMILY) || \
# include <fcntl.h> // for O_RDONLY (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
# define FMT_USE_FCNTL 1 # include <fcntl.h> // for O_RDONLY
#else # define FMT_USE_FCNTL 1
# define FMT_USE_FCNTL 0 # else
# define FMT_USE_FCNTL 0
# endif
#endif #endif
#ifndef FMT_POSIX #ifndef FMT_POSIX
@ -74,6 +73,7 @@
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1) #define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
FMT_MODULE_EXPORT_BEGIN
/** /**
\rst \rst
@ -122,19 +122,28 @@ template <typename Char> class basic_cstring_view {
using cstring_view = basic_cstring_view<char>; using cstring_view = basic_cstring_view<char>;
using wcstring_view = basic_cstring_view<wchar_t>; using wcstring_view = basic_cstring_view<wchar_t>;
// An error code. template <typename Char> struct formatter<std::error_code, Char> {
class error_code { template <typename ParseContext>
private: FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
int value_; return ctx.begin();
}
public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; } template <typename FormatContext>
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
-> decltype(ctx.out()) {
auto out = ctx.out();
out = detail::write_bytes(out, ec.category().name(),
basic_format_specs<Char>());
out = detail::write<Char>(out, Char(':'));
out = detail::write<Char>(out, ec.value());
return out;
}
}; };
#ifdef _WIN32 #ifdef _WIN32
namespace detail { FMT_API const std::error_category& system_category() FMT_NOEXCEPT;
FMT_BEGIN_DETAIL_NAMESPACE
// A converter from UTF-16 to UTF-8. // A converter from UTF-16 to UTF-8.
// It is only provided for Windows since other systems support UTF-8 natively. // It is only provided for Windows since other systems support UTF-8 natively.
class utf16_to_utf8 { class utf16_to_utf8 {
@ -143,7 +152,7 @@ class utf16_to_utf8 {
public: public:
utf16_to_utf8() {} utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(wstring_view s); FMT_API explicit utf16_to_utf8(basic_string_view<wchar_t> s);
operator string_view() const { return string_view(&buffer_[0], size()); } operator string_view() const { return string_view(&buffer_[0], size()); }
size_t size() const { return buffer_.size() - 1; } size_t size() const { return buffer_.size() - 1; }
const char* c_str() const { return &buffer_[0]; } const char* c_str() const { return &buffer_[0]; }
@ -152,59 +161,68 @@ class utf16_to_utf8 {
// Performs conversion returning a system error code instead of // Performs conversion returning a system error code instead of
// throwing exception on conversion error. This method may still throw // throwing exception on conversion error. This method may still throw
// in case of memory allocation error. // in case of memory allocation error.
FMT_API int convert(wstring_view s); FMT_API int convert(basic_string_view<wchar_t> s);
}; };
FMT_API void format_windows_error(buffer<char>& out, int error_code, FMT_API void format_windows_error(buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT; const char* message) FMT_NOEXCEPT;
} // namespace detail FMT_END_DETAIL_NAMESPACE
/** A Windows error. */ FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
class windows_error : public system_error { format_args args);
private:
FMT_API void init(int error_code, string_view format_str, format_args args);
public: /**
/** \rst
\rst Constructs a :class:`std::system_error` object with the description
Constructs a :class:`fmt::windows_error` object with the description of the form
of the form
.. parsed-literal::
.. parsed-literal:: *<message>*: *<system-message>*
*<message>*: *<system-message>*
where *<message>* is the formatted message and *<system-message>* is the
where *<message>* is the formatted message and *<system-message>* is the system message corresponding to the error code.
system message corresponding to the error code. *error_code* is a Windows error code as given by ``GetLastError``.
*error_code* is a Windows error code as given by ``GetLastError``. If *error_code* is not a valid error code such as -1, the system message
If *error_code* is not a valid error code such as -1, the system message will look like "error -1".
will look like "error -1".
**Example**::
**Example**::
// This throws a system_error with the description
// This throws a windows_error with the description // cannot open file 'madeup': The system cannot find the file specified.
// cannot open file 'madeup': The system cannot find the file specified. // or similar (system message may vary).
// or similar (system message may vary). const char *filename = "madeup";
const char *filename = "madeup"; LPOFSTRUCT of = LPOFSTRUCT();
LPOFSTRUCT of = LPOFSTRUCT(); HFILE file = OpenFile(filename, &of, OF_READ);
HFILE file = OpenFile(filename, &of, OF_READ); if (file == HFILE_ERROR) {
if (file == HFILE_ERROR) { throw fmt::windows_error(GetLastError(),
throw fmt::windows_error(GetLastError(), "cannot open file '{}'", filename);
"cannot open file '{}'", filename); }
} \endrst
\endrst */
*/ template <typename... Args>
template <typename... Args> std::system_error windows_error(int error_code, string_view message,
windows_error(int error_code, string_view message, const Args&... args) { const Args&... args) {
init(error_code, message, make_format_args(args...)); return vwindows_error(error_code, message, fmt::make_format_args(args...));
} }
};
// Reports a Windows error without throwing an exception. // Reports a Windows error without throwing an exception.
// Can be used to report errors from destructors. // Can be used to report errors from destructors.
FMT_API void report_windows_error(int error_code, FMT_API void report_windows_error(int error_code,
string_view message) FMT_NOEXCEPT; const char* message) FMT_NOEXCEPT;
#else
inline const std::error_category& system_category() FMT_NOEXCEPT {
return std::system_category();
}
#endif // _WIN32 #endif // _WIN32
// std::system is not available on some platforms such as iOS (#2248).
#ifdef __OSX__
template <typename S, typename... Args, typename Char = char_t<S>>
void say(const S& format_str, Args&&... args) {
std::system(format("say \"{}\"", format(format_str, args...)).c_str());
}
#endif
// A buffered file. // A buffered file.
class buffered_file { class buffered_file {
private: private:
@ -255,7 +273,7 @@ class buffered_file {
template <typename... Args> template <typename... Args>
inline void print(string_view format_str, const Args&... args) { inline void print(string_view format_str, const Args&... args) {
vprint(format_str, make_format_args(args...)); vprint(format_str, fmt::make_format_args(args...));
} }
}; };
@ -280,7 +298,8 @@ class file {
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only. WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing. RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist. CREATE = FMT_POSIX(O_CREAT), // Create if the file doesn't exist.
APPEND = FMT_POSIX(O_APPEND) // Open in append mode. APPEND = FMT_POSIX(O_APPEND), // Open in append mode.
TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file.
}; };
// Constructs a file object which doesn't represent any file. // Constructs a file object which doesn't represent any file.
@ -295,7 +314,8 @@ class file {
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; } file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) FMT_NOEXCEPT { // Move assignment is not noexcept because close may throw.
file& operator=(file&& other) {
close(); close();
fd_ = other.fd_; fd_ = other.fd_;
other.fd_ = -1; other.fd_ = -1;
@ -331,7 +351,7 @@ class file {
// Makes fd be the copy of this file descriptor, closing fd first if // Makes fd be the copy of this file descriptor, closing fd first if
// necessary. // necessary.
FMT_API void dup2(int fd, error_code& ec) FMT_NOEXCEPT; FMT_API void dup2(int fd, std::error_code& ec) FMT_NOEXCEPT;
// Creates a pipe setting up read_end and write_end file objects for reading // Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively. // and writing respectively.
@ -345,9 +365,10 @@ class file {
// Returns the memory page size. // Returns the memory page size.
long getpagesize(); long getpagesize();
namespace detail { FMT_BEGIN_DETAIL_NAMESPACE
struct buffer_size { struct buffer_size {
buffer_size() = default;
size_t value = 0; size_t value = 0;
buffer_size operator=(size_t val) const { buffer_size operator=(size_t val) const {
auto bs = buffer_size(); auto bs = buffer_size();
@ -357,14 +378,14 @@ struct buffer_size {
}; };
struct ostream_params { struct ostream_params {
int oflag = file::WRONLY | file::CREATE; int oflag = file::WRONLY | file::CREATE | file::TRUNC;
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768; size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
ostream_params() {} ostream_params() {}
template <typename... T> template <typename... T>
ostream_params(T... params, int oflag) : ostream_params(params...) { ostream_params(T... params, int new_oflag) : ostream_params(params...) {
this->oflag = oflag; oflag = new_oflag;
} }
template <typename... T> template <typename... T>
@ -372,23 +393,27 @@ struct ostream_params {
: ostream_params(params...) { : ostream_params(params...) {
this->buffer_size = bs.value; this->buffer_size = bs.value;
} }
// Intel has a bug that results in failure to deduce a constructor
// for empty parameter packs.
# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
ostream_params(int new_oflag) : oflag(new_oflag) {}
ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
# endif
}; };
} // namespace detail
static constexpr detail::buffer_size buffer_size; FMT_END_DETAIL_NAMESPACE
// Added {} below to work around default constructor error known to
// occur in Xcode versions 7.2.1 and 8.2.1.
constexpr detail::buffer_size buffer_size{};
// A fast output stream which is not thread-safe. /** A fast output stream which is not thread-safe. */
class ostream final : private detail::buffer<char> { class FMT_API ostream final : private detail::buffer<char> {
private: private:
file file_; file file_;
void flush() { void grow(size_t) override;
if (size() == 0) return;
file_.write(data(), size());
clear();
}
FMT_API void grow(size_t) override final;
ostream(cstring_view path, const detail::ostream_params& params) ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) { : file_(path, params.oflag) {
@ -399,6 +424,7 @@ class ostream final : private detail::buffer<char> {
ostream(ostream&& other) ostream(ostream&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()), : detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) { file_(std::move(other.file_)) {
other.clear();
other.set(nullptr, 0); other.set(nullptr, 0);
} }
~ostream() { ~ostream() {
@ -406,6 +432,12 @@ class ostream final : private detail::buffer<char> {
delete[] data(); delete[] data();
} }
void flush() {
if (size() == 0) return;
file_.write(data(), size());
clear();
}
template <typename... T> template <typename... T>
friend ostream output_file(cstring_view path, T... params); friend ostream output_file(cstring_view path, T... params);
@ -414,16 +446,30 @@ class ostream final : private detail::buffer<char> {
file_.close(); file_.close();
} }
template <typename S, typename... Args> /**
void print(const S& format_str, const Args&... args) { Formats ``args`` according to specifications in ``fmt`` and writes the
format_to(detail::buffer_appender<char>(*this), format_str, args...); output to the file.
*/
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
vformat_to(detail::buffer_appender<char>(*this), fmt,
fmt::make_format_args(args...));
} }
}; };
/** /**
Opens a file for writing. Supported parameters passed in `params`: \rst
* ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default) Opens a file for writing. Supported parameters passed in *params*:
* ``<integer>``: Flags passed to `open
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
(``file::WRONLY | file::CREATE`` by default)
* ``buffer_size=<integer>``: Output buffer size * ``buffer_size=<integer>``: Output buffer size
**Example**::
auto out = fmt::output_file("guide.txt");
out.print("Don't {}", "Panic");
\endrst
*/ */
template <typename... T> template <typename... T>
inline ostream output_file(cstring_view path, T... params) { inline ostream output_file(cstring_view path, T... params) {
@ -466,7 +512,7 @@ class locale {
// Converts string to floating-point number and advances str past the end // Converts string to floating-point number and advances str past the end
// of the parsed input. // of the parsed input.
double strtod(const char*& str) const { FMT_DEPRECATED double strtod(const char*& str) const {
char* end = nullptr; char* end = nullptr;
double result = strtod_l(str, &end, locale_); double result = strtod_l(str, &end, locale_);
str = end; str = end;
@ -475,6 +521,7 @@ class locale {
}; };
using Locale FMT_DEPRECATED_ALIAS = locale; using Locale FMT_DEPRECATED_ALIAS = locale;
#endif // FMT_LOCALE #endif // FMT_LOCALE
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_OS_H_ #endif // FMT_OS_H_

@ -14,81 +14,44 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <typename Char> class basic_printf_parse_context;
template <typename OutputIt, typename Char> class basic_printf_context; template <typename OutputIt, typename Char> class basic_printf_context;
namespace detail { namespace detail {
template <class Char> class formatbuf : public std::basic_streambuf<Char> { // Checks if T has a user-defined operator<<.
private: template <typename T, typename Char, typename Enable = void>
using int_type = typename std::basic_streambuf<Char>::int_type; class is_streamable {
using traits_type = typename std::basic_streambuf<Char>::traits_type;
buffer<Char>& buffer_;
public:
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
protected:
// The put-area is actually always empty. This makes the implementation
// simpler and has the advantage that the streambuf and the buffer are always
// in sync and sputc never writes into uninitialized memory. The obvious
// disadvantage is that each call to sputc always results in a (virtual) call
// to overflow. There is no disadvantage here for sputn since this always
// results in a call to xsputn.
int_type overflow(int_type ch = traits_type::eof()) FMT_OVERRIDE {
if (!traits_type::eq_int_type(ch, traits_type::eof()))
buffer_.push_back(static_cast<Char>(ch));
return ch;
}
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
buffer_.append(s, s + count);
return count;
}
};
struct converter {
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
};
template <typename Char> struct test_stream : std::basic_ostream<Char> {
private:
void_t<> operator<<(converter);
};
// Hide insertion operators for built-in types.
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
// Checks if T has a user-defined operator<< (e.g. not a member of
// std::ostream).
template <typename T, typename Char> class is_streamable {
private: private:
template <typename U> template <typename U>
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>() static auto test(int)
<< std::declval<U>()), -> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>()
void_t<>>::value> << std::declval<U>()) != 0>;
test(int);
template <typename> static std::false_type test(...); template <typename> static auto test(...) -> std::false_type;
using result = decltype(test<T>(0)); using result = decltype(test<T>(0));
public: public:
is_streamable() = default;
static const bool value = result::value; static const bool value = result::value;
}; };
// Formatting of built-in types and arrays is intentionally disabled because
// it's handled by standard (non-ostream) formatters.
template <typename T, typename Char>
struct is_streamable<
T, Char,
enable_if_t<
std::is_arithmetic<T>::value || std::is_array<T>::value ||
std::is_pointer<T>::value || std::is_same<T, char8_type>::value ||
std::is_same<T, std::basic_string<Char>>::value ||
std::is_same<T, std_string_view<Char>>::value ||
(std::is_convertible<T, int>::value && !std::is_enum<T>::value)>>
: std::false_type {};
// Write the content of buf to os. // Write the content of buf to os.
// It is a separate function rather than a part of vprint to simplify testing.
template <typename Char> template <typename Char>
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) { void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data(); const Char* buf_data = buf.data();
@ -106,8 +69,8 @@ void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
template <typename Char, typename T> template <typename Char, typename T>
void format_value(buffer<Char>& buf, const T& value, void format_value(buffer<Char>& buf, const T& value,
locale_ref loc = locale_ref()) { locale_ref loc = locale_ref()) {
formatbuf<Char> format_buf(buf); auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
std::basic_ostream<Char> output(&format_buf); auto&& output = std::basic_ostream<Char>(&format_buf);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) #if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>()); if (loc) output.imbue(loc.get<std::locale>());
#endif #endif
@ -120,39 +83,33 @@ void format_value(buffer<Char>& buf, const T& value,
template <typename T, typename Char> template <typename T, typename Char>
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>> struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
: private formatter<basic_string_view<Char>, Char> { : private formatter<basic_string_view<Char>, Char> {
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx) using formatter<basic_string_view<Char>, Char>::parse;
-> decltype(ctx.begin()) {
return formatter<basic_string_view<Char>, Char>::parse(ctx);
}
template <typename ParseCtx,
FMT_ENABLE_IF(std::is_same<
ParseCtx, basic_printf_parse_context<Char>>::value)>
auto parse(ParseCtx& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename OutputIt> template <typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
-> OutputIt { -> OutputIt {
basic_memory_buffer<Char> buffer; auto buffer = basic_memory_buffer<Char>();
format_value(buffer, value, ctx.locale()); format_value(buffer, value, ctx.locale());
basic_string_view<Char> str(buffer.data(), buffer.size()); return formatter<basic_string_view<Char>, Char>::format(
return formatter<basic_string_view<Char>, Char>::format(str, ctx); {buffer.data(), buffer.size()}, ctx);
} }
// DEPRECATED!
template <typename OutputIt> template <typename OutputIt>
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx) auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
-> OutputIt { -> OutputIt {
basic_memory_buffer<Char> buffer; auto buffer = basic_memory_buffer<Char>();
format_value(buffer, value, ctx.locale()); format_value(buffer, value, ctx.locale());
return std::copy(buffer.begin(), buffer.end(), ctx.out()); return std::copy(buffer.begin(), buffer.end(), ctx.out());
} }
}; };
} // namespace detail } // namespace detail
FMT_MODULE_EXPORT
template <typename Char> template <typename Char>
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str, void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) { basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buffer; auto buffer = basic_memory_buffer<Char>();
detail::vformat_to(buffer, format_str, args); detail::vformat_to(buffer, format_str, args);
detail::write_buffer(os, buffer); detail::write_buffer(os, buffer);
} }
@ -166,6 +123,7 @@ void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
fmt::print(cerr, "Don't {}!", "panic"); fmt::print(cerr, "Don't {}!", "panic");
\endrst \endrst
*/ */
FMT_MODULE_EXPORT
template <typename S, typename... Args, template <typename S, typename... Args,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) { void print(std::basic_ostream<Char>& os, const S& format_str, Args&&... args) {

@ -1,2 +0,0 @@
#include "os.h"
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"

@ -10,11 +10,54 @@
#include <algorithm> // std::max #include <algorithm> // std::max
#include <limits> // std::numeric_limits #include <limits> // std::numeric_limits
#include <ostream>
#include "ostream.h" #include "format.h"
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { FMT_MODULE_EXPORT_BEGIN
template <typename T> struct printf_formatter { printf_formatter() = delete; };
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
};
template <typename OutputIt, typename Char> class basic_printf_context {
private:
OutputIt out_;
basic_format_args<basic_printf_context> args_;
public:
using char_type = Char;
using format_arg = basic_format_arg<basic_printf_context>;
using parse_context_type = basic_printf_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
/**
\rst
Constructs a ``printf_context`` object. References to the arguments are
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args) {}
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
detail::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); }
FMT_CONSTEXPR void on_error(const char* message) {
detail::error_handler().on_error(message);
}
};
FMT_BEGIN_DETAIL_NAMESPACE
// Checks if a value fits in int - used to avoid warnings about comparing // Checks if a value fits in int - used to avoid warnings about comparing
// signed and unsigned integers. // signed and unsigned integers.
@ -178,81 +221,38 @@ template <typename Char> class printf_width_handler {
} }
}; };
template <typename Char, typename Context> // The ``printf`` argument formatter.
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
basic_format_args<Context> args) {
Context(buffer_appender<Char>(buf), format, args).format();
}
} // namespace detail
// For printing into memory_buffer.
template <typename Char, typename Context>
FMT_DEPRECATED void printf(detail::buffer<Char>& buf,
basic_string_view<Char> format,
basic_format_args<Context> args) {
return detail::vprintf(buf, format, args);
}
using detail::vprintf;
template <typename Char>
class basic_printf_parse_context : public basic_format_parse_context<Char> {
using basic_format_parse_context<Char>::basic_format_parse_context;
};
template <typename OutputIt, typename Char> class basic_printf_context;
/**
\rst
The ``printf`` argument formatter.
\endrst
*/
template <typename OutputIt, typename Char> template <typename OutputIt, typename Char>
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> { class printf_arg_formatter : public arg_formatter<Char> {
public:
using iterator = OutputIt;
private: private:
using char_type = Char; using base = arg_formatter<Char>;
using base = detail::arg_formatter_base<OutputIt, Char>;
using context_type = basic_printf_context<OutputIt, Char>; using context_type = basic_printf_context<OutputIt, Char>;
using format_specs = basic_format_specs<Char>;
context_type& context_; context_type& context_;
void write_null_pointer(char) { OutputIt write_null_pointer(bool is_string = false) {
this->specs()->type = 0; auto s = this->specs;
this->write("(nil)"); s.type = presentation_type::none;
} return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
void write_null_pointer(wchar_t) {
this->specs()->type = 0;
this->write(L"(nil)");
} }
public: public:
using format_specs = typename base::format_specs; printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx)
: base{iter, s, locale_ref()}, context_(ctx) {}
/**
\rst OutputIt operator()(monostate value) { return base::operator()(value); }
Constructs an argument formatter object.
*buffer* is a reference to the output buffer and *specs* contains format template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
specifier information for standard argument types. OutputIt operator()(T value) {
\endrst // MSVC2013 fails to compile separate overloads for bool and Char so use
*/ // std::is_same instead.
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx) if (std::is_same<T, Char>::value) {
: base(iter, &specs, detail::locale_ref()), context_(ctx) {} format_specs fmt_specs = this->specs;
if (fmt_specs.type != presentation_type::none &&
template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)> fmt_specs.type != presentation_type::chr) {
iterator operator()(T value) {
// MSVC2013 fails to compile separate overloads for bool and char_type so
// use std::is_same instead.
if (std::is_same<T, bool>::value) {
format_specs& fmt_specs = *this->specs();
if (fmt_specs.type != 's') return base::operator()(value ? 1 : 0);
fmt_specs.type = 0;
this->write(value != 0);
} else if (std::is_same<T, char_type>::value) {
format_specs& fmt_specs = *this->specs();
if (fmt_specs.type && fmt_specs.type != 'c')
return (*this)(static_cast<int>(value)); return (*this)(static_cast<int>(value));
}
fmt_specs.sign = sign::none; fmt_specs.sign = sign::none;
fmt_specs.alt = false; fmt_specs.alt = false;
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types. fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
@ -260,138 +260,49 @@ class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
// ignored for non-numeric types // ignored for non-numeric types
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric) if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
fmt_specs.align = align::right; fmt_specs.align = align::right;
return base::operator()(value); return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
} else {
return base::operator()(value);
} }
return this->out(); return base::operator()(value);
} }
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)> template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
iterator operator()(T value) { OutputIt operator()(T value) {
return base::operator()(value); return base::operator()(value);
} }
/** Formats a null-terminated C string. */ /** Formats a null-terminated C string. */
iterator operator()(const char* value) { OutputIt operator()(const char* value) {
if (value) if (value) return base::operator()(value);
base::operator()(value); return write_null_pointer(this->specs.type != presentation_type::pointer);
else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write("(null)");
return this->out();
} }
/** Formats a null-terminated wide C string. */ /** Formats a null-terminated wide C string. */
iterator operator()(const wchar_t* value) { OutputIt operator()(const wchar_t* value) {
if (value) if (value) return base::operator()(value);
base::operator()(value); return write_null_pointer(this->specs.type != presentation_type::pointer);
else if (this->specs()->type == 'p')
write_null_pointer(char_type());
else
this->write(L"(null)");
return this->out();
} }
iterator operator()(basic_string_view<char_type> value) { OutputIt operator()(basic_string_view<Char> value) {
return base::operator()(value); return base::operator()(value);
} }
iterator operator()(monostate value) { return base::operator()(value); }
/** Formats a pointer. */ /** Formats a pointer. */
iterator operator()(const void* value) { OutputIt operator()(const void* value) {
if (value) return base::operator()(value); return value ? base::operator()(value) : write_null_pointer();
this->specs()->type = 0;
write_null_pointer(char_type());
return this->out();
} }
/** Formats an argument of a custom (user-defined) type. */ /** Formats an argument of a custom (user-defined) type. */
iterator operator()(typename basic_format_arg<context_type>::handle handle) { OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
handle.format(context_.parse_context(), context_); auto parse_ctx =
return this->out(); basic_printf_parse_context<Char>(basic_string_view<Char>());
handle.format(parse_ctx, context_);
return this->out;
} }
}; };
template <typename T> struct printf_formatter { template <typename Char>
printf_formatter() = delete; void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
const Char* end) {
template <typename ParseContext>
auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
auto format(const T& value, FormatContext& ctx) -> decltype(ctx.out()) {
detail::format_value(detail::get_container(ctx.out()), value);
return ctx.out();
}
};
/**
This template formats data and writes the output through an output iterator.
*/
template <typename OutputIt, typename Char> class basic_printf_context {
public:
/** The character type for the output. */
using char_type = Char;
using iterator = OutputIt;
using format_arg = basic_format_arg<basic_printf_context>;
using parse_context_type = basic_printf_parse_context<Char>;
template <typename T> using formatter_type = printf_formatter<T>;
private:
using format_specs = basic_format_specs<char_type>;
OutputIt out_;
basic_format_args<basic_printf_context> args_;
parse_context_type parse_ctx_;
static void parse_flags(format_specs& specs, const Char*& it,
const Char* end);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
format_arg get_arg(int arg_index = -1);
// Parses argument index, flags and width and returns the argument index.
int parse_header(const Char*& it, const Char* end, format_specs& specs);
public:
/**
\rst
Constructs a ``printf_context`` object. References to the arguments are
stored in the context object so make sure they have appropriate lifetimes.
\endrst
*/
basic_printf_context(OutputIt out, basic_string_view<char_type> format_str,
basic_format_args<basic_printf_context> args)
: out_(out), args_(args), parse_ctx_(format_str) {}
OutputIt out() { return out_; }
void advance_to(OutputIt it) { out_ = it; }
detail::locale_ref locale() { return {}; }
format_arg arg(int id) const { return args_.get(id); }
parse_context_type& parse_context() { return parse_ctx_; }
FMT_CONSTEXPR void on_error(const char* message) {
parse_ctx_.on_error(message);
}
/** Formats stored arguments and writes the output to the range. */
template <typename ArgFormatter = printf_arg_formatter<OutputIt, Char>>
OutputIt format();
};
template <typename OutputIt, typename Char>
void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
const Char*& it,
const Char* end) {
for (; it != end; ++it) { for (; it != end; ++it) {
switch (*it) { switch (*it) {
case '-': case '-':
@ -417,35 +328,24 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
} }
} }
template <typename OutputIt, typename Char> template <typename Char, typename GetArg>
typename basic_printf_context<OutputIt, Char>::format_arg int parse_header(const Char*& it, const Char* end,
basic_printf_context<OutputIt, Char>::get_arg(int arg_index) { basic_format_specs<Char>& specs, GetArg get_arg) {
if (arg_index < 0)
arg_index = parse_ctx_.next_arg_id();
else
parse_ctx_.check_arg_id(--arg_index);
return detail::get_arg(*this, arg_index);
}
template <typename OutputIt, typename Char>
int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
const Char* end,
format_specs& specs) {
int arg_index = -1; int arg_index = -1;
char_type c = *it; Char c = *it;
if (c >= '0' && c <= '9') { if (c >= '0' && c <= '9') {
// Parse an argument index (if followed by '$') or a width possibly // Parse an argument index (if followed by '$') or a width possibly
// preceded with '0' flag(s). // preceded with '0' flag(s).
detail::error_handler eh; int value = parse_nonnegative_int(it, end, -1);
int value = parse_nonnegative_int(it, end, eh);
if (it != end && *it == '$') { // value is an argument index if (it != end && *it == '$') { // value is an argument index
++it; ++it;
arg_index = value; arg_index = value != -1 ? value : max_value<int>();
} else { } else {
if (c == '0') specs.fill[0] = '0'; if (c == '0') specs.fill[0] = '0';
if (value != 0) { if (value != 0) {
// Nonzero value means that we parsed width and don't need to // Nonzero value means that we parsed width and don't need to
// parse it or flags again, so return now. // parse it or flags again, so return now.
if (value == -1) FMT_THROW(format_error("number is too big"));
specs.width = value; specs.width = value;
return arg_index; return arg_index;
} }
@ -455,58 +355,76 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
// Parse width. // Parse width.
if (it != end) { if (it != end) {
if (*it >= '0' && *it <= '9') { if (*it >= '0' && *it <= '9') {
detail::error_handler eh; specs.width = parse_nonnegative_int(it, end, -1);
specs.width = parse_nonnegative_int(it, end, eh); if (specs.width == -1) FMT_THROW(format_error("number is too big"));
} else if (*it == '*') { } else if (*it == '*') {
++it; ++it;
specs.width = static_cast<int>(visit_format_arg( specs.width = static_cast<int>(visit_format_arg(
detail::printf_width_handler<char_type>(specs), get_arg())); detail::printf_width_handler<Char>(specs), get_arg(-1)));
} }
} }
return arg_index; return arg_index;
} }
template <typename OutputIt, typename Char> template <typename Char, typename Context>
template <typename ArgFormatter> void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
OutputIt basic_printf_context<OutputIt, Char>::format() { basic_format_args<Context> args) {
auto out = this->out(); using OutputIt = buffer_appender<Char>;
const Char* start = parse_ctx_.begin(); auto out = OutputIt(buf);
const Char* end = parse_ctx_.end(); auto context = basic_printf_context<OutputIt, Char>(out, args);
auto parse_ctx = basic_printf_parse_context<Char>(format);
// Returns the argument with specified index or, if arg_index is -1, the next
// argument.
auto get_arg = [&](int arg_index) {
if (arg_index < 0)
arg_index = parse_ctx.next_arg_id();
else
parse_ctx.check_arg_id(--arg_index);
return detail::get_arg(context, arg_index);
};
const Char* start = parse_ctx.begin();
const Char* end = parse_ctx.end();
auto it = start; auto it = start;
while (it != end) { while (it != end) {
char_type c = *it++; if (!detail::find<false, Char>(it, end, '%', it)) {
if (c != '%') continue; it = end; // detail::find leaves it == nullptr if it doesn't find '%'
break;
}
Char c = *it++;
if (it != end && *it == c) { if (it != end && *it == c) {
out = std::copy(start, it, out); out = detail::write(
out, basic_string_view<Char>(start, detail::to_unsigned(it - start)));
start = ++it; start = ++it;
continue; continue;
} }
out = std::copy(start, it - 1, out); out = detail::write(out, basic_string_view<Char>(
start, detail::to_unsigned(it - 1 - start)));
format_specs specs; basic_format_specs<Char> specs;
specs.align = align::right; specs.align = align::right;
// Parse argument index, flags and width. // Parse argument index, flags and width.
int arg_index = parse_header(it, end, specs); int arg_index = parse_header(it, end, specs, get_arg);
if (arg_index == 0) on_error("argument not found"); if (arg_index == 0) parse_ctx.on_error("argument not found");
// Parse precision. // Parse precision.
if (it != end && *it == '.') { if (it != end && *it == '.') {
++it; ++it;
c = it != end ? *it : 0; c = it != end ? *it : 0;
if ('0' <= c && c <= '9') { if ('0' <= c && c <= '9') {
detail::error_handler eh; specs.precision = parse_nonnegative_int(it, end, 0);
specs.precision = parse_nonnegative_int(it, end, eh);
} else if (c == '*') { } else if (c == '*') {
++it; ++it;
specs.precision = static_cast<int>( specs.precision = static_cast<int>(
visit_format_arg(detail::printf_precision_handler(), get_arg())); visit_format_arg(detail::printf_precision_handler(), get_arg(-1)));
} else { } else {
specs.precision = 0; specs.precision = 0;
} }
} }
format_arg arg = get_arg(arg_index); auto arg = get_arg(arg_index);
// For d, i, o, u, x, and X conversion specifiers, if a precision is // For d, i, o, u, x, and X conversion specifiers, if a precision is
// specified, the '0' flag is ignored // specified, the '0' flag is ignored
if (specs.precision >= 0 && arg.is_integral()) if (specs.precision >= 0 && arg.is_integral())
@ -516,9 +434,10 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
auto str = visit_format_arg(detail::get_cstring<Char>(), arg); auto str = visit_format_arg(detail::get_cstring<Char>(), arg);
auto str_end = str + specs.precision; auto str_end = str + specs.precision;
auto nul = std::find(str, str_end, Char()); auto nul = std::find(str, str_end, Char());
arg = detail::make_arg<basic_printf_context>(basic_string_view<Char>( arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
str, basic_string_view<Char>(
detail::to_unsigned(nul != str_end ? nul - str : specs.precision))); str, detail::to_unsigned(nul != str_end ? nul - str
: specs.precision)));
} }
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg)) if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
specs.alt = false; specs.alt = false;
@ -532,7 +451,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
// Parse length and convert the argument to the required type. // Parse length and convert the argument to the required type.
c = it != end ? *it++ : 0; c = it != end ? *it++ : 0;
char_type t = it != end ? *it : 0; Char t = it != end ? *it : 0;
using detail::convert_arg; using detail::convert_arg;
switch (c) { switch (c) {
case 'h': case 'h':
@ -573,28 +492,34 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
// Parse type. // Parse type.
if (it == end) FMT_THROW(format_error("invalid format string")); if (it == end) FMT_THROW(format_error("invalid format string"));
specs.type = static_cast<char>(*it++); char type = static_cast<char>(*it++);
if (arg.is_integral()) { if (arg.is_integral()) {
// Normalize type. // Normalize type.
switch (specs.type) { switch (type) {
case 'i': case 'i':
case 'u': case 'u':
specs.type = 'd'; type = 'd';
break; break;
case 'c': case 'c':
visit_format_arg(detail::char_converter<basic_printf_context>(arg), visit_format_arg(
arg); detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
arg);
break; break;
} }
} }
specs.type = parse_presentation_type(type);
if (specs.type == presentation_type::none)
parse_ctx.on_error("invalid type specifier");
start = it; start = it;
// Format argument. // Format argument.
out = visit_format_arg(ArgFormatter(out, specs, *this), arg); out = visit_format_arg(
detail::printf_arg_formatter<OutputIt, Char>(out, specs, context), arg);
} }
return std::copy(start, it, out); detail::write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
} }
FMT_END_DETAIL_NAMESPACE
template <typename Char> template <typename Char>
using basic_printf_context_t = using basic_printf_context_t =
@ -612,9 +537,9 @@ using wprintf_args = basic_format_args<wprintf_context>;
arguments and can be implicitly converted to `~fmt::printf_args`. arguments and can be implicitly converted to `~fmt::printf_args`.
\endrst \endrst
*/ */
template <typename... Args> template <typename... T>
inline format_arg_store<printf_context, Args...> make_printf_args( inline auto make_printf_args(const T&... args)
const Args&... args) { -> format_arg_store<printf_context, T...> {
return {args...}; return {args...};
} }
@ -624,18 +549,19 @@ inline format_arg_store<printf_context, Args...> make_printf_args(
arguments and can be implicitly converted to `~fmt::wprintf_args`. arguments and can be implicitly converted to `~fmt::wprintf_args`.
\endrst \endrst
*/ */
template <typename... Args> template <typename... T>
inline format_arg_store<wprintf_context, Args...> make_wprintf_args( inline auto make_wprintf_args(const T&... args)
const Args&... args) { -> format_arg_store<wprintf_context, T...> {
return {args...}; return {args...};
} }
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vsprintf( inline auto vsprintf(
const S& format, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> std::basic_string<Char> {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(format), args); vprintf(buffer, to_string_view(fmt), args);
return to_string(buffer); return to_string(buffer);
} }
@ -648,19 +574,20 @@ inline std::basic_string<Char> vsprintf(
std::string message = fmt::sprintf("The answer is %d", 42); std::string message = fmt::sprintf("The answer is %d", 42);
\endrst \endrst
*/ */
template <typename S, typename... Args, template <typename S, typename... T,
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
inline std::basic_string<Char> sprintf(const S& format, const Args&... args) { inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
using context = basic_printf_context_t<Char>; using context = basic_printf_context_t<Char>;
return vsprintf(to_string_view(format), make_format_args<context>(args...)); return vsprintf(to_string_view(fmt), fmt::make_format_args<context>(args...));
} }
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
inline int vfprintf( inline auto vfprintf(
std::FILE* f, const S& format, std::FILE* f, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(format), args); vprintf(buffer, to_string_view(fmt), args);
size_t size = buffer.size(); size_t size = buffer.size();
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
? -1 ? -1
@ -676,19 +603,19 @@ inline int vfprintf(
fmt::fprintf(stderr, "Don't %s!", "panic"); fmt::fprintf(stderr, "Don't %s!", "panic");
\endrst \endrst
*/ */
template <typename S, typename... Args, template <typename S, typename... T, typename Char = char_t<S>>
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>> inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
using context = basic_printf_context_t<Char>; using context = basic_printf_context_t<Char>;
return vfprintf(f, to_string_view(format), return vfprintf(f, to_string_view(fmt),
make_format_args<context>(args...)); fmt::make_format_args<context>(args...));
} }
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
inline int vprintf( inline auto vprintf(
const S& format, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
return vfprintf(stdout, to_string_view(format), args); -> int {
return vfprintf(stdout, to_string_view(fmt), args);
} }
/** /**
@ -700,52 +627,31 @@ inline int vprintf(
fmt::printf("Elapsed time: %.2f seconds", 1.23); fmt::printf("Elapsed time: %.2f seconds", 1.23);
\endrst \endrst
*/ */
template <typename S, typename... Args, template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
FMT_ENABLE_IF(detail::is_string<S>::value)> inline auto printf(const S& fmt, const T&... args) -> int {
inline int printf(const S& format_str, const Args&... args) { return vprintf(
using context = basic_printf_context_t<char_t<S>>; to_string_view(fmt),
return vprintf(to_string_view(format_str), fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
make_format_args<context>(args...));
} }
template <typename S, typename Char = char_t<S>> template <typename S, typename Char = char_t<S>>
inline int vfprintf( FMT_DEPRECATED auto vfprintf(
std::basic_ostream<Char>& os, const S& format, std::basic_ostream<Char>& os, const S& fmt,
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) { basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
-> int {
basic_memory_buffer<Char> buffer; basic_memory_buffer<Char> buffer;
vprintf(buffer, to_string_view(format), args); vprintf(buffer, to_string_view(fmt), args);
detail::write_buffer(os, buffer); os.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
return static_cast<int>(buffer.size()); return static_cast<int>(buffer.size());
} }
template <typename S, typename... T, typename Char = char_t<S>>
/** Formats arguments and writes the output to the range. */ FMT_DEPRECATED auto fprintf(std::basic_ostream<Char>& os, const S& fmt,
template <typename ArgFormatter, typename Char, const T&... args) -> int {
typename Context = return vfprintf(os, to_string_view(fmt),
basic_printf_context<typename ArgFormatter::iterator, Char>> fmt::make_format_args<basic_printf_context_t<Char>>(args...));
typename ArgFormatter::iterator vprintf(
detail::buffer<Char>& out, basic_string_view<Char> format_str,
basic_format_args<type_identity_t<Context>> args) {
typename ArgFormatter::iterator iter(out);
Context(iter, format_str, args).template format<ArgFormatter>();
return iter;
} }
/** FMT_MODULE_EXPORT_END
\rst
Prints formatted data to the stream *os*.
**Example**::
fmt::fprintf(cerr, "Don't %s!", "panic");
\endrst
*/
template <typename S, typename... Args, typename Char = char_t<S>>
inline int fprintf(std::basic_ostream<Char>& os, const S& format_str,
const Args&... args) {
using context = basic_printf_context_t<Char>;
return vfprintf(os, to_string_view(format_str),
make_format_args<context>(args...));
}
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_PRINTF_H_ #endif // FMT_PRINTF_H_

@ -13,47 +13,13 @@
#define FMT_RANGES_H_ #define FMT_RANGES_H_
#include <initializer_list> #include <initializer_list>
#include <tuple>
#include <type_traits> #include <type_traits>
#include "format.h" #include "format.h"
// output only up to N items from the range.
#ifndef FMT_RANGE_OUTPUT_LENGTH_LIMIT
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
#endif
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
template <typename Char> struct formatting_base {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
};
template <typename Char, typename Enable = void>
struct formatting_range : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the
// range.
Char prefix;
Char delimiter;
Char postfix;
formatting_range() : prefix('{'), delimiter(','), postfix('}') {}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
template <typename Char, typename Enable = void>
struct formatting_tuple : formatting_base<Char> {
Char prefix;
Char delimiter;
Char postfix;
formatting_tuple() : prefix('('), delimiter(','), postfix(')') {}
static FMT_CONSTEXPR_DECL const bool add_delimiter_spaces = true;
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
namespace detail { namespace detail {
template <typename RangeT, typename OutputIterator> template <typename RangeT, typename OutputIterator>
@ -75,8 +41,14 @@ OutputIterator copy(char ch, OutputIterator out) {
return out; return out;
} }
/// Return true value if T has std::string interface, like std::string_view. template <typename OutputIterator>
template <typename T> class is_like_std_string { OutputIterator copy(wchar_t ch, OutputIterator out) {
*out++ = ch;
return out;
}
// Returns true if T has a std::string-like interface, like std::string_view.
template <typename T> class is_std_string_like {
template <typename U> template <typename U>
static auto check(U* p) static auto check(U* p)
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int()); -> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
@ -84,26 +56,118 @@ template <typename T> class is_like_std_string {
public: public:
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
is_string<T>::value || !std::is_void<decltype(check<T>(nullptr))>::value; is_string<T>::value ||
std::is_convertible<T, std_string_view<char>>::value ||
!std::is_void<decltype(check<T>(nullptr))>::value;
}; };
template <typename Char> template <typename Char>
struct is_like_std_string<fmt::basic_string_view<Char>> : std::true_type {}; struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
template <typename T> class is_map {
template <typename U> static auto check(U*) -> typename U::mapped_type;
template <typename> static void check(...);
public:
#ifdef FMT_FORMAT_MAP_AS_LIST
static FMT_CONSTEXPR_DECL const bool value = false;
#else
static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value;
#endif
};
template <typename T> class is_set {
template <typename U> static auto check(U*) -> typename U::key_type;
template <typename> static void check(...);
public:
#ifdef FMT_FORMAT_SET_AS_LIST
static FMT_CONSTEXPR_DECL const bool value = false;
#else
static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
#endif
};
template <typename... Ts> struct conditional_helper {}; template <typename... Ts> struct conditional_helper {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {}; template <typename T, typename _ = void> struct is_range_ : std::false_type {};
#if !FMT_MSC_VER || FMT_MSC_VER > 1800 #if !FMT_MSC_VER || FMT_MSC_VER > 1800
# define FMT_DECLTYPE_RETURN(val) \
->decltype(val) { return val; } \
static_assert( \
true, "") // This makes it so that a semicolon is required after the
// macro, which helps clang-format handle the formatting.
// C array overload
template <typename T, std::size_t N>
auto range_begin(const T (&arr)[N]) -> const T* {
return arr;
}
template <typename T, std::size_t N>
auto range_end(const T (&arr)[N]) -> const T* {
return arr + N;
}
template <typename T, typename Enable = void>
struct has_member_fn_begin_end_t : std::false_type {};
template <typename T>
struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>>
: std::true_type {};
// Member function overload
template <typename T>
auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
template <typename T>
auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
// ADL overload. Only participates in overload resolution if member functions
// are not found.
template <typename T>
auto range_begin(T&& rng)
-> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
decltype(begin(static_cast<T&&>(rng)))> {
return begin(static_cast<T&&>(rng));
}
template <typename T> template <typename T>
struct is_range_< auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
T, conditional_t<false, decltype(end(static_cast<T&&>(rng)))> {
conditional_helper<decltype(std::declval<T>().begin()), return end(static_cast<T&&>(rng));
decltype(std::declval<T>().end())>, }
void>> : std::true_type {};
template <typename T, typename Enable = void>
struct has_const_begin_end : std::false_type {};
template <typename T, typename Enable = void>
struct has_mutable_begin_end : std::false_type {};
template <typename T>
struct has_const_begin_end<
T,
void_t<
decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
: std::true_type {};
template <typename T>
struct has_mutable_begin_end<
T, void_t<decltype(detail::range_begin(std::declval<T>())),
decltype(detail::range_end(std::declval<T>())),
enable_if_t<std::is_copy_constructible<T>::value>>>
: std::true_type {};
template <typename T>
struct is_range_<T, void>
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
has_mutable_begin_end<T>::value)> {};
# undef FMT_DECLTYPE_RETURN
#endif #endif
/// tuple_size and tuple_element check. // tuple_size and tuple_element check.
template <typename T> class is_tuple_like_ { template <typename T> class is_tuple_like_ {
template <typename U> template <typename U>
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int()); static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
@ -158,33 +222,321 @@ template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
} }
template <typename Range> template <typename Range>
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>; using value_type =
remove_cvref_t<decltype(*detail::range_begin(std::declval<Range>()))>;
template <typename Arg, FMT_ENABLE_IF(!is_like_std_string< template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
typename std::decay<Arg>::type>::value)> *out++ = ',';
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { *out++ = ' ';
return add_space ? " {}" : "{}"; return out;
} }
template <typename Arg, FMT_ENABLE_IF(is_like_std_string< struct singleton {
typename std::decay<Arg>::type>::value)> unsigned char upper;
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) { unsigned char lower_count;
return add_space ? " \"{}\"" : "\"{}\""; };
inline auto is_printable(uint16_t x, const singleton* singletons,
size_t singletons_size,
const unsigned char* singleton_lowers,
const unsigned char* normal, size_t normal_size)
-> bool {
auto upper = x >> 8;
auto lower_start = 0;
for (size_t i = 0; i < singletons_size; ++i) {
auto s = singletons[i];
auto lower_end = lower_start + s.lower_count;
if (upper < s.upper) break;
if (upper == s.upper) {
for (auto j = lower_start; j < lower_end; ++j) {
if (singleton_lowers[j] == (x & 0xff)) return false;
}
}
lower_start = lower_end;
}
auto xsigned = static_cast<int>(x);
auto current = true;
for (size_t i = 0; i < normal_size; ++i) {
auto v = static_cast<int>(normal[i]);
auto len = (v & 0x80) != 0 ? (v & 0x7f) << 8 | normal[++i] : v;
xsigned -= len;
if (xsigned < 0) break;
current = !current;
}
return current;
} }
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) { // Returns true iff the code point cp is printable.
return add_space ? " \"{}\"" : "\"{}\""; // This code is generated by support/printable.py.
inline auto is_printable(uint32_t cp) -> bool {
static constexpr singleton singletons0[] = {
{0x00, 1}, {0x03, 5}, {0x05, 6}, {0x06, 3}, {0x07, 6}, {0x08, 8},
{0x09, 17}, {0x0a, 28}, {0x0b, 25}, {0x0c, 20}, {0x0d, 16}, {0x0e, 13},
{0x0f, 4}, {0x10, 3}, {0x12, 18}, {0x13, 9}, {0x16, 1}, {0x17, 5},
{0x18, 2}, {0x19, 3}, {0x1a, 7}, {0x1c, 2}, {0x1d, 1}, {0x1f, 22},
{0x20, 3}, {0x2b, 3}, {0x2c, 2}, {0x2d, 11}, {0x2e, 1}, {0x30, 3},
{0x31, 2}, {0x32, 1}, {0xa7, 2}, {0xa9, 2}, {0xaa, 4}, {0xab, 8},
{0xfa, 2}, {0xfb, 5}, {0xfd, 4}, {0xfe, 3}, {0xff, 9},
};
static constexpr unsigned char singletons0_lower[] = {
0xad, 0x78, 0x79, 0x8b, 0x8d, 0xa2, 0x30, 0x57, 0x58, 0x8b, 0x8c, 0x90,
0x1c, 0x1d, 0xdd, 0x0e, 0x0f, 0x4b, 0x4c, 0xfb, 0xfc, 0x2e, 0x2f, 0x3f,
0x5c, 0x5d, 0x5f, 0xb5, 0xe2, 0x84, 0x8d, 0x8e, 0x91, 0x92, 0xa9, 0xb1,
0xba, 0xbb, 0xc5, 0xc6, 0xc9, 0xca, 0xde, 0xe4, 0xe5, 0xff, 0x00, 0x04,
0x11, 0x12, 0x29, 0x31, 0x34, 0x37, 0x3a, 0x3b, 0x3d, 0x49, 0x4a, 0x5d,
0x84, 0x8e, 0x92, 0xa9, 0xb1, 0xb4, 0xba, 0xbb, 0xc6, 0xca, 0xce, 0xcf,
0xe4, 0xe5, 0x00, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
0x3b, 0x45, 0x46, 0x49, 0x4a, 0x5e, 0x64, 0x65, 0x84, 0x91, 0x9b, 0x9d,
0xc9, 0xce, 0xcf, 0x0d, 0x11, 0x29, 0x45, 0x49, 0x57, 0x64, 0x65, 0x8d,
0x91, 0xa9, 0xb4, 0xba, 0xbb, 0xc5, 0xc9, 0xdf, 0xe4, 0xe5, 0xf0, 0x0d,
0x11, 0x45, 0x49, 0x64, 0x65, 0x80, 0x84, 0xb2, 0xbc, 0xbe, 0xbf, 0xd5,
0xd7, 0xf0, 0xf1, 0x83, 0x85, 0x8b, 0xa4, 0xa6, 0xbe, 0xbf, 0xc5, 0xc7,
0xce, 0xcf, 0xda, 0xdb, 0x48, 0x98, 0xbd, 0xcd, 0xc6, 0xce, 0xcf, 0x49,
0x4e, 0x4f, 0x57, 0x59, 0x5e, 0x5f, 0x89, 0x8e, 0x8f, 0xb1, 0xb6, 0xb7,
0xbf, 0xc1, 0xc6, 0xc7, 0xd7, 0x11, 0x16, 0x17, 0x5b, 0x5c, 0xf6, 0xf7,
0xfe, 0xff, 0x80, 0x0d, 0x6d, 0x71, 0xde, 0xdf, 0x0e, 0x0f, 0x1f, 0x6e,
0x6f, 0x1c, 0x1d, 0x5f, 0x7d, 0x7e, 0xae, 0xaf, 0xbb, 0xbc, 0xfa, 0x16,
0x17, 0x1e, 0x1f, 0x46, 0x47, 0x4e, 0x4f, 0x58, 0x5a, 0x5c, 0x5e, 0x7e,
0x7f, 0xb5, 0xc5, 0xd4, 0xd5, 0xdc, 0xf0, 0xf1, 0xf5, 0x72, 0x73, 0x8f,
0x74, 0x75, 0x96, 0x2f, 0x5f, 0x26, 0x2e, 0x2f, 0xa7, 0xaf, 0xb7, 0xbf,
0xc7, 0xcf, 0xd7, 0xdf, 0x9a, 0x40, 0x97, 0x98, 0x30, 0x8f, 0x1f, 0xc0,
0xc1, 0xce, 0xff, 0x4e, 0x4f, 0x5a, 0x5b, 0x07, 0x08, 0x0f, 0x10, 0x27,
0x2f, 0xee, 0xef, 0x6e, 0x6f, 0x37, 0x3d, 0x3f, 0x42, 0x45, 0x90, 0x91,
0xfe, 0xff, 0x53, 0x67, 0x75, 0xc8, 0xc9, 0xd0, 0xd1, 0xd8, 0xd9, 0xe7,
0xfe, 0xff,
};
static constexpr singleton singletons1[] = {
{0x00, 6}, {0x01, 1}, {0x03, 1}, {0x04, 2}, {0x08, 8}, {0x09, 2},
{0x0a, 5}, {0x0b, 2}, {0x0e, 4}, {0x10, 1}, {0x11, 2}, {0x12, 5},
{0x13, 17}, {0x14, 1}, {0x15, 2}, {0x17, 2}, {0x19, 13}, {0x1c, 5},
{0x1d, 8}, {0x24, 1}, {0x6a, 3}, {0x6b, 2}, {0xbc, 2}, {0xd1, 2},
{0xd4, 12}, {0xd5, 9}, {0xd6, 2}, {0xd7, 2}, {0xda, 1}, {0xe0, 5},
{0xe1, 2}, {0xe8, 2}, {0xee, 32}, {0xf0, 4}, {0xf8, 2}, {0xf9, 2},
{0xfa, 2}, {0xfb, 1},
};
static constexpr unsigned char singletons1_lower[] = {
0x0c, 0x27, 0x3b, 0x3e, 0x4e, 0x4f, 0x8f, 0x9e, 0x9e, 0x9f, 0x06, 0x07,
0x09, 0x36, 0x3d, 0x3e, 0x56, 0xf3, 0xd0, 0xd1, 0x04, 0x14, 0x18, 0x36,
0x37, 0x56, 0x57, 0x7f, 0xaa, 0xae, 0xaf, 0xbd, 0x35, 0xe0, 0x12, 0x87,
0x89, 0x8e, 0x9e, 0x04, 0x0d, 0x0e, 0x11, 0x12, 0x29, 0x31, 0x34, 0x3a,
0x45, 0x46, 0x49, 0x4a, 0x4e, 0x4f, 0x64, 0x65, 0x5c, 0xb6, 0xb7, 0x1b,
0x1c, 0x07, 0x08, 0x0a, 0x0b, 0x14, 0x17, 0x36, 0x39, 0x3a, 0xa8, 0xa9,
0xd8, 0xd9, 0x09, 0x37, 0x90, 0x91, 0xa8, 0x07, 0x0a, 0x3b, 0x3e, 0x66,
0x69, 0x8f, 0x92, 0x6f, 0x5f, 0xee, 0xef, 0x5a, 0x62, 0x9a, 0x9b, 0x27,
0x28, 0x55, 0x9d, 0xa0, 0xa1, 0xa3, 0xa4, 0xa7, 0xa8, 0xad, 0xba, 0xbc,
0xc4, 0x06, 0x0b, 0x0c, 0x15, 0x1d, 0x3a, 0x3f, 0x45, 0x51, 0xa6, 0xa7,
0xcc, 0xcd, 0xa0, 0x07, 0x19, 0x1a, 0x22, 0x25, 0x3e, 0x3f, 0xc5, 0xc6,
0x04, 0x20, 0x23, 0x25, 0x26, 0x28, 0x33, 0x38, 0x3a, 0x48, 0x4a, 0x4c,
0x50, 0x53, 0x55, 0x56, 0x58, 0x5a, 0x5c, 0x5e, 0x60, 0x63, 0x65, 0x66,
0x6b, 0x73, 0x78, 0x7d, 0x7f, 0x8a, 0xa4, 0xaa, 0xaf, 0xb0, 0xc0, 0xd0,
0xae, 0xaf, 0x79, 0xcc, 0x6e, 0x6f, 0x93,
};
static constexpr unsigned char normal0[] = {
0x00, 0x20, 0x5f, 0x22, 0x82, 0xdf, 0x04, 0x82, 0x44, 0x08, 0x1b, 0x04,
0x06, 0x11, 0x81, 0xac, 0x0e, 0x80, 0xab, 0x35, 0x28, 0x0b, 0x80, 0xe0,
0x03, 0x19, 0x08, 0x01, 0x04, 0x2f, 0x04, 0x34, 0x04, 0x07, 0x03, 0x01,
0x07, 0x06, 0x07, 0x11, 0x0a, 0x50, 0x0f, 0x12, 0x07, 0x55, 0x07, 0x03,
0x04, 0x1c, 0x0a, 0x09, 0x03, 0x08, 0x03, 0x07, 0x03, 0x02, 0x03, 0x03,
0x03, 0x0c, 0x04, 0x05, 0x03, 0x0b, 0x06, 0x01, 0x0e, 0x15, 0x05, 0x3a,
0x03, 0x11, 0x07, 0x06, 0x05, 0x10, 0x07, 0x57, 0x07, 0x02, 0x07, 0x15,
0x0d, 0x50, 0x04, 0x43, 0x03, 0x2d, 0x03, 0x01, 0x04, 0x11, 0x06, 0x0f,
0x0c, 0x3a, 0x04, 0x1d, 0x25, 0x5f, 0x20, 0x6d, 0x04, 0x6a, 0x25, 0x80,
0xc8, 0x05, 0x82, 0xb0, 0x03, 0x1a, 0x06, 0x82, 0xfd, 0x03, 0x59, 0x07,
0x15, 0x0b, 0x17, 0x09, 0x14, 0x0c, 0x14, 0x0c, 0x6a, 0x06, 0x0a, 0x06,
0x1a, 0x06, 0x59, 0x07, 0x2b, 0x05, 0x46, 0x0a, 0x2c, 0x04, 0x0c, 0x04,
0x01, 0x03, 0x31, 0x0b, 0x2c, 0x04, 0x1a, 0x06, 0x0b, 0x03, 0x80, 0xac,
0x06, 0x0a, 0x06, 0x21, 0x3f, 0x4c, 0x04, 0x2d, 0x03, 0x74, 0x08, 0x3c,
0x03, 0x0f, 0x03, 0x3c, 0x07, 0x38, 0x08, 0x2b, 0x05, 0x82, 0xff, 0x11,
0x18, 0x08, 0x2f, 0x11, 0x2d, 0x03, 0x20, 0x10, 0x21, 0x0f, 0x80, 0x8c,
0x04, 0x82, 0x97, 0x19, 0x0b, 0x15, 0x88, 0x94, 0x05, 0x2f, 0x05, 0x3b,
0x07, 0x02, 0x0e, 0x18, 0x09, 0x80, 0xb3, 0x2d, 0x74, 0x0c, 0x80, 0xd6,
0x1a, 0x0c, 0x05, 0x80, 0xff, 0x05, 0x80, 0xdf, 0x0c, 0xee, 0x0d, 0x03,
0x84, 0x8d, 0x03, 0x37, 0x09, 0x81, 0x5c, 0x14, 0x80, 0xb8, 0x08, 0x80,
0xcb, 0x2a, 0x38, 0x03, 0x0a, 0x06, 0x38, 0x08, 0x46, 0x08, 0x0c, 0x06,
0x74, 0x0b, 0x1e, 0x03, 0x5a, 0x04, 0x59, 0x09, 0x80, 0x83, 0x18, 0x1c,
0x0a, 0x16, 0x09, 0x4c, 0x04, 0x80, 0x8a, 0x06, 0xab, 0xa4, 0x0c, 0x17,
0x04, 0x31, 0xa1, 0x04, 0x81, 0xda, 0x26, 0x07, 0x0c, 0x05, 0x05, 0x80,
0xa5, 0x11, 0x81, 0x6d, 0x10, 0x78, 0x28, 0x2a, 0x06, 0x4c, 0x04, 0x80,
0x8d, 0x04, 0x80, 0xbe, 0x03, 0x1b, 0x03, 0x0f, 0x0d,
};
static constexpr unsigned char normal1[] = {
0x5e, 0x22, 0x7b, 0x05, 0x03, 0x04, 0x2d, 0x03, 0x66, 0x03, 0x01, 0x2f,
0x2e, 0x80, 0x82, 0x1d, 0x03, 0x31, 0x0f, 0x1c, 0x04, 0x24, 0x09, 0x1e,
0x05, 0x2b, 0x05, 0x44, 0x04, 0x0e, 0x2a, 0x80, 0xaa, 0x06, 0x24, 0x04,
0x24, 0x04, 0x28, 0x08, 0x34, 0x0b, 0x01, 0x80, 0x90, 0x81, 0x37, 0x09,
0x16, 0x0a, 0x08, 0x80, 0x98, 0x39, 0x03, 0x63, 0x08, 0x09, 0x30, 0x16,
0x05, 0x21, 0x03, 0x1b, 0x05, 0x01, 0x40, 0x38, 0x04, 0x4b, 0x05, 0x2f,
0x04, 0x0a, 0x07, 0x09, 0x07, 0x40, 0x20, 0x27, 0x04, 0x0c, 0x09, 0x36,
0x03, 0x3a, 0x05, 0x1a, 0x07, 0x04, 0x0c, 0x07, 0x50, 0x49, 0x37, 0x33,
0x0d, 0x33, 0x07, 0x2e, 0x08, 0x0a, 0x81, 0x26, 0x52, 0x4e, 0x28, 0x08,
0x2a, 0x56, 0x1c, 0x14, 0x17, 0x09, 0x4e, 0x04, 0x1e, 0x0f, 0x43, 0x0e,
0x19, 0x07, 0x0a, 0x06, 0x48, 0x08, 0x27, 0x09, 0x75, 0x0b, 0x3f, 0x41,
0x2a, 0x06, 0x3b, 0x05, 0x0a, 0x06, 0x51, 0x06, 0x01, 0x05, 0x10, 0x03,
0x05, 0x80, 0x8b, 0x62, 0x1e, 0x48, 0x08, 0x0a, 0x80, 0xa6, 0x5e, 0x22,
0x45, 0x0b, 0x0a, 0x06, 0x0d, 0x13, 0x39, 0x07, 0x0a, 0x36, 0x2c, 0x04,
0x10, 0x80, 0xc0, 0x3c, 0x64, 0x53, 0x0c, 0x48, 0x09, 0x0a, 0x46, 0x45,
0x1b, 0x48, 0x08, 0x53, 0x1d, 0x39, 0x81, 0x07, 0x46, 0x0a, 0x1d, 0x03,
0x47, 0x49, 0x37, 0x03, 0x0e, 0x08, 0x0a, 0x06, 0x39, 0x07, 0x0a, 0x81,
0x36, 0x19, 0x80, 0xb7, 0x01, 0x0f, 0x32, 0x0d, 0x83, 0x9b, 0x66, 0x75,
0x0b, 0x80, 0xc4, 0x8a, 0xbc, 0x84, 0x2f, 0x8f, 0xd1, 0x82, 0x47, 0xa1,
0xb9, 0x82, 0x39, 0x07, 0x2a, 0x04, 0x02, 0x60, 0x26, 0x0a, 0x46, 0x0a,
0x28, 0x05, 0x13, 0x82, 0xb0, 0x5b, 0x65, 0x4b, 0x04, 0x39, 0x07, 0x11,
0x40, 0x05, 0x0b, 0x02, 0x0e, 0x97, 0xf8, 0x08, 0x84, 0xd6, 0x2a, 0x09,
0xa2, 0xf7, 0x81, 0x1f, 0x31, 0x03, 0x11, 0x04, 0x08, 0x81, 0x8c, 0x89,
0x04, 0x6b, 0x05, 0x0d, 0x03, 0x09, 0x07, 0x10, 0x93, 0x60, 0x80, 0xf6,
0x0a, 0x73, 0x08, 0x6e, 0x17, 0x46, 0x80, 0x9a, 0x14, 0x0c, 0x57, 0x09,
0x19, 0x80, 0x87, 0x81, 0x47, 0x03, 0x85, 0x42, 0x0f, 0x15, 0x85, 0x50,
0x2b, 0x80, 0xd5, 0x2d, 0x03, 0x1a, 0x04, 0x02, 0x81, 0x70, 0x3a, 0x05,
0x01, 0x85, 0x00, 0x80, 0xd7, 0x29, 0x4c, 0x04, 0x0a, 0x04, 0x02, 0x83,
0x11, 0x44, 0x4c, 0x3d, 0x80, 0xc2, 0x3c, 0x06, 0x01, 0x04, 0x55, 0x05,
0x1b, 0x34, 0x02, 0x81, 0x0e, 0x2c, 0x04, 0x64, 0x0c, 0x56, 0x0a, 0x80,
0xae, 0x38, 0x1d, 0x0d, 0x2c, 0x04, 0x09, 0x07, 0x02, 0x0e, 0x06, 0x80,
0x9a, 0x83, 0xd8, 0x08, 0x0d, 0x03, 0x0d, 0x03, 0x74, 0x0c, 0x59, 0x07,
0x0c, 0x14, 0x0c, 0x04, 0x38, 0x08, 0x0a, 0x06, 0x28, 0x08, 0x22, 0x4e,
0x81, 0x54, 0x0c, 0x15, 0x03, 0x03, 0x05, 0x07, 0x09, 0x19, 0x07, 0x07,
0x09, 0x03, 0x0d, 0x07, 0x29, 0x80, 0xcb, 0x25, 0x0a, 0x84, 0x06,
};
auto lower = static_cast<uint16_t>(cp);
if (cp < 0x10000) {
return is_printable(lower, singletons0,
sizeof(singletons0) / sizeof(*singletons0),
singletons0_lower, normal0, sizeof(normal0));
}
if (cp < 0x20000) {
return is_printable(lower, singletons1,
sizeof(singletons1) / sizeof(*singletons1),
singletons1_lower, normal1, sizeof(normal1));
}
if (0x2a6de <= cp && cp < 0x2a700) return false;
if (0x2b735 <= cp && cp < 0x2b740) return false;
if (0x2b81e <= cp && cp < 0x2b820) return false;
if (0x2cea2 <= cp && cp < 0x2ceb0) return false;
if (0x2ebe1 <= cp && cp < 0x2f800) return false;
if (0x2fa1e <= cp && cp < 0x30000) return false;
if (0x3134b <= cp && cp < 0xe0100) return false;
if (0xe01f0 <= cp && cp < 0x110000) return false;
return cp < 0x110000;
} }
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
return add_space ? L" \"{}\"" : L"\"{}\""; inline auto needs_escape(uint32_t cp) -> bool {
return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
!is_printable(cp);
} }
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) { template <typename Char> struct find_escape_result {
return add_space ? " '{}'" : "'{}'"; const Char* begin;
const Char* end;
uint32_t cp;
};
template <typename Char>
auto find_escape(const Char* begin, const Char* end)
-> find_escape_result<Char> {
for (; begin != end; ++begin) {
auto cp = static_cast<typename std::make_unsigned<Char>::type>(*begin);
if (sizeof(Char) == 1 && cp >= 0x80) continue;
if (needs_escape(cp)) return {begin, begin + 1, cp};
}
return {begin, nullptr, 0};
} }
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'"; inline auto find_escape(const char* begin, const char* end)
-> find_escape_result<char> {
if (!is_utf8()) return find_escape<char>(begin, end);
auto result = find_escape_result<char>{end, nullptr, 0};
for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
[&](uint32_t cp, string_view sv) {
if (needs_escape(cp)) {
result = {sv.begin(), sv.end(), cp};
return false;
}
return true;
});
return result;
} }
template <typename Char, typename OutputIt>
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
*out++ = '"';
auto begin = str.begin(), end = str.end();
do {
auto escape = find_escape(begin, end);
out = copy_str<Char>(begin, escape.begin, out);
begin = escape.end;
if (!begin) break;
auto c = static_cast<Char>(escape.cp);
switch (escape.cp) {
case '\n':
*out++ = '\\';
c = 'n';
break;
case '\r':
*out++ = '\\';
c = 'r';
break;
case '\t':
*out++ = '\\';
c = 't';
break;
case '"':
FMT_FALLTHROUGH;
case '\\':
*out++ = '\\';
break;
default:
if (is_utf8()) {
if (escape.cp < 0x100) {
out = format_to(out, "\\x{:02x}", escape.cp);
continue;
}
if (escape.cp < 0x10000) {
out = format_to(out, "\\u{:04x}", escape.cp);
continue;
}
if (escape.cp < 0x110000) {
out = format_to(out, "\\U{:08x}", escape.cp);
continue;
}
}
for (Char escape_char : basic_string_view<Char>(
escape.begin, to_unsigned(escape.end - escape.begin))) {
out = format_to(
out, "\\x{:02x}",
static_cast<typename std::make_unsigned<Char>::type>(escape_char));
}
continue;
}
*out++ = c;
} while (begin != end);
*out++ = '"';
return out;
}
template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)>
inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt {
auto sv = std_string_view<Char>(str);
return write_range_entry<Char>(out, basic_string_view<Char>(sv));
}
template <typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
OutputIt write_range_entry(OutputIt out, const Arg v) {
*out++ = '\'';
*out++ = v;
*out++ = '\'';
return out;
}
template <
typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value &&
!std::is_same<Arg, Char>::value)>
OutputIt write_range_entry(OutputIt out, const Arg& v) {
return write<Char>(out, v);
}
} // namespace detail } // namespace detail
template <typename T> struct is_tuple_like { template <typename T> struct is_tuple_like {
@ -195,55 +547,37 @@ template <typename T> struct is_tuple_like {
template <typename TupleT, typename Char> template <typename TupleT, typename Char>
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> { struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
private: private:
// C++11 generic lambda for format() // C++11 generic lambda for format().
template <typename FormatContext> struct format_each { template <typename FormatContext> struct format_each {
template <typename T> void operator()(const T& v) { template <typename T> void operator()(const T& v) {
if (i > 0) { if (i > 0) out = detail::write_delimiter(out);
if (formatting.add_prepostfix_space) { out = detail::write_range_entry<Char>(out, v);
*out++ = ' ';
}
out = detail::copy(formatting.delimiter, out);
}
out = format_to(out,
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v),
v);
++i; ++i;
} }
int i;
formatting_tuple<Char>& formatting; typename FormatContext::iterator& out;
size_t& i;
typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out;
}; };
public: public:
formatting_tuple<Char> formatting;
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx); return ctx.begin();
} }
template <typename FormatContext = format_context> template <typename FormatContext = format_context>
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) { auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out(); auto out = ctx.out();
size_t i = 0; *out++ = '(';
detail::copy(formatting.prefix, out); detail::for_each(values, format_each<FormatContext>{0, out});
*out++ = ')';
detail::for_each(values, format_each<FormatContext>{formatting, i, out}); return out;
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
detail::copy(formatting.postfix, out);
return ctx.out();
} }
}; };
template <typename T, typename Char> struct is_range { template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value = static FMT_CONSTEXPR_DECL const bool value =
detail::is_range_<T>::value && !detail::is_like_std_string<T>::value && detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
!detail::is_map<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value && !std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<detail::std_string_view<Char>, T>::value; !std::is_constructible<detail::std_string_view<Char>, T>::value;
}; };
@ -251,100 +585,167 @@ template <typename T, typename Char> struct is_range {
template <typename T, typename Char> template <typename T, typename Char>
struct formatter< struct formatter<
T, Char, T, Char,
enable_if_t<fmt::is_range<T, Char>::value enable_if_t<
// Workaround a bug in MSVC 2017 and earlier. fmt::is_range<T, Char>::value
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927 // Workaround a bug in MSVC 2019 and earlier.
&& #if !FMT_MSC_VER
(has_formatter<detail::value_type<T>, format_context>::value || && (is_formattable<detail::value_type<T>, Char>::value ||
detail::has_fallback_formatter<detail::value_type<T>, detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
format_context>::value)
#endif #endif
>> { >> {
formatting_range<Char> formatting;
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx); return ctx.begin();
} }
template <typename FormatContext> template <
typename FormatContext::iterator format(const T& values, FormatContext& ctx) { typename FormatContext, typename U,
auto out = detail::copy(formatting.prefix, ctx.out()); FMT_ENABLE_IF(
size_t i = 0; std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
auto it = values.begin(); const T, T>>::value)>
auto end = values.end(); auto format(U& range, FormatContext& ctx) -> decltype(ctx.out()) {
#ifdef FMT_DEPRECATED_BRACED_RANGES
Char prefix = '{';
Char postfix = '}';
#else
Char prefix = detail::is_set<T>::value ? '{' : '[';
Char postfix = detail::is_set<T>::value ? '}' : ']';
#endif
auto out = ctx.out();
*out++ = prefix;
int i = 0;
auto it = std::begin(range);
auto end = std::end(range);
for (; it != end; ++it) { for (; it != end; ++it) {
if (i > 0) { if (i > 0) out = detail::write_delimiter(out);
if (formatting.add_prepostfix_space) *out++ = ' '; out = detail::write_range_entry<Char>(out, *it);
out = detail::copy(formatting.delimiter, out); ++i;
}
out = format_to(out,
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it),
*it);
if (++i > formatting.range_length_limit) {
out = format_to(out, " ... <other elements>");
break;
}
} }
if (formatting.add_prepostfix_space) *out++ = ' '; *out++ = postfix;
return detail::copy(formatting.postfix, out); return out;
}
};
template <typename T, typename Char>
struct formatter<
T, Char,
enable_if_t<
detail::is_map<T>::value
// Workaround a bug in MSVC 2019 and earlier.
#if !FMT_MSC_VER
&& (is_formattable<detail::value_type<T>, Char>::value ||
detail::has_fallback_formatter<detail::value_type<T>, Char>::value)
#endif
>> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <
typename FormatContext, typename U,
FMT_ENABLE_IF(
std::is_same<U, conditional_t<detail::has_const_begin_end<T>::value,
const T, T>>::value)>
auto format(U& map, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
*out++ = '{';
int i = 0;
for (const auto& item : map) {
if (i > 0) out = detail::write_delimiter(out);
out = detail::write_range_entry<Char>(out, item.first);
*out++ = ':';
*out++ = ' ';
out = detail::write_range_entry<Char>(out, item.second);
++i;
}
*out++ = '}';
return out;
} }
}; };
template <typename Char, typename... T> struct tuple_arg_join : detail::view { template <typename Char, typename... T> struct tuple_join_view : detail::view {
const std::tuple<T...>& tuple; const std::tuple<T...>& tuple;
basic_string_view<Char> sep; basic_string_view<Char> sep;
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s) tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
: tuple{t}, sep{s} {} : tuple(t), sep{s} {}
}; };
template <typename Char, typename... T> template <typename Char, typename... T>
struct formatter<tuple_arg_join<Char, T...>, Char> { using tuple_arg_join = tuple_join_view<Char, T...>;
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
// support in tuple_join. It is disabled by default because of issues with
// the dynamic width and precision.
#ifndef FMT_TUPLE_JOIN_SPECIFIERS
# define FMT_TUPLE_JOIN_SPECIFIERS 0
#endif
template <typename Char, typename... T>
struct formatter<tuple_join_view<Char, T...>, Char> {
template <typename ParseContext> template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin(); return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
} }
template <typename FormatContext> template <typename FormatContext>
typename FormatContext::iterator format( auto format(const tuple_join_view<Char, T...>& value,
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) { FormatContext& ctx) const -> typename FormatContext::iterator {
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{}); return do_format(value, ctx,
std::integral_constant<size_t, sizeof...(T)>());
} }
private: private:
template <typename FormatContext, size_t... N> std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx, template <typename ParseContext>
detail::index_sequence<N...>) { FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
return format_args(value, ctx, std::get<N>(value.tuple)...); std::integral_constant<size_t, 0>)
-> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename ParseContext, size_t N>
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
std::integral_constant<size_t, N>)
-> decltype(ctx.begin()) {
auto end = ctx.begin();
#if FMT_TUPLE_JOIN_SPECIFIERS
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
if (N > 1) {
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
if (end != end1)
FMT_THROW(format_error("incompatible format specs for tuple elements"));
}
#endif
return end;
} }
template <typename FormatContext> template <typename FormatContext>
typename FormatContext::iterator format_args( auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
const tuple_arg_join<Char, T...>&, FormatContext& ctx) { std::integral_constant<size_t, 0>) const ->
// NOTE: for compilers that support C++17, this empty function instantiation typename FormatContext::iterator {
// can be replaced with a constexpr branch in the variadic overload.
return ctx.out(); return ctx.out();
} }
template <typename FormatContext, typename Arg, typename... Args> template <typename FormatContext, size_t N>
typename FormatContext::iterator format_args( auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
const tuple_arg_join<Char, T...>& value, FormatContext& ctx, std::integral_constant<size_t, N>) const ->
const Arg& arg, const Args&... args) { typename FormatContext::iterator {
using base = formatter<typename std::decay<Arg>::type, Char>; auto out = std::get<sizeof...(T) - N>(formatters_)
auto out = ctx.out(); .format(std::get<sizeof...(T) - N>(value.tuple), ctx);
out = base{}.format(arg, ctx); if (N > 1) {
if (sizeof...(Args) > 0) {
out = std::copy(value.sep.begin(), value.sep.end(), out); out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out); ctx.advance_to(out);
return format_args(value, ctx, args...); return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
} }
return out; return out;
} }
}; };
FMT_MODULE_EXPORT_BEGIN
/** /**
\rst \rst
Returns an object that formats `tuple` with elements separated by `sep`. Returns an object that formats `tuple` with elements separated by `sep`.
@ -357,14 +758,15 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
\endrst \endrst
*/ */
template <typename... T> template <typename... T>
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple, FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
string_view sep) { -> tuple_join_view<char, T...> {
return {tuple, sep}; return {tuple, sep};
} }
template <typename... T> template <typename... T>
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple, FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
wstring_view sep) { basic_string_view<wchar_t> sep)
-> tuple_join_view<wchar_t, T...> {
return {tuple, sep}; return {tuple, sep};
} }
@ -380,17 +782,12 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
\endrst \endrst
*/ */
template <typename T> template <typename T>
arg_join<const T*, const T*, char> join(std::initializer_list<T> list, auto join(std::initializer_list<T> list, string_view sep)
string_view sep) { -> join_view<const T*, const T*> {
return join(std::begin(list), std::end(list), sep);
}
template <typename T>
arg_join<const T*, const T*, wchar_t> join(std::initializer_list<T> list,
wstring_view sep) {
return join(std::begin(list), std::end(list), sep); return join(std::begin(list), std::end(list), sep);
} }
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE FMT_END_NAMESPACE
#endif // FMT_RANGES_H_ #endif // FMT_RANGES_H_

@ -1,160 +0,0 @@
// Formatting library for C++ - time formatting
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_TIME_H_
#define FMT_TIME_H_
#include "format.h"
#include <ctime>
#include <locale>
FMT_BEGIN_NAMESPACE
// Prevents expansion of a preceding token as a function-style macro.
// Usage: f FMT_NOMACRO()
#define FMT_NOMACRO
namespace internal{
inline null<> localtime_r FMT_NOMACRO(...) { return null<>(); }
inline null<> localtime_s(...) { return null<>(); }
inline null<> gmtime_r(...) { return null<>(); }
inline null<> gmtime_s(...) { return null<>(); }
} // namespace internal
// Thread-safe replacement for std::localtime
inline std::tm localtime(std::time_t time) {
struct dispatcher {
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t): time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(localtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; }
bool handle(internal::null<>) {
using namespace fmt::internal;
return fallback(localtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) {
using namespace fmt::internal;
std::tm *tm = std::localtime(&time_);
if (tm) tm_ = *tm;
return tm != FMT_NULL;
}
#endif
};
dispatcher lt(time);
// Too big time values may be unsupported.
if (!lt.run())
FMT_THROW(format_error("time_t value out of range"));
return lt.tm_;
}
// Thread-safe replacement for std::gmtime
inline std::tm gmtime(std::time_t time) {
struct dispatcher {
std::time_t time_;
std::tm tm_;
dispatcher(std::time_t t): time_(t) {}
bool run() {
using namespace fmt::internal;
return handle(gmtime_r(&time_, &tm_));
}
bool handle(std::tm *tm) { return tm != FMT_NULL; }
bool handle(internal::null<>) {
using namespace fmt::internal;
return fallback(gmtime_s(&tm_, &time_));
}
bool fallback(int res) { return res == 0; }
#if !FMT_MSC_VER
bool fallback(internal::null<>) {
std::tm *tm = std::gmtime(&time_);
if (tm) tm_ = *tm;
return tm != FMT_NULL;
}
#endif
};
dispatcher gt(time);
// Too big time values may be unsupported.
if (!gt.run())
FMT_THROW(format_error("time_t value out of range"));
return gt.tm_;
}
namespace internal {
inline std::size_t strftime(char *str, std::size_t count, const char *format,
const std::tm *time) {
return std::strftime(str, count, format, time);
}
inline std::size_t strftime(wchar_t *str, std::size_t count,
const wchar_t *format, const std::tm *time) {
return std::wcsftime(str, count, format, time);
}
}
template <typename Char>
struct formatter<std::tm, Char> {
template <typename ParseContext>
auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
auto it = ctx.begin();
if (it != ctx.end() && *it == ':')
++it;
auto end = it;
while (end != ctx.end() && *end != '}')
++end;
tm_format.reserve(internal::to_unsigned(end - it + 1));
tm_format.append(it, end);
tm_format.push_back('\0');
return end;
}
template <typename FormatContext>
auto format(const std::tm &tm, FormatContext &ctx) -> decltype(ctx.out()) {
basic_memory_buffer<Char> buf;
std::size_t start = buf.size();
for (;;) {
std::size_t size = buf.capacity() - start;
std::size_t count =
internal::strftime(&buf[start], size, &tm_format[0], &tm);
if (count != 0) {
buf.resize(start + count);
break;
}
if (size >= tm_format.size() * 256) {
// If the buffer is 256 times larger than the format string, assume
// that `strftime` gives an empty result. There doesn't seem to be a
// better way to distinguish the two cases:
// https://github.com/fmtlib/fmt/issues/367
break;
}
const std::size_t MIN_GROWTH = 10;
buf.reserve(buf.capacity() + (size > MIN_GROWTH ? size : MIN_GROWTH));
}
return std::copy(buf.begin(), buf.end(), ctx.out());
}
basic_memory_buffer<Char> tm_format;
};
FMT_END_NAMESPACE
#endif // FMT_TIME_H_

@ -0,0 +1,236 @@
// Formatting library for C++ - optional wchar_t and exotic character support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_XCHAR_H_
#define FMT_XCHAR_H_
#include <cwchar>
#include <tuple>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
template <typename T>
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
}
FMT_MODULE_EXPORT_BEGIN
using wstring_view = basic_string_view<wchar_t>;
using wformat_parse_context = basic_format_parse_context<wchar_t>;
using wformat_context = buffer_context<wchar_t>;
using wformat_args = basic_format_args<wformat_context>;
using wmemory_buffer = basic_memory_buffer<wchar_t>;
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
// Workaround broken conversion on older gcc.
template <typename... Args> using wformat_string = wstring_view;
#else
template <typename... Args>
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
#endif
template <> struct is_char<wchar_t> : std::true_type {};
template <> struct is_char<detail::char8_type> : std::true_type {};
template <> struct is_char<char16_t> : std::true_type {};
template <> struct is_char<char32_t> : std::true_type {};
template <typename... Args>
constexpr format_arg_store<wformat_context, Args...> make_wformat_args(
const Args&... args) {
return {args...};
}
inline namespace literals {
constexpr auto operator"" _format(const wchar_t* s, size_t n)
-> detail::udl_formatter<wchar_t> {
return {{s, n}};
}
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
return {s};
}
#endif
} // namespace literals
template <typename It, typename Sentinel>
auto join(It begin, Sentinel end, wstring_view sep)
-> join_view<It, Sentinel, wchar_t> {
return {begin, end, sep};
}
template <typename Range>
auto join(Range&& range, wstring_view sep)
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>,
wchar_t> {
return join(std::begin(range), std::end(range), sep);
}
template <typename T>
auto join(std::initializer_list<T> list, wstring_view sep)
-> join_view<const T*, const T*, wchar_t> {
return join(std::begin(list), std::end(list), sep);
}
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto vformat(basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> {
basic_memory_buffer<Char> buffer;
detail::vformat_to(buffer, format_str, args);
return to_string(buffer);
}
// Pass char_t as a default template parameter instead of using
// std::basic_string<char_t<S>> to reduce the symbol size.
template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
auto format(const S& format_str, Args&&... args) -> std::basic_string<Char> {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat(to_string_view(format_str), vargs);
}
template <typename Locale, typename S, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat(
const Locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str), args);
}
template <typename Locale, typename S, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format(const Locale& loc, const S& format_str, Args&&... args)
-> std::basic_string<Char> {
return detail::vformat(loc, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
template <typename OutputIt, typename S, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
auto vformat_to(OutputIt out, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
detail::vformat_to(buf, to_string_view(format_str), args);
return detail::get_iterator(buf);
}
template <typename OutputIt, typename S, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format_to(OutputIt out, const S& fmt, Args&&... args) -> OutputIt {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
return vformat_to(out, to_string_view(fmt), vargs);
}
template <typename S, typename... Args, typename Char, size_t SIZE,
typename Allocator, FMT_ENABLE_IF(detail::is_string<S>::value)>
FMT_DEPRECATED auto format_to(basic_memory_buffer<Char, SIZE, Allocator>& buf,
const S& format_str, Args&&... args) ->
typename buffer_context<Char>::iterator {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
detail::vformat_to(buf, to_string_view(format_str), vargs, {});
return detail::buffer_appender<Char>(buf);
}
template <typename Locale, typename S, typename OutputIt, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to(
OutputIt out, const Locale& loc, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
auto&& buf = detail::get_buffer<Char>(out);
vformat_to(buf, to_string_view(format_str), args, detail::locale_ref(loc));
return detail::get_iterator(buf);
}
template <
typename OutputIt, typename Locale, typename S, typename... Args,
typename Char = char_t<S>,
bool enable = detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value>
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
const auto& vargs = fmt::make_args_checked<Args...>(format_str, args...);
return vformat_to(out, loc, to_string_view(format_str), vargs);
}
template <typename OutputIt, typename Char, typename... Args,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto vformat_to_n(
OutputIt out, size_t n, basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args)
-> format_to_n_result<OutputIt> {
detail::iterator_buffer<OutputIt, Char, detail::fixed_buffer_traits> buf(out,
n);
detail::vformat_to(buf, format_str, args);
return {buf.out(), buf.count()};
}
template <typename OutputIt, typename S, typename... Args,
typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
detail::is_exotic_char<Char>::value)>
inline auto format_to_n(OutputIt out, size_t n, const S& fmt,
const Args&... args) -> format_to_n_result<OutputIt> {
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
return vformat_to_n(out, n, to_string_view(fmt), vargs);
}
template <typename S, typename... Args, typename Char = char_t<S>,
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
inline auto formatted_size(const S& fmt, Args&&... args) -> size_t {
detail::counting_buffer<Char> buf;
const auto& vargs = fmt::make_args_checked<Args...>(fmt, args...);
detail::vformat_to(buf, to_string_view(fmt), vargs);
return buf.count();
}
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
wmemory_buffer buffer;
detail::vformat_to(buffer, fmt, args);
buffer.push_back(L'\0');
if (std::fputws(buffer.data(), f) == -1)
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
}
inline void vprint(wstring_view fmt, wformat_args args) {
vprint(stdout, fmt, args);
}
template <typename... T>
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
}
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
}
/**
Converts *value* to ``std::wstring`` using the default format for type *T*.
*/
template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
return format(FMT_STRING(L"{}"), value);
}
FMT_MODULE_EXPORT_END
FMT_END_NAMESPACE
#endif // FMT_XCHAR_H_

@ -10,6 +10,52 @@
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
namespace detail { namespace detail {
// DEPRECATED!
template <typename T = void> struct basic_data {
FMT_API static constexpr const char digits[100][2] = {
{'0', '0'}, {'0', '1'}, {'0', '2'}, {'0', '3'}, {'0', '4'}, {'0', '5'},
{'0', '6'}, {'0', '7'}, {'0', '8'}, {'0', '9'}, {'1', '0'}, {'1', '1'},
{'1', '2'}, {'1', '3'}, {'1', '4'}, {'1', '5'}, {'1', '6'}, {'1', '7'},
{'1', '8'}, {'1', '9'}, {'2', '0'}, {'2', '1'}, {'2', '2'}, {'2', '3'},
{'2', '4'}, {'2', '5'}, {'2', '6'}, {'2', '7'}, {'2', '8'}, {'2', '9'},
{'3', '0'}, {'3', '1'}, {'3', '2'}, {'3', '3'}, {'3', '4'}, {'3', '5'},
{'3', '6'}, {'3', '7'}, {'3', '8'}, {'3', '9'}, {'4', '0'}, {'4', '1'},
{'4', '2'}, {'4', '3'}, {'4', '4'}, {'4', '5'}, {'4', '6'}, {'4', '7'},
{'4', '8'}, {'4', '9'}, {'5', '0'}, {'5', '1'}, {'5', '2'}, {'5', '3'},
{'5', '4'}, {'5', '5'}, {'5', '6'}, {'5', '7'}, {'5', '8'}, {'5', '9'},
{'6', '0'}, {'6', '1'}, {'6', '2'}, {'6', '3'}, {'6', '4'}, {'6', '5'},
{'6', '6'}, {'6', '7'}, {'6', '8'}, {'6', '9'}, {'7', '0'}, {'7', '1'},
{'7', '2'}, {'7', '3'}, {'7', '4'}, {'7', '5'}, {'7', '6'}, {'7', '7'},
{'7', '8'}, {'7', '9'}, {'8', '0'}, {'8', '1'}, {'8', '2'}, {'8', '3'},
{'8', '4'}, {'8', '5'}, {'8', '6'}, {'8', '7'}, {'8', '8'}, {'8', '9'},
{'9', '0'}, {'9', '1'}, {'9', '2'}, {'9', '3'}, {'9', '4'}, {'9', '5'},
{'9', '6'}, {'9', '7'}, {'9', '8'}, {'9', '9'}};
FMT_API static constexpr const char hex_digits[] = "0123456789abcdef";
FMT_API static constexpr const char signs[4] = {0, '-', '+', ' '};
FMT_API static constexpr const char left_padding_shifts[5] = {31, 31, 0, 1,
0};
FMT_API static constexpr const char right_padding_shifts[5] = {0, 31, 0, 1,
0};
FMT_API static constexpr const unsigned prefixes[4] = {0, 0, 0x1000000u | '+',
0x1000000u | ' '};
};
#ifdef FMT_SHARED
// Required for -flto, -fivisibility=hidden and -shared to work
extern template struct basic_data<void>;
#endif
#if __cplusplus < 201703L
// DEPRECATED! These are here only for ABI compatiblity.
template <typename T> constexpr const char basic_data<T>::digits[][2];
template <typename T> constexpr const char basic_data<T>::hex_digits[];
template <typename T> constexpr const char basic_data<T>::signs[];
template <typename T> constexpr const char basic_data<T>::left_padding_shifts[];
template <typename T>
constexpr const char basic_data<T>::right_padding_shifts[];
template <typename T> constexpr const unsigned basic_data<T>::prefixes[];
#endif
template <typename T> template <typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision, int format_float(char* buf, std::size_t size, const char* format, int precision,
T value) { T value) {
@ -28,35 +74,8 @@ template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
FMT_NOEXCEPT; FMT_NOEXCEPT;
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x) template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
FMT_NOEXCEPT; FMT_NOEXCEPT;
// DEPRECATED! This function exists for ABI compatibility.
template <typename Char>
typename basic_format_context<std::back_insert_iterator<buffer<Char>>,
Char>::iterator
vformat_to(buffer<Char>& buf, basic_string_view<Char> format_str,
basic_format_args<basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
type_identity_t<Char>>>
args) {
using iterator = std::back_insert_iterator<buffer<char>>;
using context = basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<Char>>>,
type_identity_t<Char>>;
auto out = iterator(buf);
format_handler<iterator, Char, context> h(out, format_str, args, {});
parse_format_string<false>(format_str, h);
return out;
}
template basic_format_context<std::back_insert_iterator<buffer<char>>,
char>::iterator
vformat_to(buffer<char>&, string_view,
basic_format_args<basic_format_context<
std::back_insert_iterator<buffer<type_identity_t<char>>>,
type_identity_t<char>>>);
} // namespace detail } // namespace detail
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
// Workaround a bug in MSVC2013 that prevents instantiation of format_float. // Workaround a bug in MSVC2013 that prevents instantiation of format_float.
int (*instantiate_format_float)(double, int, detail::float_specs, int (*instantiate_format_float)(double, int, detail::float_specs,
detail::buffer<char>&) = detail::format_float; detail::buffer<char>&) = detail::format_float;
@ -68,12 +87,15 @@ template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
// Explicit instantiations for char. // Explicit instantiations for char.
template FMT_API std::string detail::grouping_impl<char>(locale_ref); template FMT_API auto detail::thousands_sep_impl(locale_ref)
template FMT_API char detail::thousands_sep_impl(locale_ref); -> thousands_sep_result<char>;
template FMT_API char detail::decimal_point_impl(locale_ref); template FMT_API char detail::decimal_point_impl(locale_ref);
template FMT_API void detail::buffer<char>::append(const char*, const char*); template FMT_API void detail::buffer<char>::append(const char*, const char*);
// DEPRECATED!
// There is no correspondent extern template in format.h because of
// incompatibility between clang and gcc (#2377).
template FMT_API void detail::vformat_to( template FMT_API void detail::vformat_to(
detail::buffer<char>&, string_view, detail::buffer<char>&, string_view,
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref); basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
@ -90,10 +112,13 @@ template FMT_API int detail::format_float(long double, int, detail::float_specs,
// Explicit instantiations for wchar_t. // Explicit instantiations for wchar_t.
template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref); template FMT_API auto detail::thousands_sep_impl(locale_ref)
template FMT_API wchar_t detail::thousands_sep_impl(locale_ref); -> thousands_sep_result<wchar_t>;
template FMT_API wchar_t detail::decimal_point_impl(locale_ref); template FMT_API wchar_t detail::decimal_point_impl(locale_ref);
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*, template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*); const wchar_t*);
template struct detail::basic_data<void>;
FMT_END_NAMESPACE FMT_END_NAMESPACE

@ -25,21 +25,18 @@
# define WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN
# endif # endif
# include <io.h> # include <io.h>
# include <windows.h>
# define O_CREAT _O_CREAT
# define O_TRUNC _O_TRUNC
# ifndef S_IRUSR # ifndef S_IRUSR
# define S_IRUSR _S_IREAD # define S_IRUSR _S_IREAD
# endif # endif
# ifndef S_IWUSR # ifndef S_IWUSR
# define S_IWUSR _S_IWRITE # define S_IWUSR _S_IWRITE
# endif # endif
# ifndef S_IRGRP
# ifdef __MINGW32__ # define S_IRGRP 0
# define _SH_DENYNO 0x40 # endif
# ifndef S_IROTH
# define S_IROTH 0
# endif # endif
# endif // _WIN32 # endif // _WIN32
#endif // FMT_USE_FCNTL #endif // FMT_USE_FCNTL
@ -55,7 +52,7 @@
namespace { namespace {
#ifdef _WIN32 #ifdef _WIN32
// Return type of read and write functions. // Return type of read and write functions.
using RWResult = int; using rwresult = int;
// On Windows the count argument to read and write is unsigned, so convert // On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow. // it from size_t preventing integer overflow.
@ -64,7 +61,7 @@ inline unsigned convert_rwcount(std::size_t count) {
} }
#elif FMT_USE_FCNTL #elif FMT_USE_FCNTL
// Return type of read and write functions. // Return type of read and write functions.
using RWResult = ssize_t; using rwresult = ssize_t;
inline std::size_t convert_rwcount(std::size_t count) { return count; } inline std::size_t convert_rwcount(std::size_t count) { return count; }
#endif #endif
@ -73,14 +70,14 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; }
FMT_BEGIN_NAMESPACE FMT_BEGIN_NAMESPACE
#ifdef _WIN32 #ifdef _WIN32
detail::utf16_to_utf8::utf16_to_utf8(wstring_view s) { detail::utf16_to_utf8::utf16_to_utf8(basic_string_view<wchar_t> s) {
if (int error_code = convert(s)) { if (int error_code = convert(s)) {
FMT_THROW(windows_error(error_code, FMT_THROW(windows_error(error_code,
"cannot convert string from UTF-16 to UTF-8")); "cannot convert string from UTF-16 to UTF-8"));
} }
} }
int detail::utf16_to_utf8::convert(wstring_view s) { int detail::utf16_to_utf8::convert(basic_string_view<wchar_t> s) {
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER; if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size()); int s_size = static_cast<int>(s.size());
if (s_size == 0) { if (s_size == 0) {
@ -101,46 +98,85 @@ int detail::utf16_to_utf8::convert(wstring_view s) {
return 0; return 0;
} }
void windows_error::init(int err_code, string_view format_str, namespace detail {
format_args args) {
error_code_ = err_code; class system_message {
memory_buffer buffer; system_message(const system_message&) = delete;
detail::format_windows_error(buffer, err_code, vformat(format_str, args)); void operator=(const system_message&) = delete;
std::runtime_error& base = *this;
base = std::runtime_error(to_string(buffer)); unsigned long result_;
wchar_t* message_;
static bool is_whitespace(wchar_t c) FMT_NOEXCEPT {
return c == L' ' || c == L'\n' || c == L'\r' || c == L'\t' || c == L'\0';
}
public:
explicit system_message(unsigned long error_code)
: result_(0), message_(nullptr) {
result_ = FormatMessageW(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
reinterpret_cast<wchar_t*>(&message_), 0, nullptr);
if (result_ != 0) {
while (result_ != 0 && is_whitespace(message_[result_ - 1])) {
--result_;
}
}
}
~system_message() { LocalFree(message_); }
explicit operator bool() const FMT_NOEXCEPT { return result_ != 0; }
operator basic_string_view<wchar_t>() const FMT_NOEXCEPT {
return basic_string_view<wchar_t>(message_, result_);
}
};
class utf8_system_category final : public std::error_category {
public:
const char* name() const FMT_NOEXCEPT override { return "system"; }
std::string message(int error_code) const override {
system_message msg(error_code);
if (msg) {
utf16_to_utf8 utf8_message;
if (utf8_message.convert(msg) == ERROR_SUCCESS) {
return utf8_message.str();
}
}
return "unknown error";
}
};
} // namespace detail
FMT_API const std::error_category& system_category() FMT_NOEXCEPT {
static const detail::utf8_system_category category;
return category;
}
std::system_error vwindows_error(int err_code, string_view format_str,
format_args args) {
auto ec = std::error_code(err_code, system_category());
return std::system_error(ec, vformat(format_str, args));
} }
void detail::format_windows_error(detail::buffer<char>& out, int error_code, void detail::format_windows_error(detail::buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT { const char* message) FMT_NOEXCEPT {
FMT_TRY { FMT_TRY {
wmemory_buffer buf; system_message msg(error_code);
buf.resize(inline_buffer_size); if (msg) {
for (;;) { utf16_to_utf8 utf8_message;
wchar_t* system_message = &buf[0]; if (utf8_message.convert(msg) == ERROR_SUCCESS) {
int result = FormatMessageW( format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message);
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, return;
error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), system_message,
static_cast<uint32_t>(buf.size()), nullptr);
if (result != 0) {
utf16_to_utf8 utf8_message;
if (utf8_message.convert(system_message) == ERROR_SUCCESS) {
format_to(buffer_appender<char>(out), "{}: {}", message,
utf8_message);
return;
}
break;
} }
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
break; // Can't get error message, report error code instead.
buf.resize(buf.size() * 2);
} }
} }
FMT_CATCH(...) {} FMT_CATCH(...) {}
format_error_code(out, error_code, message); format_error_code(out, error_code, message);
} }
void report_windows_error(int error_code, void report_windows_error(int error_code, const char* message) FMT_NOEXCEPT {
fmt::string_view message) FMT_NOEXCEPT {
report_error(detail::format_windows_error, error_code, message); report_error(detail::format_windows_error, error_code, message);
} }
#endif // _WIN32 #endif // _WIN32
@ -175,7 +211,10 @@ int buffered_file::fileno() const {
#if FMT_USE_FCNTL #if FMT_USE_FCNTL
file::file(cstring_view path, int oflag) { file::file(cstring_view path, int oflag) {
int mode = S_IRUSR | S_IWUSR; # ifdef _WIN32
using mode_t = int;
# endif
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
# if defined(_WIN32) && !defined(__MINGW32__) # if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1; fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode)); FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
@ -229,14 +268,14 @@ long long file::size() const {
} }
std::size_t file::read(void* buffer, std::size_t count) { std::size_t file::read(void* buffer, std::size_t count) {
RWResult result = 0; rwresult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count)))); FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file")); if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
return detail::to_unsigned(result); return detail::to_unsigned(result);
} }
std::size_t file::write(const void* buffer, std::size_t count) { std::size_t file::write(const void* buffer, std::size_t count) {
RWResult result = 0; rwresult result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count)))); FMT_RETRY(result, FMT_POSIX_CALL(write(fd_, buffer, convert_rwcount(count))));
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file")); if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
return detail::to_unsigned(result); return detail::to_unsigned(result);
@ -260,10 +299,10 @@ void file::dup2(int fd) {
} }
} }
void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT { void file::dup2(int fd, std::error_code& ec) FMT_NOEXCEPT {
int result = 0; int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd))); FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) ec = error_code(errno); if (result == -1) ec = std::error_code(errno, std::generic_category());
} }
void file::pipe(file& read_end, file& write_end) { void file::pipe(file& read_end, file& write_end) {

@ -36,16 +36,15 @@
#include "base/string_util.hh" #include "base/string_util.hh"
#include "config.h" #include "config.h"
#include "fmt/format.h" #include "fmt/format.h"
#include "fmt/printf.h"
#include "readline_highlighters.hh" #include "readline_highlighters.hh"
using namespace std;
std::multimap<std::string, help_text*> help_text::TAGGED; std::multimap<std::string, help_text*> help_text::TAGGED;
static vector<help_text*> static std::vector<help_text*>
get_related(const help_text& ht) get_related(const help_text& ht)
{ {
vector<help_text*> retval; std::vector<help_text*> retval;
for (const auto& tag : ht.ht_tags) { for (const auto& tag : ht.ht_tags) {
auto tagged = help_text::TAGGED.equal_range(tag); auto tagged = help_text::TAGGED.equal_range(tag);
@ -97,7 +96,7 @@ format_help_text_for_term(const help_text& ht,
.append(body_indent, ' ') .append(body_indent, ' ')
.append(":") .append(":")
.append(ht.ht_name, &view_curses::VC_STYLE, A_BOLD); .append(ht.ht_name, &view_curses::VC_STYLE, A_BOLD);
for (auto& param : ht.ht_parameters) { for (const auto& param : ht.ht_parameters) {
out.append(" "); out.append(" ");
if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) { if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) {
out.append("["); out.append("[");
@ -138,7 +137,7 @@ format_help_text_for_term(const help_text& ht,
out.append(body_indent, ' ') out.append(body_indent, ' ')
.append(ht.ht_name, &view_curses::VC_STYLE, A_BOLD) .append(ht.ht_name, &view_curses::VC_STYLE, A_BOLD)
.append("("); .append("(");
for (auto& param : ht.ht_parameters) { for (const auto& param : ht.ht_parameters) {
if (!param.ht_flag_name && needs_comma) { if (!param.ht_flag_name && needs_comma) {
out.append(", "); out.append(", ");
} }
@ -187,7 +186,7 @@ format_help_text_for_term(const help_text& ht,
.append(body_indent, ' ') .append(body_indent, ' ')
.append(";") .append(";")
.append(ht.ht_name, &view_curses::VC_STYLE, A_BOLD); .append(ht.ht_name, &view_curses::VC_STYLE, A_BOLD);
for (auto& param : ht.ht_parameters) { for (const auto& param : ht.ht_parameters) {
out.append(" "); out.append(" ");
if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) { if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) {
out.append("["); out.append("[");
@ -226,7 +225,7 @@ format_help_text_for_term(const help_text& ht,
out.append(body_indent, ' ') out.append(body_indent, ' ')
.append( .append(
ht.ht_name, &view_curses::VC_STYLE, is_infix ? 0 : A_BOLD); ht.ht_name, &view_curses::VC_STYLE, is_infix ? 0 : A_BOLD);
for (auto& param : ht.ht_parameters) { for (const auto& param : ht.ht_parameters) {
if (break_all if (break_all
|| (int) (out.get_string().length() - start_index || (int) (out.get_string().length() - start_index
- line_start + 10) - line_start + 10)
@ -338,7 +337,7 @@ format_help_text_for_term(const help_text& ht,
if (!synopsis_only && !ht.ht_parameters.empty()) { if (!synopsis_only && !ht.ht_parameters.empty()) {
size_t max_param_name_width = 0; size_t max_param_name_width = 0;
for (auto& param : ht.ht_parameters) { for (const auto& param : ht.ht_parameters) {
max_param_name_width max_param_name_width
= std::max(strlen(param.ht_name), max_param_name_width); = std::max(strlen(param.ht_name), max_param_name_width);
} }
@ -348,7 +347,7 @@ format_help_text_for_term(const help_text& ht,
A_UNDERLINE) A_UNDERLINE)
.append("\n"); .append("\n");
for (auto& param : ht.ht_parameters) { for (const auto& param : ht.ht_parameters) {
if (!param.ht_summary) { if (!param.ht_summary) {
continue; continue;
} }
@ -367,7 +366,7 @@ format_help_text_for_term(const help_text& ht,
if (!synopsis_only && !ht.ht_results.empty()) { if (!synopsis_only && !ht.ht_results.empty()) {
size_t max_result_name_width = 0; size_t max_result_name_width = 0;
for (auto& result : ht.ht_results) { for (const auto& result : ht.ht_results) {
max_result_name_width max_result_name_width
= std::max(strlen(result.ht_name), max_result_name_width); = std::max(strlen(result.ht_name), max_result_name_width);
} }
@ -377,7 +376,7 @@ format_help_text_for_term(const help_text& ht,
A_UNDERLINE) A_UNDERLINE)
.append("\n"); .append("\n");
for (auto& result : ht.ht_results) { for (const auto& result : ht.ht_results) {
if (!result.ht_summary) { if (!result.ht_summary) {
continue; continue;
} }
@ -395,10 +394,10 @@ format_help_text_for_term(const help_text& ht,
} }
if (!synopsis_only && !ht.ht_tags.empty()) { if (!synopsis_only && !ht.ht_tags.empty()) {
auto related_help = get_related(ht); auto related_help = get_related(ht);
auto related_refs = vector<string>(); auto related_refs = std::vector<std::string>();
for (auto related : related_help) { for (const auto* related : related_help) {
string name = related->ht_name; std::string name = related->ht_name;
switch (related->ht_context) { switch (related->ht_context) {
case help_context_t::HC_COMMAND: case help_context_t::HC_COMMAND:
name = ":" + name; name = ":" + name;
@ -451,7 +450,7 @@ format_example_text_for_term(const help_text& ht,
&view_curses::VC_STYLE, &view_curses::VC_STYLE,
A_UNDERLINE) A_UNDERLINE)
.append("\n"); .append("\n");
for (auto& ex : ht.ht_example) { for (const auto& ex : ht.ht_example) {
attr_line_t ex_line(ex.he_cmd); attr_line_t ex_line(ex.he_cmd);
size_t keyword_offset = 0; size_t keyword_offset = 0;
const char* space = strchr(ex.he_cmd, ' '); const char* space = strchr(ex.he_cmd, ' ');
@ -482,7 +481,7 @@ format_example_text_for_term(const help_text& ht,
} }
out.append("#") out.append("#")
.append(to_string(count)) .append(fmt::to_string(count))
.append(" ") .append(" ")
.append(ex.he_description, &tws.with_indent(3)) .append(ex.he_description, &tws.with_indent(3))
.append(":\n ") .append(":\n ")
@ -504,7 +503,7 @@ link_name(const help_text& ht)
const static std::regex SCRUBBER("[^\\w_]"); const static std::regex SCRUBBER("[^\\w_]");
bool is_sql_infix = ht.ht_context == help_context_t::HC_SQL_INFIX; bool is_sql_infix = ht.ht_context == help_context_t::HC_SQL_INFIX;
string scrubbed_name; std::string scrubbed_name;
if (is_sql_infix) { if (is_sql_infix) {
scrubbed_name = "infix"; scrubbed_name = "infix";
@ -514,7 +513,7 @@ link_name(const help_text& ht)
if (ht.ht_function_type == help_function_type_t::HFT_AGGREGATE) { if (ht.ht_function_type == help_function_type_t::HFT_AGGREGATE) {
scrubbed_name += "_agg"; scrubbed_name += "_agg";
} }
for (auto& param : ht.ht_parameters) { for (const auto& param : ht.ht_parameters) {
if (!is_sql_infix && param.ht_name[0]) { if (!is_sql_infix && param.ht_name[0]) {
continue; continue;
} }
@ -565,66 +564,66 @@ format_help_text_for_rst(const help_text& ht,
break; break;
} }
fprintf(rst_file, "\n.. _%s:\n\n", link_name(ht).c_str()); fmt::print(rst_file, FMT_STRING("\n.. _{}:\n\n"), link_name(ht));
out_count += fprintf(rst_file, "%s%s", prefix, ht.ht_name); out_count += fmt::fprintf(rst_file, "%s%s", prefix, ht.ht_name);
if (is_sql_func) { if (is_sql_func) {
out_count += fprintf(rst_file, "("); out_count += fmt::fprintf(rst_file, "(");
} }
bool needs_comma = false; bool needs_comma = false;
for (auto& param : ht.ht_parameters) { for (const auto& param : ht.ht_parameters) {
if (needs_comma) { if (needs_comma) {
if (param.ht_flag_name) { if (param.ht_flag_name) {
out_count += fprintf(rst_file, " "); out_count += fmt::fprintf(rst_file, " ");
} else { } else {
out_count += fprintf(rst_file, ", "); out_count += fmt::fprintf(rst_file, ", ");
} }
} }
if (!is_sql_func) { if (!is_sql_func) {
out_count += fprintf(rst_file, " "); out_count += fmt::fprintf(rst_file, " ");
} }
if (param.ht_flag_name) { if (param.ht_flag_name) {
out_count += fprintf(rst_file, "%s ", param.ht_flag_name); out_count += fmt::fprintf(rst_file, "%s ", param.ht_flag_name);
} }
if (param.ht_name[0]) { if (param.ht_name[0]) {
out_count += fprintf(rst_file, "*"); out_count += fmt::fprintf(rst_file, "*");
if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) { if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) {
out_count += fprintf(rst_file, "\\["); out_count += fmt::fprintf(rst_file, "\\[");
} }
out_count += fprintf(rst_file, "%s", param.ht_name); out_count += fmt::fprintf(rst_file, "%s", param.ht_name);
if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) { if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) {
out_count += fprintf(rst_file, "\\]"); out_count += fmt::fprintf(rst_file, "\\]");
} }
out_count += fprintf(rst_file, "*"); out_count += fmt::fprintf(rst_file, "*");
} }
if (is_sql_func) { if (is_sql_func) {
needs_comma = true; needs_comma = true;
} }
} }
if (is_sql_func) { if (is_sql_func) {
out_count += fprintf(rst_file, ")"); out_count += fmt::fprintf(rst_file, ")");
} }
fprintf(rst_file, "\n"); fmt::fprintf(rst_file, "\n");
fprintf(rst_file, "%s\n\n", string(out_count, '^').c_str()); fmt::print(rst_file, FMT_STRING("{0:^^{1}}\n\n"), "", out_count);
fprintf(rst_file, " %s\n", ht.ht_summary); fmt::fprintf(rst_file, " %s\n", ht.ht_summary);
fprintf(rst_file, "\n"); fmt::fprintf(rst_file, "\n");
if (ht.ht_description) { if (ht.ht_description != nullptr) {
fprintf(rst_file, " %s\n", ht.ht_description); fmt::fprintf(rst_file, " %s\n", ht.ht_description);
} }
int param_count = 0; int param_count = 0;
for (auto& param : ht.ht_parameters) { for (const auto& param : ht.ht_parameters) {
if (param.ht_summary && param.ht_summary[0]) { if (param.ht_summary && param.ht_summary[0]) {
param_count += 1; param_count += 1;
} }
} }
if (param_count > 0) { if (param_count > 0) {
fprintf(rst_file, " **Parameters**\n"); fmt::fprintf(rst_file, " **Parameters**\n");
for (auto& param : ht.ht_parameters) { for (const auto& param : ht.ht_parameters) {
if (param.ht_summary && param.ht_summary[0]) { if (param.ht_summary && param.ht_summary[0]) {
fprintf( fmt::fprintf(
rst_file, rst_file,
" * **%s%s** --- %s\n", " * **%s%s** --- %s\n",
param.ht_name, param.ht_name,
@ -632,44 +631,44 @@ format_help_text_for_rst(const help_text& ht,
param.ht_summary); param.ht_summary);
} }
} }
fprintf(rst_file, "\n"); fmt::fprintf(rst_file, "\n");
} }
if (is_sql) { if (is_sql) {
prefix = ";"; prefix = ";";
} }
if (!ht.ht_example.empty()) { if (!ht.ht_example.empty()) {
fprintf(rst_file, " **Examples**\n"); fmt::fprintf(rst_file, " **Examples**\n");
for (auto& example : ht.ht_example) { for (const auto& example : ht.ht_example) {
fprintf(rst_file, " %s:\n\n", example.he_description); fmt::fprintf(rst_file, " %s:\n\n", example.he_description);
fprintf(rst_file, fmt::fprintf(rst_file,
" .. code-block:: %s\n\n", " .. code-block:: %s\n\n",
is_sql ? "custsqlite" : "lnav"); is_sql ? "custsqlite" : "lnav");
if (ht.ht_context == help_context_t::HC_COMMAND) { if (ht.ht_context == help_context_t::HC_COMMAND) {
fprintf(rst_file, fmt::fprintf(rst_file,
" %s%s %s\n", " %s%s %s\n",
prefix, prefix,
ht.ht_name, ht.ht_name,
example.he_cmd); example.he_cmd);
} else { } else {
fprintf(rst_file, " %s%s\n", prefix, example.he_cmd); fmt::fprintf(rst_file, " %s%s\n", prefix, example.he_cmd);
} }
auto result = eval(ht, example); auto result = eval(ht, example);
if (!result.empty()) { if (!result.empty()) {
vector<attr_line_t> lines; std::vector<attr_line_t> lines;
result.split_lines(lines); result.split_lines(lines);
for (auto& line : lines) { for (const auto& line : lines) {
fprintf(rst_file, " %s\n", line.get_string().c_str()); fmt::fprintf(rst_file, " %s\n", line.get_string());
} }
} }
fprintf(rst_file, "\n"); fmt::fprintf(rst_file, "\n");
} }
} }
if (!ht.ht_tags.empty()) { if (!ht.ht_tags.empty()) {
auto related_refs = vector<string>(); auto related_refs = std::vector<std::string>();
for (auto related : get_related(ht)) { for (const auto* related : get_related(ht)) {
related_refs.emplace_back( related_refs.emplace_back(
fmt::format(":ref:`{}`", link_name(*related))); fmt::format(":ref:`{}`", link_name(*related)));
} }
@ -680,5 +679,5 @@ format_help_text_for_rst(const help_text& ht,
fmt::join(related_refs, ", ")); fmt::join(related_refs, ", "));
} }
fprintf(rst_file, "\n----\n\n"); fmt::fprintf(rst_file, "\n----\n\n");
} }

@ -31,11 +31,7 @@
#include "base/math_util.hh" #include "base/math_util.hh"
#include "config.h" #include "config.h"
#include "fmt/chrono.h"
using namespace std;
const char* hist_source2::LINE_FORMAT
= " %8d normal %8d errors %8d warnings %8d marks";
nonstd::optional<vis_line_t> nonstd::optional<vis_line_t>
hist_source2::row_for_time(struct timeval tv_bucket) hist_source2::row_for_time(struct timeval tv_bucket)
@ -73,27 +69,22 @@ hist_source2::text_value_for_line(textview_curses& tc,
{ {
bucket_t& bucket = this->find_bucket(row); bucket_t& bucket = this->find_bucket(row);
struct tm bucket_tm; struct tm bucket_tm;
char tm_buffer[128];
char line[256];
value_out.clear();
if (gmtime_r(&bucket.b_time, &bucket_tm) != nullptr) { if (gmtime_r(&bucket.b_time, &bucket_tm) != nullptr) {
strftime( fmt::format_to(std::back_inserter(value_out),
tm_buffer, sizeof(tm_buffer), " %a %b %d %H:%M:%S ", &bucket_tm); FMT_STRING(" {:%a %b %d %H:%M:%S} "),
bucket_tm);
} else { } else {
log_error("no time?"); log_error("no time?");
tm_buffer[0] = '\0';
} }
snprintf(line, fmt::format_to(
sizeof(line), std::back_inserter(value_out),
LINE_FORMAT, FMT_STRING(" {:8L} normal {:8L} errors {:8L} warnings {:8L} marks"),
(int) rint(bucket.b_values[HT_NORMAL].hv_value), rint(bucket.b_values[HT_NORMAL].hv_value),
(int) rint(bucket.b_values[HT_ERROR].hv_value), rint(bucket.b_values[HT_ERROR].hv_value),
(int) rint(bucket.b_values[HT_WARNING].hv_value), rint(bucket.b_values[HT_WARNING].hv_value),
(int) rint(bucket.b_values[HT_MARK].hv_value)); rint(bucket.b_values[HT_MARK].hv_value));
value_out.clear();
value_out.append(tm_buffer);
value_out.append(line);
} }
void void

@ -351,7 +351,7 @@ public:
size_t text_line_width(textview_curses& curses) override size_t text_line_width(textview_curses& curses) override
{ {
return strlen(LINE_FORMAT) + 8 * 4; return 48 + 8 * 4;
}; };
void clear(); void clear();
@ -382,8 +382,6 @@ public:
struct timeval tv_bucket) override; struct timeval tv_bucket) override;
private: private:
static const char* LINE_FORMAT;
struct hist_value { struct hist_value {
double hv_value; double hv_value;
}; };

@ -194,11 +194,8 @@ handle_paging_key(int ch)
text_sub_source* tc_tss = tc->get_sub_source(); text_sub_source* tc_tss = tc->get_sub_source();
bookmarks<vis_line_t>::type& bm = tc->get_bookmarks(); bookmarks<vis_line_t>::type& bm = tc->get_bookmarks();
char keyseq[16]; auto keyseq = fmt::format(FMT_STRING("x{:02x}"), ch);
if (handle_keyseq(keyseq.c_str())) {
snprintf(keyseq, sizeof(keyseq), "x%02x", ch);
if (handle_keyseq(keyseq)) {
return true; return true;
} }
@ -739,13 +736,17 @@ handle_paging_key(int ch)
} }
if (log_line_index != -1) { if (log_line_index != -1) {
char linestr[64]; fmt::memory_buffer linestr;
int line_number = (int) tc->get_top(); int line_number = (int) tc->get_top();
unsigned int row; unsigned int row;
snprintf(linestr, sizeof(linestr), "%d", line_number); fmt::format_to(std::back_inserter(linestr),
FMT_STRING("{}"),
line_number);
linestr.push_back('\0');
for (row = 0; row < dls.dls_rows.size(); row++) { for (row = 0; row < dls.dls_rows.size(); row++) {
if (strcmp(dls.dls_rows[row][log_line_index], linestr) if (strcmp(dls.dls_rows[row][log_line_index],
linestr.data())
== 0) { == 0) {
vis_line_t db_line(row); vis_line_t db_line(row);

@ -29,10 +29,9 @@
* @file json-extension-functions.cc * @file json-extension-functions.cc
*/ */
#include <cstring>
#include <string> #include <string>
#include <string.h>
#include "config.h" #include "config.h"
#include "mapbox/variant.hpp" #include "mapbox/variant.hpp"
#include "sqlite-extension-func.hh" #include "sqlite-extension-func.hh"
@ -43,17 +42,16 @@
#include "yajlpp/json_op.hh" #include "yajlpp/json_op.hh"
#include "yajlpp/yajlpp.hh" #include "yajlpp/yajlpp.hh"
using namespace std;
using namespace mapbox; using namespace mapbox;
#define JSON_SUBTYPE 74 /* Ascii for "J" */ #define JSON_SUBTYPE 74 /* Ascii for "J" */
class sql_json_op : public json_op { class sql_json_op : public json_op {
public: public:
sql_json_op(json_ptr& ptr) : json_op(ptr){}; explicit sql_json_op(json_ptr& ptr) : json_op(ptr){};
int sjo_type{-1}; int sjo_type{-1};
string sjo_str; std::string sjo_str;
int64_t sjo_int{0}; int64_t sjo_int{0};
double sjo_float{0.0}; double sjo_float{0.0};
}; };
@ -120,7 +118,7 @@ json_contains(vtab_types::nullable<const char> nullable_json_in,
return false; return false;
} }
auto json_in = nullable_json_in.n_value; const auto* json_in = nullable_json_in.n_value;
auto_mem<yajl_handle_t> handle(yajl_free); auto_mem<yajl_handle_t> handle(yajl_free);
yajl_callbacks cb; yajl_callbacks cb;
contains_userdata cu; contains_userdata cu;
@ -220,7 +218,7 @@ gen_handle_string(void* ctx, const unsigned char* stringVal, size_t len)
if (sjo->jo_ptr.jp_state == json_ptr::MS_DONE) { if (sjo->jo_ptr.jp_state == json_ptr::MS_DONE) {
sjo->sjo_type = SQLITE3_TEXT; sjo->sjo_type = SQLITE3_TEXT;
sjo->sjo_str = string((char*) stringVal, len); sjo->sjo_str = std::string((char*) stringVal, len);
} else { } else {
sjo->jo_ptr_error_code = yajl_gen_string(gen, stringVal, len); sjo->jo_ptr_error_code = yajl_gen_string(gen, stringVal, len);
} }
@ -375,7 +373,7 @@ struct concat_context {
static int static int
concat_gen_null(void* ctx) concat_gen_null(void* ctx)
{ {
auto cc = static_cast<concat_context*>(ctx); auto* cc = static_cast<concat_context*>(ctx);
if (cc->cc_depth > 0) { if (cc->cc_depth > 0) {
return yajl_gen_null(cc->cc_gen_handle) == yajl_gen_status_ok; return yajl_gen_null(cc->cc_gen_handle) == yajl_gen_status_ok;
@ -387,7 +385,7 @@ concat_gen_null(void* ctx)
static int static int
concat_gen_boolean(void* ctx, int val) concat_gen_boolean(void* ctx, int val)
{ {
auto cc = static_cast<concat_context*>(ctx); auto* cc = static_cast<concat_context*>(ctx);
return yajl_gen_bool(cc->cc_gen_handle, val) == yajl_gen_status_ok; return yajl_gen_bool(cc->cc_gen_handle, val) == yajl_gen_status_ok;
} }
@ -395,7 +393,7 @@ concat_gen_boolean(void* ctx, int val)
static int static int
concat_gen_number(void* ctx, const char* val, size_t len) concat_gen_number(void* ctx, const char* val, size_t len)
{ {
auto cc = static_cast<concat_context*>(ctx); auto* cc = static_cast<concat_context*>(ctx);
return yajl_gen_number(cc->cc_gen_handle, val, len) == yajl_gen_status_ok; return yajl_gen_number(cc->cc_gen_handle, val, len) == yajl_gen_status_ok;
} }
@ -403,7 +401,7 @@ concat_gen_number(void* ctx, const char* val, size_t len)
static int static int
concat_gen_string(void* ctx, const unsigned char* val, size_t len) concat_gen_string(void* ctx, const unsigned char* val, size_t len)
{ {
auto cc = static_cast<concat_context*>(ctx); auto* cc = static_cast<concat_context*>(ctx);
return yajl_gen_string(cc->cc_gen_handle, val, len) == yajl_gen_status_ok; return yajl_gen_string(cc->cc_gen_handle, val, len) == yajl_gen_status_ok;
} }
@ -411,7 +409,7 @@ concat_gen_string(void* ctx, const unsigned char* val, size_t len)
static int static int
concat_gen_start_map(void* ctx) concat_gen_start_map(void* ctx)
{ {
auto cc = static_cast<concat_context*>(ctx); auto* cc = static_cast<concat_context*>(ctx);
cc->cc_depth += 1; cc->cc_depth += 1;
return yajl_gen_map_open(cc->cc_gen_handle) == yajl_gen_status_ok; return yajl_gen_map_open(cc->cc_gen_handle) == yajl_gen_status_ok;
@ -420,7 +418,7 @@ concat_gen_start_map(void* ctx)
static int static int
concat_gen_end_map(void* ctx) concat_gen_end_map(void* ctx)
{ {
auto cc = static_cast<concat_context*>(ctx); auto* cc = static_cast<concat_context*>(ctx);
cc->cc_depth -= 1; cc->cc_depth -= 1;
return yajl_gen_map_close(cc->cc_gen_handle) == yajl_gen_status_ok; return yajl_gen_map_close(cc->cc_gen_handle) == yajl_gen_status_ok;
@ -429,7 +427,7 @@ concat_gen_end_map(void* ctx)
static int static int
concat_gen_map_key(void* ctx, const unsigned char* key, size_t len) concat_gen_map_key(void* ctx, const unsigned char* key, size_t len)
{ {
auto cc = static_cast<concat_context*>(ctx); auto* cc = static_cast<concat_context*>(ctx);
return yajl_gen_string(cc->cc_gen_handle, key, len) == yajl_gen_status_ok; return yajl_gen_string(cc->cc_gen_handle, key, len) == yajl_gen_status_ok;
} }
@ -437,7 +435,7 @@ concat_gen_map_key(void* ctx, const unsigned char* key, size_t len)
static int static int
concat_gen_start_array(void* ctx) concat_gen_start_array(void* ctx)
{ {
auto cc = static_cast<concat_context*>(ctx); auto* cc = static_cast<concat_context*>(ctx);
cc->cc_depth += 1; cc->cc_depth += 1;
if (cc->cc_depth == 1) { if (cc->cc_depth == 1) {
@ -449,7 +447,7 @@ concat_gen_start_array(void* ctx)
static int static int
concat_gen_end_array(void* ctx) concat_gen_end_array(void* ctx)
{ {
auto cc = static_cast<concat_context*>(ctx); auto* cc = static_cast<concat_context*>(ctx);
cc->cc_depth -= 1; cc->cc_depth -= 1;
if (cc->cc_depth == 0) { if (cc->cc_depth == 0) {
@ -480,7 +478,7 @@ concat_gen_elements(yajl_gen gen, const unsigned char* text, size_t len)
if (yajl_parse(handle, (const unsigned char*) text, len) != yajl_status_ok if (yajl_parse(handle, (const unsigned char*) text, len) != yajl_status_ok
|| yajl_complete_parse(handle) != yajl_status_ok) || yajl_complete_parse(handle) != yajl_status_ok)
{ {
unique_ptr<unsigned char, decltype(&free)> err_msg( std::unique_ptr<unsigned char, decltype(&free)> err_msg(
yajl_get_error(handle, 1, (const unsigned char*) text, len), free); yajl_get_error(handle, 1, (const unsigned char*) text, len), free);
throw sqlite_func_error("Invalid JSON: {}", throw sqlite_func_error("Invalid JSON: {}",
@ -490,7 +488,7 @@ concat_gen_elements(yajl_gen gen, const unsigned char* text, size_t len)
static json_string static json_string
json_concat(nonstd::optional<const char*> json_in, json_concat(nonstd::optional<const char*> json_in,
const vector<sqlite3_value*>& values) const std::vector<sqlite3_value*>& values)
{ {
yajlpp_gen gen; yajlpp_gen gen;
@ -618,7 +616,7 @@ sql_json_group_object_final(sqlite3_context* context)
json_agg_context* jac json_agg_context* jac
= (json_agg_context*) sqlite3_aggregate_context(context, 0); = (json_agg_context*) sqlite3_aggregate_context(context, 0);
if (jac == NULL) { if (jac == nullptr) {
sqlite3_result_text(context, "{}", -1, SQLITE_STATIC); sqlite3_result_text(context, "{}", -1, SQLITE_STATIC);
} else { } else {
const unsigned char* buf; const unsigned char* buf;
@ -642,8 +640,8 @@ sql_json_group_array_step(sqlite3_context* context,
json_agg_context* jac = (json_agg_context*) sqlite3_aggregate_context( json_agg_context* jac = (json_agg_context*) sqlite3_aggregate_context(
context, sizeof(json_agg_context)); context, sizeof(json_agg_context));
if (jac->jac_yajl_gen == NULL) { if (jac->jac_yajl_gen == nullptr) {
jac->jac_yajl_gen = yajl_gen_alloc(NULL); jac->jac_yajl_gen = yajl_gen_alloc(nullptr);
yajl_gen_config(jac->jac_yajl_gen, yajl_gen_beautify, false); yajl_gen_config(jac->jac_yajl_gen, yajl_gen_beautify, false);
yajl_gen_array_open(jac->jac_yajl_gen); yajl_gen_array_open(jac->jac_yajl_gen);
@ -737,13 +735,18 @@ json_extension_functions(struct FuncDef** basic_funcs,
"The value(s) to add to the end of the array.") "The value(s) to add to the end of the array.")
.one_or_more()) .one_or_more())
.with_tags({"json"}) .with_tags({"json"})
.with_example({"To append the number 4 to null", .with_example({
"SELECT json_concat(NULL, 4)"}) "To append the number 4 to null",
.with_example({"To append 4 and 5 to the array [1, 2, 3]", "SELECT json_concat(NULL, 4)",
"SELECT json_concat('[1, 2, 3]', 4, 5)"}) })
.with_example( .with_example({
{"To concatenate two arrays together", "To append 4 and 5 to the array [1, 2, 3]",
"SELECT json_concat('[1, 2, 3]', json('[4, 5]'))"})), "SELECT json_concat('[1, 2, 3]', 4, 5)",
})
.with_example({
"To concatenate two arrays together",
"SELECT json_concat('[1, 2, 3]', json('[4, 5]'))",
})),
sqlite_func_adapter<decltype(&json_contains), json_contains>::builder( sqlite_func_adapter<decltype(&json_contains), json_contains>::builder(
help_text("json_contains", help_text("json_contains",
@ -814,10 +817,11 @@ json_extension_functions(struct FuncDef** basic_funcs,
.with_tags({"json"}) .with_tags({"json"})
.with_example({"To create an object from arguments", .with_example({"To create an object from arguments",
"SELECT json_group_object('a', 1, 'b', 2)"}) "SELECT json_group_object('a', 1, 'b', 2)"})
.with_example( .with_example({
{"To create an object from a pair of columns", "To create an object from a pair of columns",
"SELECT json_group_object(column1, column2) FROM " "SELECT json_group_object(column1, column2) FROM "
"(VALUES ('a', 1), ('b', 2))"}), "(VALUES ('a', 1), ('b', 2))",
}),
}, },
{ {
"json_group_array", "json_group_array",
@ -833,11 +837,15 @@ json_extension_functions(struct FuncDef** basic_funcs,
help_text("value", "The values to append to the array") help_text("value", "The values to append to the array")
.one_or_more()) .one_or_more())
.with_tags({"json"}) .with_tags({"json"})
.with_example({"To create an array from arguments", .with_example({
"SELECT json_group_array('one', 2, 3.4)"}) "To create an array from arguments",
.with_example({"To create an array from a column of values", "SELECT json_group_array('one', 2, 3.4)",
"SELECT json_group_array(column1) FROM (VALUES " })
"(1), (2), (3))"}), .with_example({
"To create an array from a column of values",
"SELECT json_group_array(column1) FROM (VALUES "
"(1), (2), (3))",
}),
}, },
{nullptr}, {nullptr},

@ -951,7 +951,7 @@ update_active_files(file_collection& new_files)
} }
for (const auto& other_pair : new_files.fc_other_files) { for (const auto& other_pair : new_files.fc_other_files) {
switch (other_pair.second.ofd_format) { switch (other_pair.second.ofd_format) {
case file_format_t::FF_SQLITE_DB: case file_format_t::SQLITE_DB:
attach_sqlite_db(lnav_data.ld_db.in(), other_pair.first); attach_sqlite_db(lnav_data.ld_db.in(), other_pair.first);
break; break;
default: default:
@ -989,7 +989,7 @@ rescan_files(bool req)
mlooper.get_port().process_for(delay); mlooper.get_port().process_for(delay);
if (lnav_data.ld_flags & LNF_HEADLESS) { if (lnav_data.ld_flags & LNF_HEADLESS) {
for (const auto& pair : lnav_data.ld_active_files.fc_other_files) { for (const auto& pair : lnav_data.ld_active_files.fc_other_files) {
if (pair.second.ofd_format != file_format_t::FF_REMOTE) { if (pair.second.ofd_format != file_format_t::REMOTE) {
continue; continue;
} }
@ -1910,7 +1910,7 @@ looper()
lnav_data.ld_active_files.fc_other_files.end(), lnav_data.ld_active_files.fc_other_files.end(),
[](const auto& pair) { [](const auto& pair) {
return pair.second.ofd_format return pair.second.ofd_format
== file_format_t::FF_SQLITE_DB; == file_format_t::SQLITE_DB;
})) }))
{ {
ensure_view(&lnav_data.ld_views[LNV_SCHEMA]); ensure_view(&lnav_data.ld_views[LNV_SCHEMA]);
@ -2132,7 +2132,7 @@ main(int argc, char* argv[])
} }
(void) signal(SIGPIPE, SIG_IGN); (void) signal(SIGPIPE, SIG_IGN);
setlocale(LC_ALL, ""); std::locale::global(std::locale(""));
umask(027); umask(027);
/* Disable Lnav from being able to execute external commands if /* Disable Lnav from being able to execute external commands if
@ -2779,12 +2779,9 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
.expect("Cannot create temporary file for FIFO") .expect("Cannot create temporary file for FIFO")
.second); .second);
auto fifo_out_fd = fifo_piper->get_fd(); auto fifo_out_fd = fifo_piper->get_fd();
char desc[128]; auto desc = fmt::format(FMT_STRING("FIFO [{}]"),
lnav_data.ld_fifo_counter++);
snprintf(desc,
sizeof(desc),
"FIFO [%d]",
lnav_data.ld_fifo_counter++);
lnav_data.ld_active_files.fc_file_names[desc].with_fd( lnav_data.ld_active_files.fc_file_names[desc].with_fd(
fifo_out_fd); fifo_out_fd);
lnav_data.ld_pipers.push_back(fifo_piper); lnav_data.ld_pipers.push_back(fifo_piper);

@ -50,6 +50,7 @@
#include "base/paths.hh" #include "base/paths.hh"
#include "base/string_util.hh" #include "base/string_util.hh"
#include "bound_tags.hh" #include "bound_tags.hh"
#include "fmt/printf.h"
#include "command_executor.hh" #include "command_executor.hh"
#include "config.h" #include "config.h"
#include "curl_looper.hh" #include "curl_looper.hh"
@ -705,9 +706,9 @@ csv_write_string(FILE* outfile, const string& str)
if (csv_needs_quoting(str)) { if (csv_needs_quoting(str)) {
string quoted_str = csv_quote_string(str); string quoted_str = csv_quote_string(str);
fprintf(outfile, "%s", quoted_str.c_str()); fmt::fprintf(outfile, "%s", quoted_str);
} else { } else {
fprintf(outfile, "%s", str.c_str()); fmt::fprintf(outfile, "%s", str);
} }
} }

@ -275,11 +275,11 @@ update_installs_from_git()
if (glob(git_formats.c_str(), GLOB_NOCHECK, nullptr, gl.inout()) == 0) { if (glob(git_formats.c_str(), GLOB_NOCHECK, nullptr, gl.inout()) == 0) {
for (int lpc = 0; lpc < (int) gl->gl_pathc; lpc++) { for (int lpc = 0; lpc < (int) gl->gl_pathc; lpc++) {
char* git_dir = dirname(gl->gl_pathv[lpc]); char* git_dir = dirname(gl->gl_pathv[lpc]);
char pull_cmd[1024];
printf("Updating formats in %s\n", git_dir); printf("Updating formats in %s\n", git_dir);
snprintf(pull_cmd, sizeof(pull_cmd), "cd %s && git pull", git_dir); auto pull_cmd
int ret = system(pull_cmd); = fmt::format(FMT_STRING("cd '{}' && git pull"), git_dir);
int ret = system(pull_cmd.c_str());
if (ret == -1) { if (ret == -1) {
std::cerr << "Failed to spawn command " std::cerr << "Failed to spawn command "
<< "\"" << pull_cmd << "\": " << strerror(errno) << "\"" << pull_cmd << "\": " << strerror(errno)
@ -314,7 +314,7 @@ read_repo_path(yajlpp_parse_context* ypc, const unsigned char* str, size_t len)
return 1; return 1;
} }
static struct json_path_container format_handlers static const struct json_path_container format_handlers
= {json_path_handler("format-repos#", read_repo_path)}; = {json_path_handler("format-repos#", read_repo_path)};
void void
@ -324,23 +324,17 @@ install_extra_formats()
auto_fd fd; auto_fd fd;
if (access(config_root.c_str(), R_OK) == 0) { if (access(config_root.c_str(), R_OK) == 0) {
char pull_cmd[1024];
printf("Updating lnav remote config repo...\n"); printf("Updating lnav remote config repo...\n");
snprintf(pull_cmd, auto pull_cmd = fmt::format(FMT_STRING("cd '{}' && git pull"),
sizeof(pull_cmd), config_root.string());
"cd '%s' && git pull", log_perror(system(pull_cmd.c_str()));
config_root.c_str());
log_perror(system(pull_cmd));
} else { } else {
char clone_cmd[1024];
printf("Cloning lnav remote config repo...\n"); printf("Cloning lnav remote config repo...\n");
snprintf(clone_cmd, auto clone_cmd = fmt::format(
sizeof(clone_cmd), FMT_STRING(
"git clone https://github.com/tstack/lnav-config.git %s", "git clone https://github.com/tstack/lnav-config.git {}"),
config_root.c_str()); config_root.string());
log_perror(system(clone_cmd)); log_perror(system(clone_cmd.c_str()));
} }
auto config_json = config_root / "remote-config.json"; auto config_json = config_root / "remote-config.json";
@ -392,7 +386,7 @@ config_error_reporter(const yajlpp_parse_context& ypc,
} }
} }
static struct json_path_container key_command_handlers = { static const struct json_path_container key_command_handlers = {
yajlpp::property_handler("command") yajlpp::property_handler("command")
.with_synopsis("<command>") .with_synopsis("<command>")
.with_description( .with_description(
@ -407,7 +401,7 @@ static struct json_path_container key_command_handlers = {
"The help message to display after the key is pressed.") "The help message to display after the key is pressed.")
.FOR_FIELD(key_command, kc_alt_msg)}; .FOR_FIELD(key_command, kc_alt_msg)};
static struct json_path_container keymap_def_handlers static const struct json_path_container keymap_def_handlers
= {yajlpp::pattern_property_handler("(?<key_seq>(?:x[0-9a-f]{2})+)") = {yajlpp::pattern_property_handler("(?<key_seq>(?:x[0-9a-f]{2})+)")
.with_synopsis("<utf8-key-code-in-hex>") .with_synopsis("<utf8-key-code-in-hex>")
.with_description( .with_description(
@ -431,7 +425,7 @@ static struct json_path_container keymap_def_handlers
}) })
.with_children(key_command_handlers)}; .with_children(key_command_handlers)};
static struct json_path_container keymap_defs_handlers static const struct json_path_container keymap_defs_handlers
= {yajlpp::pattern_property_handler("(?<keymap_name>[\\w\\-]+)") = {yajlpp::pattern_property_handler("(?<keymap_name>[\\w\\-]+)")
.with_description("The keymap definitions") .with_description("The keymap definitions")
.with_obj_provider<key_map, _lnav_config>( .with_obj_provider<key_map, _lnav_config>(
@ -449,7 +443,7 @@ static struct json_path_container keymap_defs_handlers
}) })
.with_children(keymap_def_handlers)}; .with_children(keymap_def_handlers)};
static struct json_path_container global_var_handlers = { static const struct json_path_container global_var_handlers = {
yajlpp::pattern_property_handler("(?<var_name>\\w+)") yajlpp::pattern_property_handler("(?<var_name>\\w+)")
.with_synopsis("<name>") .with_synopsis("<name>")
.with_description( .with_description(
@ -463,7 +457,7 @@ static struct json_path_container global_var_handlers = {
}) })
.FOR_FIELD(_lnav_config, lc_global_vars)}; .FOR_FIELD(_lnav_config, lc_global_vars)};
static struct json_path_container style_config_handlers = static const struct json_path_container style_config_handlers =
json_path_container{ json_path_container{
yajlpp::property_handler("color") yajlpp::property_handler("color")
.with_synopsis("#hex|color_name") .with_synopsis("#hex|color_name")
@ -493,7 +487,7 @@ static struct json_path_container style_config_handlers =
} }
.with_definition_id("style"); .with_definition_id("style");
static struct json_path_container theme_styles_handlers = { static const struct json_path_container theme_styles_handlers = {
yajlpp::property_handler("identifier") yajlpp::property_handler("identifier")
.with_description("Styling for identifiers in logs") .with_description("Styling for identifiers in logs")
.with_obj_provider<style_config, lnav_theme>( .with_obj_provider<style_config, lnav_theme>(
@ -601,7 +595,7 @@ static struct json_path_container theme_styles_handlers = {
}) })
.with_children(style_config_handlers)}; .with_children(style_config_handlers)};
static struct json_path_container theme_syntax_styles_handlers static const struct json_path_container theme_syntax_styles_handlers
= {yajlpp::property_handler("keyword") = {yajlpp::property_handler("keyword")
.with_description("Styling for keywords in source files") .with_description("Styling for keywords in source files")
.with_obj_provider<style_config, lnav_theme>( .with_obj_provider<style_config, lnav_theme>(
@ -697,7 +691,7 @@ static struct json_path_container theme_syntax_styles_handlers
}) })
.with_children(style_config_handlers)}; .with_children(style_config_handlers)};
static struct json_path_container theme_status_styles_handlers = { static const struct json_path_container theme_status_styles_handlers = {
yajlpp::property_handler("text") yajlpp::property_handler("text")
.with_description("Styling for status bars") .with_description("Styling for status bars")
.with_obj_provider<style_config, lnav_theme>( .with_obj_provider<style_config, lnav_theme>(
@ -777,7 +771,7 @@ static struct json_path_container theme_status_styles_handlers = {
.with_children(style_config_handlers), .with_children(style_config_handlers),
}; };
static struct json_path_container theme_log_level_styles_handlers static const struct json_path_container theme_log_level_styles_handlers
= {yajlpp::pattern_property_handler( = {yajlpp::pattern_property_handler(
"(?<level>trace|debug5|debug4|debug3|debug2|debug|info|stats|notice|" "(?<level>trace|debug5|debug4|debug3|debug2|debug|info|stats|notice|"
"warning|error|critical|fatal|invalid)") "warning|error|critical|fatal|invalid)")
@ -796,7 +790,7 @@ static struct json_path_container theme_log_level_styles_handlers
}) })
.with_children(style_config_handlers)}; .with_children(style_config_handlers)};
static struct json_path_container highlighter_handlers = { static const struct json_path_container highlighter_handlers = {
yajlpp::property_handler("pattern") yajlpp::property_handler("pattern")
.with_synopsis("regular expression") .with_synopsis("regular expression")
.with_description("The regular expression to highlight") .with_description("The regular expression to highlight")
@ -812,7 +806,7 @@ static struct json_path_container highlighter_handlers = {
.with_children(style_config_handlers), .with_children(style_config_handlers),
}; };
static struct json_path_container theme_highlights_handlers static const struct json_path_container theme_highlights_handlers
= {yajlpp::pattern_property_handler("(?<highlight_name>\\w+)") = {yajlpp::pattern_property_handler("(?<highlight_name>\\w+)")
.with_obj_provider<highlighter_config, lnav_theme>( .with_obj_provider<highlighter_config, lnav_theme>(
[](const yajlpp_provider_context& ypc, lnav_theme* root) { [](const yajlpp_provider_context& ypc, lnav_theme* root) {
@ -831,7 +825,7 @@ static struct json_path_container theme_highlights_handlers
}) })
.with_children(highlighter_handlers)}; .with_children(highlighter_handlers)};
static struct json_path_container theme_vars_handlers static const struct json_path_container theme_vars_handlers
= {yajlpp::pattern_property_handler("(?<var_name>\\w+)") = {yajlpp::pattern_property_handler("(?<var_name>\\w+)")
.with_synopsis("name") .with_synopsis("name")
.with_description("A theme variable definition") .with_description("A theme variable definition")
@ -843,7 +837,7 @@ static struct json_path_container theme_vars_handlers
}) })
.FOR_FIELD(lnav_theme, lt_vars)}; .FOR_FIELD(lnav_theme, lt_vars)};
static struct json_path_container theme_def_handlers = { static const struct json_path_container theme_def_handlers = {
yajlpp::property_handler("vars") yajlpp::property_handler("vars")
.with_description("Variables definitions that are used in this theme.") .with_description("Variables definitions that are used in this theme.")
.with_children(theme_vars_handlers), .with_children(theme_vars_handlers),
@ -869,7 +863,7 @@ static struct json_path_container theme_def_handlers = {
.with_children(theme_highlights_handlers), .with_children(theme_highlights_handlers),
}; };
static struct json_path_container theme_defs_handlers static const struct json_path_container theme_defs_handlers
= {yajlpp::pattern_property_handler("(?<theme_name>[\\w\\-]+)") = {yajlpp::pattern_property_handler("(?<theme_name>[\\w\\-]+)")
.with_description("Theme definitions") .with_description("Theme definitions")
.with_obj_provider<lnav_theme, _lnav_config>( .with_obj_provider<lnav_theme, _lnav_config>(
@ -888,7 +882,7 @@ static struct json_path_container theme_defs_handlers
}) })
.with_children(theme_def_handlers)}; .with_children(theme_def_handlers)};
static struct json_path_container ui_handlers = { static const struct json_path_container ui_handlers = {
yajlpp::property_handler("clock-format") yajlpp::property_handler("clock-format")
.with_synopsis("format") .with_synopsis("format")
.with_description("The format for the clock displayed in " .with_description("The format for the clock displayed in "
@ -925,7 +919,7 @@ static struct json_path_container ui_handlers = {
.with_children(keymap_defs_handlers), .with_children(keymap_defs_handlers),
}; };
static struct json_path_container archive_handlers = { static const struct json_path_container archive_handlers = {
yajlpp::property_handler("min-free-space") yajlpp::property_handler("min-free-space")
.with_synopsis("<bytes>") .with_synopsis("<bytes>")
.with_description( .with_description(
@ -945,7 +939,7 @@ static struct json_path_container archive_handlers = {
&archive_manager::config::amc_cache_ttl), &archive_manager::config::amc_cache_ttl),
}; };
static struct json_path_container file_vtab_handlers = { static const struct json_path_container file_vtab_handlers = {
yajlpp::property_handler("max-content-size") yajlpp::property_handler("max-content-size")
.with_synopsis("<bytes>") .with_synopsis("<bytes>")
.with_description( .with_description(
@ -955,7 +949,7 @@ static struct json_path_container file_vtab_handlers = {
&file_vtab::config::fvc_max_content_size), &file_vtab::config::fvc_max_content_size),
}; };
static struct json_path_container logfile_handlers = { static const struct json_path_container logfile_handlers = {
yajlpp::property_handler("max-unrecognized-lines") yajlpp::property_handler("max-unrecognized-lines")
.with_synopsis("<lines>") .with_synopsis("<lines>")
.with_description("The maximum number of lines in a file to use when " .with_description("The maximum number of lines in a file to use when "
@ -965,7 +959,7 @@ static struct json_path_container logfile_handlers = {
&lnav::logfile::config::lc_max_unrecognized_lines), &lnav::logfile::config::lc_max_unrecognized_lines),
}; };
static struct json_path_container ssh_config_handlers = { static const struct json_path_container ssh_config_handlers = {
yajlpp::pattern_property_handler("(?<config_name>\\w+)") yajlpp::pattern_property_handler("(?<config_name>\\w+)")
.with_synopsis("name") .with_synopsis("name")
.with_description("Set an SSH configuration value") .with_description("Set an SSH configuration value")
@ -978,14 +972,14 @@ static struct json_path_container ssh_config_handlers = {
.for_field(&_lnav_config::lc_tailer, &tailer::config::c_ssh_config), .for_field(&_lnav_config::lc_tailer, &tailer::config::c_ssh_config),
}; };
static struct json_path_container ssh_option_handlers = { static const struct json_path_container ssh_option_handlers = {
yajlpp::pattern_property_handler("(?<option_name>\\w+)") yajlpp::pattern_property_handler("(?<option_name>\\w+)")
.with_synopsis("name") .with_synopsis("name")
.with_description("Set an option to be passed to the SSH command") .with_description("Set an option to be passed to the SSH command")
.for_field(&_lnav_config::lc_tailer, &tailer::config::c_ssh_options), .for_field(&_lnav_config::lc_tailer, &tailer::config::c_ssh_options),
}; };
static struct json_path_container ssh_handlers = { static const struct json_path_container ssh_handlers = {
yajlpp::property_handler("command") yajlpp::property_handler("command")
.with_synopsis("ssh-command") .with_synopsis("ssh-command")
.with_description("The SSH command to execute") .with_description("The SSH command to execute")
@ -1012,7 +1006,7 @@ static struct json_path_container ssh_handlers = {
.with_children(ssh_config_handlers), .with_children(ssh_config_handlers),
}; };
static struct json_path_container remote_handlers = { static const struct json_path_container remote_handlers = {
yajlpp::property_handler("cache-ttl") yajlpp::property_handler("cache-ttl")
.with_synopsis("<duration>") .with_synopsis("<duration>")
.with_description("The time-to-live for files copied from remote " .with_description("The time-to-live for files copied from remote "
@ -1028,7 +1022,7 @@ static struct json_path_container remote_handlers = {
.with_children(ssh_handlers), .with_children(ssh_handlers),
}; };
static struct json_path_container sysclip_impl_cmd_handlers = json_path_container{ static const struct json_path_container sysclip_impl_cmd_handlers = json_path_container{
yajlpp::property_handler("write") yajlpp::property_handler("write")
.with_synopsis("<command>") .with_synopsis("<command>")
.with_description("The command used to write to the clipboard") .with_description("The command used to write to the clipboard")
@ -1043,7 +1037,7 @@ static struct json_path_container sysclip_impl_cmd_handlers = json_path_containe
.with_description("Container for the commands used to read from and write to the system clipboard") .with_description("Container for the commands used to read from and write to the system clipboard")
.with_definition_id("clip-commands"); .with_definition_id("clip-commands");
static struct json_path_container sysclip_impl_handlers = { static const struct json_path_container sysclip_impl_handlers = {
yajlpp::property_handler("test") yajlpp::property_handler("test")
.with_synopsis("<command>") .with_synopsis("<command>")
.with_description("The command that checks") .with_description("The command that checks")
@ -1065,7 +1059,7 @@ static struct json_path_container sysclip_impl_handlers = {
.with_children(sysclip_impl_cmd_handlers), .with_children(sysclip_impl_cmd_handlers),
}; };
static struct json_path_container sysclip_impls_handlers = { static const struct json_path_container sysclip_impls_handlers = {
yajlpp::pattern_property_handler("(?<clipboard_impl_name>[\\w\\-]+)") yajlpp::pattern_property_handler("(?<clipboard_impl_name>[\\w\\-]+)")
.with_synopsis("<name>") .with_synopsis("<name>")
.with_description("Clipboard implementation") .with_description("Clipboard implementation")
@ -1086,13 +1080,13 @@ static struct json_path_container sysclip_impls_handlers = {
.with_children(sysclip_impl_handlers), .with_children(sysclip_impl_handlers),
}; };
static struct json_path_container sysclip_handlers = { static const struct json_path_container sysclip_handlers = {
yajlpp::property_handler("impls") yajlpp::property_handler("impls")
.with_description("Clipboard implementations") .with_description("Clipboard implementations")
.with_children(sysclip_impls_handlers), .with_children(sysclip_impls_handlers),
}; };
static struct json_path_container tuning_handlers = { static const struct json_path_container tuning_handlers = {
yajlpp::property_handler("archive-manager") yajlpp::property_handler("archive-manager")
.with_description("Settings related to opening archive files") .with_description("Settings related to opening archive files")
.with_children(archive_handlers), .with_children(archive_handlers),
@ -1110,14 +1104,14 @@ static struct json_path_container tuning_handlers = {
.with_children(sysclip_handlers), .with_children(sysclip_handlers),
}; };
static set<string> SUPPORTED_CONFIG_SCHEMAS = { static const set<string> SUPPORTED_CONFIG_SCHEMAS = {
"https://lnav.org/schemas/config-v1.schema.json", "https://lnav.org/schemas/config-v1.schema.json",
}; };
const char* DEFAULT_FORMAT_SCHEMA const char* DEFAULT_FORMAT_SCHEMA
= "https://lnav.org/schemas/format-v1.schema.json"; = "https://lnav.org/schemas/format-v1.schema.json";
set<string> SUPPORTED_FORMAT_SCHEMAS = { const set<string> SUPPORTED_FORMAT_SCHEMAS = {
DEFAULT_FORMAT_SCHEMA, DEFAULT_FORMAT_SCHEMA,
}; };
@ -1139,25 +1133,24 @@ read_id(yajlpp_parse_context* ypc, const unsigned char* str, size_t len)
return 1; return 1;
} }
struct json_path_container lnav_config_handlers const auto lnav_config_handlers = json_path_container {
= json_path_container{json_path_handler("$schema", read_id) json_path_handler("$schema", read_id)
.with_synopsis( .with_synopsis("The URI of the schema for this file")
"The URI of the schema for this file") .with_description("Specifies the type of this file"),
.with_description(
"Specifies the type of this file"),
yajlpp::property_handler("tuning") yajlpp::property_handler("tuning")
.with_description("Internal settings") .with_description("Internal settings")
.with_children(tuning_handlers), .with_children(tuning_handlers),
yajlpp::property_handler("ui") yajlpp::property_handler("ui")
.with_description("User-interface settings") .with_description("User-interface settings")
.with_children(ui_handlers), .with_children(ui_handlers),
yajlpp::property_handler("global") yajlpp::property_handler("global")
.with_description("Global variable definitions") .with_description("Global variable definitions")
.with_children(global_var_handlers)} .with_children(global_var_handlers),
.with_schema_id(*SUPPORTED_CONFIG_SCHEMAS.cbegin()); }
.with_schema_id(*SUPPORTED_CONFIG_SCHEMAS.cbegin());
class active_key_map_listener : public lnav_config_listener { class active_key_map_listener : public lnav_config_listener {
public: public:
@ -1234,13 +1227,9 @@ load_config_from(_lnav_config& lconfig,
ypc.with_error_reporter(config_error_reporter); ypc.with_error_reporter(config_error_reporter);
if ((fd = lnav::filesystem::openp(path, O_RDONLY)) == -1) { if ((fd = lnav::filesystem::openp(path, O_RDONLY)) == -1) {
if (errno != ENOENT) { if (errno != ENOENT) {
char errmsg[1024]; errors.emplace_back(fmt::format(
FMT_STRING("error: unable to open format file -- {}"),
snprintf(errmsg, path.string()));
sizeof(errmsg),
"error: unable to open format file -- %s",
path.c_str());
errors.emplace_back(errmsg);
} }
} else { } else {
auto_mem<yajl_handle_t> handle(yajl_free); auto_mem<yajl_handle_t> handle(yajl_free);
@ -1257,8 +1246,10 @@ load_config_from(_lnav_config& lconfig,
if (rc == 0) { if (rc == 0) {
break; break;
} else if (rc == -1) { } else if (rc == -1) {
errors.push_back(path.string() + ":unable to read file -- " errors.emplace_back(
+ string(strerror(errno))); fmt::format(FMT_STRING("{}:unable to read file -- {}"),
path.string(),
strerror(errno)));
break; break;
} }
if (ypc.parse((const unsigned char*) buffer, rc) != yajl_status_ok) if (ypc.parse((const unsigned char*) buffer, rc) != yajl_status_ok)
@ -1446,19 +1437,13 @@ reload_config(vector<string>& errors)
return; return;
} }
char msg[1024]; errors.emplace_back(fmt::format(FMT_STRING("{}:{}:{}"),
loc_iter->second.sl_source,
snprintf(msg, loc_iter->second.sl_line_number,
sizeof(msg), errmsg));
"%s:%d:%s",
loc_iter->second.sl_source.get(),
loc_iter->second.sl_line_number,
errmsg.c_str());
errors.emplace_back(msg);
}; };
for (auto& jph : lnav_config_handlers.jpc_children) { for (const auto& jph : lnav_config_handlers.jpc_children) {
jph.walk(cb, &lnav_config); jph.walk(cb, &lnav_config);
} }
}; };

@ -106,7 +106,7 @@ extern struct _lnav_config lnav_config;
extern struct _lnav_config rollback_lnav_config; extern struct _lnav_config rollback_lnav_config;
extern std::map<intern_string_t, source_location> lnav_config_locations; extern std::map<intern_string_t, source_location> lnav_config_locations;
extern struct json_path_container lnav_config_handlers; extern const struct json_path_container lnav_config_handlers;
enum class config_file_type { enum class config_file_type {
FORMAT, FORMAT,
@ -126,6 +126,6 @@ void reload_config(std::vector<std::string>& errors);
std::string save_config(); std::string save_config();
extern const char* DEFAULT_FORMAT_SCHEMA; extern const char* DEFAULT_FORMAT_SCHEMA;
extern std::set<std::string> SUPPORTED_FORMAT_SCHEMAS; extern const std::set<std::string> SUPPORTED_FORMAT_SCHEMAS;
#endif #endif

@ -42,8 +42,6 @@
#include "fmt/format.h" #include "fmt/format.h"
#include "view_curses.hh" #include "view_curses.hh"
using namespace std;
bool bool
change_to_parent_dir() change_to_parent_dir()
{ {

@ -2354,7 +2354,7 @@ external_log_format::match_name(const string& filename)
bool bool
external_log_format::match_mime_type(const file_format_t ff) const external_log_format::match_mime_type(const file_format_t ff) const
{ {
if (ff == file_format_t::FF_UNKNOWN && this->elf_mime_types.empty()) { if (ff == file_format_t::UNKNOWN && this->elf_mime_types.empty()) {
return true; return true;
} }

@ -363,7 +363,7 @@ public:
virtual bool match_mime_type(const file_format_t ff) const virtual bool match_mime_type(const file_format_t ff) const
{ {
if (ff == file_format_t::FF_UNKNOWN) { if (ff == file_format_t::UNKNOWN) {
return true; return true;
} }
return false; return false;

@ -47,7 +47,7 @@
using namespace std; using namespace std;
static pcrepp RDNS_PATTERN( static const pcrepp RDNS_PATTERN(
"^(?:com|net|org|edu|[a-z][a-z])" "^(?:com|net|org|edu|[a-z][a-z])"
"(\\.\\w+)+(.+)"); "(\\.\\w+)+(.+)");
@ -125,23 +125,24 @@ class generic_log_format : public log_format {
pcre_format("^(?:\\*\\*\\*\\s+)?\\[(?<timestamp>[\\w: ,+/-]+)\\] " pcre_format("^(?:\\*\\*\\*\\s+)?\\[(?<timestamp>[\\w: ,+/-]+)\\] "
"\\(\\d+\\) (.*)"), "\\(\\d+\\) (.*)"),
pcre_format()}; pcre_format(),
};
return log_fmt; return log_fmt;
}; };
std::string get_pattern_regex(uint64_t line_number) const std::string get_pattern_regex(uint64_t line_number) const override
{ {
int pat_index = this->pattern_index_for_line(line_number); int pat_index = this->pattern_index_for_line(line_number);
return get_pcre_log_formats()[pat_index].name; return get_pcre_log_formats()[pat_index].name;
} }
const intern_string_t get_name() const const intern_string_t get_name() const override
{ {
return intern_string::lookup("generic_log"); return intern_string::lookup("generic_log");
}; };
void scrub(string& line) void scrub(string& line) override
{ {
pcre_context_static<30> context; pcre_context_static<30> context;
pcre_input pi(line); pcre_input pi(line);
@ -161,7 +162,7 @@ class generic_log_format : public log_format {
scan_result_t scan(logfile& lf, scan_result_t scan(logfile& lf,
vector<logline>& dst, vector<logline>& dst,
const line_info& li, const line_info& li,
shared_buffer_ref& sbr) shared_buffer_ref& sbr) override
{ {
struct exttm log_time; struct exttm log_time;
struct timeval log_tv; struct timeval log_tv;
@ -201,7 +202,7 @@ class generic_log_format : public log_format {
shared_buffer_ref& line, shared_buffer_ref& line,
string_attrs_t& sa, string_attrs_t& sa,
std::vector<logline_value>& values, std::vector<logline_value>& values,
bool annotate_module) const bool annotate_module) const override
{ {
int pat_index = this->pattern_index_for_line(line_number); int pat_index = this->pattern_index_for_line(line_number);
pcre_format& fmt = get_pcre_log_formats()[pat_index]; pcre_format& fmt = get_pcre_log_formats()[pat_index];
@ -235,7 +236,7 @@ class generic_log_format : public log_format {
sa.emplace_back(lr, &SA_BODY); sa.emplace_back(lr, &SA_BODY);
}; };
shared_ptr<log_format> specialized(int fmt_lock) shared_ptr<log_format> specialized(int fmt_lock) override
{ {
return std::make_shared<generic_log_format>(*this); return std::make_shared<generic_log_format>(*this);
}; };
@ -299,17 +300,17 @@ struct separated_string {
size_t ss_separator_len; size_t ss_separator_len;
separated_string(const char* str, size_t len) separated_string(const char* str, size_t len)
: ss_str(str), ss_len(len), ss_separator(",") : ss_str(str), ss_len(len), ss_separator(","),
ss_separator_len(strlen(this->ss_separator))
{ {
this->ss_separator_len = strlen(this->ss_separator); }
};
separated_string& with_separator(const char* sep) separated_string& with_separator(const char* sep)
{ {
this->ss_separator = sep; this->ss_separator = sep;
this->ss_separator_len = strlen(sep); this->ss_separator_len = strlen(sep);
return *this; return *this;
}; }
struct iterator { struct iterator {
const separated_string& i_parent; const separated_string& i_parent;
@ -379,12 +380,12 @@ struct separated_string {
iterator begin() iterator begin()
{ {
return {*this, this->ss_str}; return {*this, this->ss_str};
}; }
iterator end() iterator end()
{ {
return {*this, this->ss_str + this->ss_len}; return {*this, this->ss_str + this->ss_len};
}; }
}; };
class bro_log_format : public log_format { class bro_log_format : public log_format {
@ -423,14 +424,14 @@ public:
this->lf_time_ordered = false; this->lf_time_ordered = false;
}; };
const intern_string_t get_name() const const intern_string_t get_name() const override
{ {
static const intern_string_t name(intern_string::lookup("bro")); static const intern_string_t name(intern_string::lookup("bro"));
return this->blf_format_name.empty() ? name : this->blf_format_name; return this->blf_format_name.empty() ? name : this->blf_format_name;
}; };
virtual void clear() void clear() override
{ {
this->log_format::clear(); this->log_format::clear();
this->blf_format_name.clear(); this->blf_format_name.clear();
@ -518,9 +519,9 @@ public:
scan_result_t scan(logfile& lf, scan_result_t scan(logfile& lf,
std::vector<logline>& dst, std::vector<logline>& dst,
const line_info& li, const line_info& li,
shared_buffer_ref& sbr) shared_buffer_ref& sbr) override
{ {
static pcrepp SEP_RE(R"(^#separator\s+(.+))"); static const pcrepp SEP_RE(R"(^#separator\s+(.+))");
if (!this->blf_format_name.empty()) { if (!this->blf_format_name.empty()) {
return this->scan_int(dst, li, sbr); return this->scan_int(dst, li, sbr);
@ -583,10 +584,7 @@ public:
} else if (directive == "#unset_field") { } else if (directive == "#unset_field") {
this->blf_unset_field = intern_string::lookup(*iter); this->blf_unset_field = intern_string::lookup(*iter);
} else if (directive == "#path") { } else if (directive == "#path") {
string path = to_string(*iter); auto full_name = fmt::format(FMT_STRING("bro_{}_log"), *iter);
char full_name[128];
snprintf(
full_name, sizeof(full_name), "bro_%s_log", path.c_str());
this->blf_format_name = intern_string::lookup(full_name); this->blf_format_name = intern_string::lookup(full_name);
} else if (directive == "#fields") { } else if (directive == "#fields") {
do { do {
@ -674,7 +672,7 @@ public:
shared_buffer_ref& sbr, shared_buffer_ref& sbr,
string_attrs_t& sa, string_attrs_t& sa,
std::vector<logline_value>& values, std::vector<logline_value>& values,
bool annotate_module) const bool annotate_module) const override
{ {
static const intern_string_t TS = intern_string::lookup("bro_ts"); static const intern_string_t TS = intern_string::lookup("bro_ts");
static const intern_string_t UID = intern_string::lookup("bro_uid"); static const intern_string_t UID = intern_string::lookup("bro_uid");
@ -714,17 +712,16 @@ public:
}; };
const logline_value_stats* stats_for_value( const logline_value_stats* stats_for_value(
const intern_string_t& name) const const intern_string_t& name) const override
{ {
const logline_value_stats* retval = nullptr; const logline_value_stats* retval = nullptr;
for (size_t lpc = 0; lpc < this->blf_field_defs.size(); lpc++) { for (const auto& blf_field_def : this->blf_field_defs) {
if (this->blf_field_defs[lpc].fd_meta.lvm_name == name) { if (blf_field_def.fd_meta.lvm_name == name) {
if (this->blf_field_defs[lpc].fd_numeric_index < 0) { if (blf_field_def.fd_numeric_index < 0) {
break; break;
} }
retval = &this->lf_value_stats[this->blf_field_defs[lpc] retval = &this->lf_value_stats[blf_field_def.fd_numeric_index];
.fd_numeric_index];
break; break;
} }
} }
@ -732,7 +729,7 @@ public:
return retval; return retval;
}; };
std::shared_ptr<log_format> specialized(int fmt_lock = -1) std::shared_ptr<log_format> specialized(int fmt_lock = -1) override
{ {
return make_shared<bro_log_format>(*this); return make_shared<bro_log_format>(*this);
}; };
@ -782,7 +779,7 @@ public:
return retval; return retval;
}; };
std::shared_ptr<log_vtab_impl> get_vtab_impl() const std::shared_ptr<log_vtab_impl> get_vtab_impl() const override
{ {
if (this->blf_format_name.empty()) { if (this->blf_format_name.empty()) {
return nullptr; return nullptr;
@ -802,7 +799,7 @@ public:
void get_subline(const logline& ll, void get_subline(const logline& ll,
shared_buffer_ref& sbr, shared_buffer_ref& sbr,
bool full_message) bool full_message) override
{ {
} }
@ -1129,8 +1126,8 @@ public:
const line_info& li, const line_info& li,
shared_buffer_ref& sbr) override shared_buffer_ref& sbr) override
{ {
static auto W3C_LOG_NAME = intern_string::lookup("w3c_log"); static const auto* W3C_LOG_NAME = intern_string::lookup("w3c_log");
static auto X_FIELDS_NAME = intern_string::lookup("x_fields"); static const auto* X_FIELDS_NAME = intern_string::lookup("x_fields");
static auto X_FIELDS_IDX = 0; static auto X_FIELDS_IDX = 0;
if (!this->wlf_format_name.empty()) { if (!this->wlf_format_name.empty()) {

@ -704,7 +704,7 @@ static struct json_path_container search_table_handlers
static const json_path_handler_base::enum_value_t MIME_TYPE_ENUM[] static const json_path_handler_base::enum_value_t MIME_TYPE_ENUM[]
= {{ = {{
"application/vnd.tcpdump.pcap", "application/vnd.tcpdump.pcap",
file_format_t::FF_PCAP, file_format_t::PCAP,
}, },
json_path_handler_base::ENUM_TERMINATOR}; json_path_handler_base::ENUM_TERMINATOR};
@ -885,14 +885,11 @@ write_sample_file()
struct script_metadata meta; struct script_metadata meta;
auto sf = bsf.to_string_fragment(); auto sf = bsf.to_string_fragment();
auto_fd script_fd; auto_fd script_fd;
char path[2048];
struct stat st; struct stat st;
extract_metadata(sf.data(), sf.length(), meta); extract_metadata(sf.data(), sf.length(), meta);
snprintf(path, auto path
sizeof(path), = fmt::format(FMT_STRING("formats/default/{}.lnav"), meta.sm_name);
"formats/default/%s.lnav",
meta.sm_name.c_str());
auto script_path = lnav::paths::dotlnav() / path; auto script_path = lnav::paths::dotlnav() / path;
if (lnav::filesystem::statp(script_path, &st) == 0 if (lnav::filesystem::statp(script_path, &st) == 0
&& st.st_size == sf.length()) { && st.st_size == sf.length()) {

@ -132,7 +132,7 @@ struct logfile_open_options {
bool loo_non_utf_is_visible{true}; bool loo_non_utf_is_visible{true};
ssize_t loo_visible_size_limit{-1}; ssize_t loo_visible_size_limit{-1};
bool loo_tail{true}; bool loo_tail{true};
file_format_t loo_file_format{file_format_t::FF_UNKNOWN}; file_format_t loo_file_format{file_format_t::UNKNOWN};
}; };
#endif #endif

@ -584,8 +584,8 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
auto sql_filter_opt = this->get_sql_filter(); auto sql_filter_opt = this->get_sql_filter();
if (sql_filter_opt) { if (sql_filter_opt) {
auto sf = (sql_filter*) sql_filter_opt.value().get(); auto* sf = (sql_filter*) sql_filter_opt.value().get();
int color; log_debug("eval sql %p %p", &this->tss_filters, sf);
auto eval_res = this->eval_sql_filter(sf->sf_filter_stmt.in(), auto eval_res = this->eval_sql_filter(sf->sf_filter_stmt.in(),
this->lss_token_file_data, this->lss_token_file_data,
this->lss_token_line); this->lss_token_line);
@ -593,7 +593,7 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
auto msg = fmt::format( auto msg = fmt::format(
"filter expression evaluation failed with -- {}", "filter expression evaluation failed with -- {}",
eval_res.unwrapErr()); eval_res.unwrapErr());
color = COLOR_YELLOW; auto color = COLOR_YELLOW;
value_out.emplace_back(line_range{0, -1}, &SA_ERROR, msg); value_out.emplace_back(line_range{0, -1}, &SA_ERROR, msg);
value_out.emplace_back( value_out.emplace_back(
line_range{0, 1}, &view_curses::VC_BACKGROUND, color); line_range{0, 1}, &view_curses::VC_BACKGROUND, color);
@ -1101,7 +1101,7 @@ logfile_sub_source::text_filters_changed()
} }
for (auto& ld : *this) { for (auto& ld : *this) {
auto lf = ld->get_file_ptr(); auto* lf = ld->get_file_ptr();
if (lf != nullptr) { if (lf != nullptr) {
ld->ld_filter_state.clear_deleted_filter_state(); ld->ld_filter_state.clear_deleted_filter_state();
@ -1237,6 +1237,9 @@ logfile_sub_source::insert_file(const shared_ptr<logfile>& lf)
Result<void, std::string> Result<void, std::string>
logfile_sub_source::set_sql_filter(std::string stmt_str, sqlite3_stmt* stmt) logfile_sub_source::set_sql_filter(std::string stmt_str, sqlite3_stmt* stmt)
{ {
for (auto& filt : this->tss_filters) {
log_debug("set filt %p %d", filt.get(), filt->lf_deleted);
}
if (stmt != nullptr && !this->lss_filtered_index.empty()) { if (stmt != nullptr && !this->lss_filtered_index.empty()) {
auto top_cl = this->at(0_vl); auto top_cl = this->at(0_vl);
auto ld = this->find_data(top_cl); auto ld = this->find_data(top_cl);
@ -1255,8 +1258,10 @@ logfile_sub_source::set_sql_filter(std::string stmt_str, sqlite3_stmt* stmt)
auto old_filter = this->get_sql_filter(); auto old_filter = this->get_sql_filter();
if (stmt != nullptr) { if (stmt != nullptr) {
auto new_filter = std::make_shared<sql_filter>(*this, stmt_str, stmt); auto new_filter
= std::make_shared<sql_filter>(*this, std::move(stmt_str), stmt);
log_debug("fstack %p new %p", &this->tss_filters, new_filter.get());
if (old_filter) { if (old_filter) {
auto existing_iter = std::find(this->tss_filters.begin(), auto existing_iter = std::find(this->tss_filters.begin(),
this->tss_filters.end(), this->tss_filters.end(),

@ -425,7 +425,7 @@ public:
{ {
int retval = 0; int retval = 0;
for (auto& ld : this->lss_files) { for (const auto& ld : this->lss_files) {
retval += ld->ld_filter_state.lfo_filter_state retval += ld->ld_filter_state.lfo_filter_state
.tfs_filter_hits[filter_index]; .tfs_filter_hits[filter_index];
} }
@ -451,6 +451,8 @@ public:
return ""; return "";
} }
nonstd::optional<std::shared_ptr<text_filter>> get_sql_filter();
std::string get_sql_marker_text() const std::string get_sql_marker_text() const
{ {
return this->lss_marker_stmt_text; return this->lss_marker_stmt_text;
@ -588,9 +590,9 @@ public:
bool ld_visible; bool ld_visible;
}; };
typedef std::vector<std::unique_ptr<logfile_data>>::iterator iterator; using iterator = std::vector<std::unique_ptr<logfile_data>>::iterator;
typedef std::vector<std::unique_ptr<logfile_data>>::const_iterator using const_iterator
const_iterator; = std::vector<std::unique_ptr<logfile_data>>::const_iterator;
iterator begin() iterator begin()
{ {
@ -868,8 +870,6 @@ private:
this->lss_line_size_cache[0].first = -1; this->lss_line_size_cache[0].first = -1;
}; };
nonstd::optional<std::shared_ptr<text_filter>> get_sql_filter();
bool check_extra_filters(iterator ld, logfile::iterator ll); bool check_extra_filters(iterator ld, logfile::iterator ll);
size_t lss_basename_width = 0; size_t lss_basename_width = 0;

@ -129,20 +129,15 @@ rl_set_help()
case LNM_SQL: { case LNM_SQL: {
textview_curses& log_view = lnav_data.ld_views[LNV_LOG]; textview_curses& log_view = lnav_data.ld_views[LNV_LOG];
auto lss = (logfile_sub_source*) log_view.get_sub_source(); auto lss = (logfile_sub_source*) log_view.get_sub_source();
char example_txt[1024];
attr_line_t example_al; attr_line_t example_al;
if (log_view.get_inner_height() > 0) { if (log_view.get_inner_height() > 0) {
auto cl = lss->at(log_view.get_top()); auto cl = lss->at(log_view.get_top());
auto lf = lss->find(cl); auto lf = lss->find(cl);
auto format_name = lf->get_format()->get_name().get(); const auto* format_name = lf->get_format()->get_name().get();
snprintf(example_txt, example_al.with_ansi_string(
sizeof(example_txt), SQL_EXAMPLE, format_name, format_name);
SQL_EXAMPLE,
format_name,
format_name);
example_al.with_ansi_string(example_txt);
readline_sqlite_highlighter(example_al, 0); readline_sqlite_highlighter(example_al, 0);
} }

@ -354,7 +354,6 @@ nonstd::optional<session_pair_t>
scan_sessions() scan_sessions()
{ {
static_root_mem<glob_t, globfree> view_info_list; static_root_mem<glob_t, globfree> view_info_list;
char view_info_pattern_base[128];
cleanup_session_data(); cleanup_session_data();
@ -367,10 +366,8 @@ scan_sessions()
session_file_names.clear(); session_file_names.clear();
snprintf(view_info_pattern_base, auto view_info_pattern_base
sizeof(view_info_pattern_base), = fmt::format(FMT_STRING("view-info-{}.*.json"), session_id.value());
"view-info-%s.*.json",
session_id.value().c_str());
auto view_info_pattern = lnav::paths::dotlnav() / view_info_pattern_base; auto view_info_pattern = lnav::paths::dotlnav() / view_info_pattern_base;
if (glob(view_info_pattern.c_str(), 0, nullptr, view_info_list.inout()) if (glob(view_info_pattern.c_str(), 0, nullptr, view_info_list.inout())
== 0) { == 0) {
@ -1424,7 +1421,7 @@ save_time_bookmarks()
} }
static void static void
save_session_with_id(const std::string session_id) save_session_with_id(const std::string& session_id)
{ {
auto_mem<FILE> file(fclose); auto_mem<FILE> file(fclose);
yajl_gen handle = nullptr; yajl_gen handle = nullptr;
@ -1433,14 +1430,11 @@ save_session_with_id(const std::string session_id)
log_info("saving session with id: %s", session_id.c_str()); log_info("saving session with id: %s", session_id.c_str());
char view_base_name[256]; auto view_base_name
snprintf(view_base_name, = fmt::format(FMT_STRING("view-info-{}.ts{}.ppid{}.json"),
sizeof(view_base_name), session_id,
"view-info-%s.ts%ld.ppid%d.json", lnav_data.ld_session_time,
session_id.c_str(), getppid());
lnav_data.ld_session_time,
getppid());
auto view_file_name = lnav::paths::dotlnav() / view_base_name; auto view_file_name = lnav::paths::dotlnav() / view_base_name;
auto view_file_tmp_name = view_file_name.string() + ".tmp"; auto view_file_tmp_name = view_file_name.string() + ".tmp";

@ -31,6 +31,7 @@
#define lnav_tailerpp_hh #define lnav_tailerpp_hh
#include <string> #include <string>
#include <vector>
#include "auto_mem.hh" #include "auto_mem.hh"
#include "base/result.h" #include "base/result.h"

@ -39,23 +39,23 @@ text_format_t
detect_text_format(const char* str, size_t len) detect_text_format(const char* str, size_t len)
{ {
// XXX This is a pretty crude way of detecting format... // XXX This is a pretty crude way of detecting format...
static pcrepp PYTHON_MATCHERS = pcrepp( static const pcrepp PYTHON_MATCHERS = pcrepp(
"(?:" "(?:"
"^\\s*def\\s+\\w+\\([^)]*\\):[^\\n]*$|" "^\\s*def\\s+\\w+\\([^)]*\\):[^\\n]*$|"
"^\\s*try:[^\\n]*$" "^\\s*try:[^\\n]*$"
")", ")",
PCRE_MULTILINE); PCRE_MULTILINE);
static pcrepp RUST_MATCHERS = pcrepp(R"( static const pcrepp RUST_MATCHERS = pcrepp(R"(
(?: (?:
^\s*use\s+[\w+:\{\}]+;$| ^\s*use\s+[\w+:\{\}]+;$|
^\s*(?:pub)?\s+(?:const|enum|fn)\s+\w+.*$| ^\s*(?:pub)?\s+(?:const|enum|fn)\s+\w+.*$|
^\s*impl\s+\w+.*$ ^\s*impl\s+\w+.*$
) )
)", )",
PCRE_MULTILINE); PCRE_MULTILINE);
static pcrepp JAVA_MATCHERS = pcrepp( static const pcrepp JAVA_MATCHERS = pcrepp(
"(?:" "(?:"
"^package\\s+|" "^package\\s+|"
"^import\\s+|" "^import\\s+|"
@ -63,7 +63,7 @@ detect_text_format(const char* str, size_t len)
")", ")",
PCRE_MULTILINE); PCRE_MULTILINE);
static pcrepp C_LIKE_MATCHERS = pcrepp( static const pcrepp C_LIKE_MATCHERS = pcrepp(
"(?:" "(?:"
"^#\\s*include\\s+|" "^#\\s*include\\s+|"
"^#\\s*define\\s+|" "^#\\s*define\\s+|"
@ -72,14 +72,14 @@ detect_text_format(const char* str, size_t len)
")", ")",
PCRE_MULTILINE); PCRE_MULTILINE);
static pcrepp SQL_MATCHERS = pcrepp( static const pcrepp SQL_MATCHERS = pcrepp(
"(?:" "(?:"
"select\\s+.+\\s+from\\s+|" "select\\s+.+\\s+from\\s+|"
"insert\\s+into\\s+.+\\s+values" "insert\\s+into\\s+.+\\s+values"
")", ")",
PCRE_MULTILINE | PCRE_CASELESS); PCRE_MULTILINE | PCRE_CASELESS);
static pcrepp XML_MATCHERS = pcrepp( static const pcrepp XML_MATCHERS = pcrepp(
"(?:" "(?:"
R"(<\?xml(\s+\w+\s*=\s*"[^"]*")*\?>|)" R"(<\?xml(\s+\w+\s*=\s*"[^"]*")*\?>|)"
R"(</?\w+(\s+\w+\s*=\s*"[^"]*")*\s*>)" R"(</?\w+(\s+\w+\s*=\s*"[^"]*")*\s*>)"

@ -247,7 +247,7 @@ public:
class filter_stack { class filter_stack {
public: public:
typedef std::vector<std::shared_ptr<text_filter>>::iterator iterator; using iterator = std::vector<std::shared_ptr<text_filter>>::iterator;
explicit filter_stack(size_t reserved = 0) : fs_reserved(reserved) {} explicit filter_stack(size_t reserved = 0) : fs_reserved(reserved) {}

@ -27,9 +27,10 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
#include <cstring>
#include "views_vtab.hh" #include "views_vtab.hh"
#include <string.h>
#include <unistd.h> #include <unistd.h>
#include "base/injector.bind.hh" #include "base/injector.bind.hh"
@ -40,8 +41,6 @@
#include "sql_util.hh" #include "sql_util.hh"
#include "view_curses.hh" #include "view_curses.hh"
using namespace std;
template<> template<>
struct from_sqlite<lnav_view_t> { struct from_sqlite<lnav_view_t> {
inline lnav_view_t operator()(int argc, sqlite3_value** val, int argi) inline lnav_view_t operator()(int argc, sqlite3_value** val, int argi)
@ -67,19 +66,36 @@ struct from_sqlite<text_filter::type_t> {
if (strcasecmp(type_name, "in") == 0) { if (strcasecmp(type_name, "in") == 0) {
return text_filter::INCLUDE; return text_filter::INCLUDE;
} else if (strcasecmp(type_name, "out") == 0) { }
if (strcasecmp(type_name, "out") == 0) {
return text_filter::EXCLUDE; return text_filter::EXCLUDE;
} }
throw from_sqlite_conversion_error("filter type", argi); throw from_sqlite_conversion_error("value of 'in' or 'out'", argi);
}
};
template<>
struct from_sqlite<filter_lang_t> {
inline filter_lang_t operator()(int argc, sqlite3_value** val, int argi)
{
const char* type_name = (const char*) sqlite3_value_text(val[argi]);
if (strcasecmp(type_name, "regex") == 0) {
return filter_lang_t::REGEX;
}
if (strcasecmp(type_name, "sql") == 0) {
return filter_lang_t::SQL;
}
throw from_sqlite_conversion_error("value of 'regex' or 'sql'", argi);
} }
}; };
template<> template<>
struct from_sqlite<pair<string, auto_mem<pcre>>> { struct from_sqlite<std::pair<std::string, auto_mem<pcre>>> {
inline pair<string, auto_mem<pcre>> operator()(int argc, inline std::pair<std::string, auto_mem<pcre>> operator()(
sqlite3_value** val, int argc, sqlite3_value** val, int argi)
int argi)
{ {
const char* pattern = (const char*) sqlite3_value_text(val[argi]); const char* pattern = (const char*) sqlite3_value_text(val[argi]);
const char* errptr; const char* errptr;
@ -87,20 +103,19 @@ struct from_sqlite<pair<string, auto_mem<pcre>>> {
int eoff; int eoff;
if (pattern == nullptr || pattern[0] == '\0') { if (pattern == nullptr || pattern[0] == '\0') {
throw from_sqlite_conversion_error("non-empty pattern", argi); throw sqlite_func_error("Expecting a non-empty pattern value");
} }
code = pcre_compile(pattern, PCRE_CASELESS, &errptr, &eoff, nullptr); code = pcre_compile(pattern, PCRE_CASELESS, &errptr, &eoff, nullptr);
if (code == nullptr) { if (code == nullptr) {
throw sqlite_func_error( throw sqlite_func_error(
"Invalid regular expression in column {}: {} at offset {}", "Invalid regular expression for pattern: {} at offset {}",
argi,
errptr, errptr,
eoff); eoff);
} }
return make_pair(string(pattern), std::move(code)); return std::make_pair(std::string(pattern), std::move(code));
} }
}; };
@ -136,8 +151,8 @@ CREATE TABLE lnav_views (
int get_column(cursor& vc, sqlite3_context* ctx, int col) int get_column(cursor& vc, sqlite3_context* ctx, int col)
{ {
lnav_view_t view_index lnav_view_t view_index = (lnav_view_t) std::distance(
= (lnav_view_t) distance(std::begin(lnav_data.ld_views), vc.iter); std::begin(lnav_data.ld_views), vc.iter);
textview_curses& tc = *vc.iter; textview_curses& tc = *vc.iter;
unsigned long width; unsigned long width;
vis_line_t height; vis_line_t height;
@ -284,7 +299,7 @@ CREATE TABLE lnav_views (
}; };
struct lnav_view_stack : public tvt_iterator_cursor<lnav_view_stack> { struct lnav_view_stack : public tvt_iterator_cursor<lnav_view_stack> {
using iterator = vector<textview_curses*>::iterator; using iterator = std::vector<textview_curses*>::iterator;
static constexpr const char* NAME = "lnav_view_stack"; static constexpr const char* NAME = "lnav_view_stack";
static constexpr const char* CREATE_STMT = R"( static constexpr const char* CREATE_STMT = R"(
@ -358,7 +373,7 @@ struct lnav_view_filter_base {
using value_type = text_filter; using value_type = text_filter;
using pointer = text_filter*; using pointer = text_filter*;
using reference = text_filter&; using reference = text_filter&;
using iterator_category = forward_iterator_tag; using iterator_category = std::forward_iterator_tag;
lnav_view_t i_view_index; lnav_view_t i_view_index;
int i_filter_index; int i_filter_index;
@ -440,11 +455,12 @@ struct lnav_view_filters
static constexpr const char* CREATE_STMT = R"( static constexpr const char* CREATE_STMT = R"(
-- Access lnav's filters through this table. -- Access lnav's filters through this table.
CREATE TABLE lnav_view_filters ( CREATE TABLE lnav_view_filters (
view_name TEXT, -- The name of the view. view_name TEXT, -- The name of the view.
filter_id INTEGER DEFAULT 0, -- The filter identifier. filter_id INTEGER DEFAULT 0, -- The filter identifier.
enabled INTEGER DEFAULT 1, -- Indicates if the filter is enabled/disabled. enabled INTEGER DEFAULT 1, -- Indicates if the filter is enabled/disabled.
type TEXT DEFAULT 'out', -- The type of filter (i.e. in/out). type TEXT DEFAULT 'out', -- The type of filter (i.e. in/out).
pattern TEXT -- The filter pattern. language TEXT DEFAULT 'regex', -- The filter language.
pattern TEXT -- The filter pattern.
); );
)"; )";
@ -481,6 +497,18 @@ CREATE TABLE lnav_view_filters (
} }
break; break;
case 4: case 4:
switch (tf->get_lang()) {
case filter_lang_t::REGEX:
sqlite3_result_text(ctx, "regex", 5, SQLITE_STATIC);
break;
case filter_lang_t::SQL:
sqlite3_result_text(ctx, "sql", 3, SQLITE_STATIC);
break;
default:
ensure(0);
}
break;
case 5:
sqlite3_result_text( sqlite3_result_text(
ctx, tf->get_id().c_str(), -1, SQLITE_TRANSIENT); ctx, tf->get_id().c_str(), -1, SQLITE_TRANSIENT);
break; break;
@ -495,23 +523,75 @@ CREATE TABLE lnav_view_filters (
nonstd::optional<int64_t> _filter_id, nonstd::optional<int64_t> _filter_id,
nonstd::optional<bool> enabled, nonstd::optional<bool> enabled,
nonstd::optional<text_filter::type_t> type, nonstd::optional<text_filter::type_t> type,
pair<string, auto_mem<pcre>> pattern) nonstd::optional<filter_lang_t> lang,
sqlite3_value* pattern_str)
{ {
textview_curses& tc = lnav_data.ld_views[view_index]; textview_curses& tc = lnav_data.ld_views[view_index];
text_sub_source* tss = tc.get_sub_source(); text_sub_source* tss = tc.get_sub_source();
filter_stack& fs = tss->get_filters(); filter_stack& fs = tss->get_filters();
auto filter_index = fs.next_index(); auto filter_index
= lang.value_or(filter_lang_t::REGEX) == filter_lang_t::REGEX
? fs.next_index()
: nonstd::make_optional(size_t{0});
if (!filter_index) { if (!filter_index) {
throw sqlite_func_error("Too many filters"); throw sqlite_func_error("Too many filters");
} }
auto pf = make_shared<pcre_filter>( std::shared_ptr<text_filter> tf;
type.value_or(text_filter::type_t::EXCLUDE), switch (lang.value_or(filter_lang_t::REGEX)) {
pattern.first, case filter_lang_t::REGEX: {
*filter_index, auto pattern
pattern.second.release()); = from_sqlite<std::pair<std::string, auto_mem<pcre>>>()(
fs.add_filter(pf); 1, &pattern_str, 0);
auto pf = std::make_shared<pcre_filter>(
type.value_or(text_filter::type_t::EXCLUDE),
pattern.first,
*filter_index,
pattern.second.release());
fs.add_filter(pf);
tf = pf;
break;
}
case filter_lang_t::SQL: {
if (view_index != LNV_LOG) {
throw sqlite_func_error(
"SQL filters are only supported in the log view");
}
auto clause = from_sqlite<std::string>()(1, &pattern_str, 0);
auto expr = fmt::format("SELECT 1 WHERE {}", clause);
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
#ifdef SQLITE_PREPARE_PERSISTENT
auto retcode = sqlite3_prepare_v3(lnav_data.ld_db.in(),
expr.c_str(),
expr.size(),
SQLITE_PREPARE_PERSISTENT,
stmt.out(),
nullptr);
#else
auto retcode = sqlite3_prepare_v2(lnav_data.ld_db.in(),
expr.c_str(),
expr.size(),
stmt.out(),
nullptr);
#endif
if (retcode != SQLITE_OK) {
const char* errmsg = sqlite3_errmsg(lnav_data.ld_db);
throw sqlite_func_error("Invalid SQL: {}", errmsg);
}
auto set_res = lnav_data.ld_log_source.set_sql_filter(
clause, stmt.release());
if (set_res.isErr()) {
throw sqlite_func_error("filter expression failed with: {}",
set_res.unwrapErr());
}
tf = lnav_data.ld_log_source.get_sql_filter().value();
break;
}
default:
ensure(0);
}
if (!enabled.value_or(true)) { if (!enabled.value_or(true)) {
pf->disable(); tf->disable();
} }
tss->text_filters_changed(); tss->text_filters_changed();
tc.set_needs_update(); tc.set_needs_update();
@ -545,13 +625,14 @@ CREATE TABLE lnav_view_filters (
int64_t new_filter_id, int64_t new_filter_id,
bool enabled, bool enabled,
text_filter::type_t type, text_filter::type_t type,
pair<string, auto_mem<pcre>> pattern) filter_lang_t lang,
sqlite3_value* pattern_val)
{ {
auto view_index = lnav_view_t(rowid >> 32); auto view_index = lnav_view_t(rowid >> 32);
auto filter_index = rowid & 0xffffffffLL; auto filter_index = rowid & 0xffffffffLL;
textview_curses& tc = lnav_data.ld_views[view_index]; textview_curses& tc = lnav_data.ld_views[view_index];
text_sub_source* tss = tc.get_sub_source(); text_sub_source* tss = tc.get_sub_source();
filter_stack& fs = tss->get_filters(); auto& fs = tss->get_filters();
auto iter = fs.begin(); auto iter = fs.begin();
for (; iter != fs.end(); ++iter) { for (; iter != fs.end(); ++iter) {
if ((*iter)->get_index() == (size_t) filter_index) { if ((*iter)->get_index() == (size_t) filter_index) {
@ -559,7 +640,7 @@ CREATE TABLE lnav_view_filters (
} }
} }
shared_ptr<text_filter> tf = *iter; std::shared_ptr<text_filter> tf = *iter;
if (new_view_index != view_index) { if (new_view_index != view_index) {
tab->zErrMsg tab->zErrMsg
@ -567,17 +648,55 @@ CREATE TABLE lnav_view_filters (
return SQLITE_ERROR; return SQLITE_ERROR;
} }
tf->lf_deleted = true; if (lang == filter_lang_t::SQL && tf->get_index() == 0) {
tss->text_filters_changed(); if (view_index != LNV_LOG) {
throw sqlite_func_error(
"SQL filters are only supported in the log view");
}
auto clause = from_sqlite<std::string>()(1, &pattern_val, 0);
auto expr = fmt::format("SELECT 1 WHERE {}", clause);
auto_mem<sqlite3_stmt> stmt(sqlite3_finalize);
#ifdef SQLITE_PREPARE_PERSISTENT
auto retcode = sqlite3_prepare_v3(lnav_data.ld_db.in(),
expr.c_str(),
expr.size(),
SQLITE_PREPARE_PERSISTENT,
stmt.out(),
nullptr);
#else
auto retcode = sqlite3_prepare_v2(lnav_data.ld_db.in(),
expr.c_str(),
expr.size(),
stmt.out(),
nullptr);
#endif
if (retcode != SQLITE_OK) {
const char* errmsg = sqlite3_errmsg(lnav_data.ld_db);
throw sqlite_func_error("Invalid SQL: {}", errmsg);
}
auto set_res = lnav_data.ld_log_source.set_sql_filter(
clause, stmt.release());
if (set_res.isErr()) {
throw sqlite_func_error("filter expression failed with: {}",
set_res.unwrapErr());
}
*iter = lnav_data.ld_log_source.get_sql_filter().value();
} else {
tf->lf_deleted = true;
tss->text_filters_changed();
auto pf = make_shared<pcre_filter>( auto pattern
type, pattern.first, tf->get_index(), pattern.second.release()); = from_sqlite<std::pair<std::string, auto_mem<pcre>>>()(
1, &pattern_val, 0);
auto pf = std::make_shared<pcre_filter>(
type, pattern.first, tf->get_index(), pattern.second.release());
*iter = pf;
}
if (!enabled) { if (!enabled) {
pf->disable(); (*iter)->disable();
} }
*iter = pf;
tss->text_filters_changed(); tss->text_filters_changed();
tc.set_needs_update(); tc.set_needs_update();

@ -656,7 +656,7 @@ struct vtab_module : public vtab_module_base {
rowid, rowid,
argv, argv,
std::make_index_sequence<sizeof...(Args)>{}); std::make_index_sequence<sizeof...(Args)>{});
} catch (from_sqlite_conversion_error& e) { } catch (const from_sqlite_conversion_error& e) {
tab->zErrMsg = sqlite3_mprintf( tab->zErrMsg = sqlite3_mprintf(
"Expecting an %s for column number %d", e.e_type, e.e_argi); "Expecting an %s for column number %d", e.e_type, e.e_argi);
return SQLITE_ERROR; return SQLITE_ERROR;

@ -34,6 +34,7 @@
#endif #endif
#include "config.h" #include "config.h"
#include "fmt/format.h"
#include "yajl/api/yajl_gen.h" #include "yajl/api/yajl_gen.h"
#include "yajlpp/json_ptr.hh" #include "yajlpp/json_ptr.hh"
@ -450,10 +451,9 @@ json_ptr_walk::current_ptr()
if (this->jpw_array_indexes[lpc] == -1) { if (this->jpw_array_indexes[lpc] == -1) {
retval.append(this->jpw_keys[lpc]); retval.append(this->jpw_keys[lpc]);
} else { } else {
char num[64]; fmt::format_to(std::back_inserter(retval),
FMT_STRING("{}"),
snprintf(num, sizeof(num), "%d", this->jpw_array_indexes[lpc]); this->jpw_array_indexes[lpc]);
retval.append(num);
} }
} }

@ -385,6 +385,46 @@ json_path_handler_base::walk(
} }
} }
nonstd::optional<int>
json_path_handler_base::to_enum_value(const string_fragment& sf) const
{
for (int lpc = 0; this->jph_enum_values[lpc].first; lpc++) {
const enum_value_t& ev = this->jph_enum_values[lpc];
if (sf == ev.first) {
return ev.second;
}
}
return nonstd::nullopt;
}
vector<json_path_handler_base::schema_type_t>
json_path_handler_base::get_types() const
{
std::vector<schema_type_t> retval;
if (this->jph_callbacks.yajl_boolean) {
retval.push_back(schema_type_t::BOOLEAN);
}
if (this->jph_callbacks.yajl_integer) {
retval.push_back(schema_type_t::INTEGER);
}
if (this->jph_callbacks.yajl_double || this->jph_callbacks.yajl_number) {
retval.push_back(schema_type_t::NUMBER);
}
if (this->jph_callbacks.yajl_string) {
retval.push_back(schema_type_t::STRING);
}
if (this->jph_children) {
retval.push_back(schema_type_t::OBJECT);
}
if (retval.empty()) {
retval.push_back(schema_type_t::ANY);
}
return retval;
}
yajlpp_parse_context::yajlpp_parse_context( yajlpp_parse_context::yajlpp_parse_context(
std::string source, const struct json_path_container* handlers) std::string source, const struct json_path_container* handlers)
: ypc_source(std::move(source)), ypc_handlers(handlers) : ypc_source(std::move(source)), ypc_handlers(handlers)
@ -1073,3 +1113,13 @@ dump_schema_to(const json_path_container& jpc,
ygc.gen_schema(); ygc.gen_schema();
} }
string_fragment
yajlpp_gen::to_string_fragment()
{
const unsigned char* buf;
size_t len;
yajl_gen_get_buf(this->yg_handle.in(), &buf, &len);
return string_fragment((const char*) buf, 0, len);
}

@ -151,18 +151,7 @@ struct json_path_handler_base {
return this->jph_is_array; return this->jph_is_array;
} }
nonstd::optional<int> to_enum_value(const string_fragment& sf) const nonstd::optional<int> to_enum_value(const string_fragment& sf) const;
{
for (int lpc = 0; this->jph_enum_values[lpc].first; lpc++) {
const enum_value_t& ev = this->jph_enum_values[lpc];
if (sf == ev.first) {
return ev.second;
}
}
return nonstd::nullopt;
};
yajl_gen_status gen(yajlpp_gen_context& ygc, yajl_gen handle) const; yajl_gen_status gen(yajlpp_gen_context& ygc, yajl_gen handle) const;
yajl_gen_status gen_schema(yajlpp_gen_context& ygc) const; yajl_gen_status gen_schema(yajlpp_gen_context& ygc) const;
@ -183,31 +172,7 @@ struct json_path_handler_base {
OBJECT, OBJECT,
}; };
std::vector<schema_type_t> get_types() const std::vector<schema_type_t> get_types() const;
{
std::vector<schema_type_t> retval;
if (this->jph_callbacks.yajl_boolean) {
retval.push_back(schema_type_t::BOOLEAN);
}
if (this->jph_callbacks.yajl_integer) {
retval.push_back(schema_type_t::INTEGER);
}
if (this->jph_callbacks.yajl_double || this->jph_callbacks.yajl_number)
{
retval.push_back(schema_type_t::NUMBER);
}
if (this->jph_callbacks.yajl_string) {
retval.push_back(schema_type_t::STRING);
}
if (this->jph_children) {
retval.push_back(schema_type_t::OBJECT);
}
if (retval.empty()) {
retval.push_back(schema_type_t::ANY);
}
return retval;
};
std::string jph_property; std::string jph_property;
pcrepp jph_regex; pcrepp jph_regex;
@ -607,15 +572,7 @@ public:
return this->yg_handle.in(); return this->yg_handle.in();
}; };
string_fragment to_string_fragment() string_fragment to_string_fragment();
{
const unsigned char* buf;
size_t len;
yajl_gen_get_buf(this->yg_handle.in(), &buf, &len);
return string_fragment((const char*) buf, 0, len);
};
private: private:
auto_mem<yajl_gen_t> yg_handle; auto_mem<yajl_gen_t> yg_handle;

@ -2,4 +2,4 @@
CREATE VIEW web_status AS CREATE VIEW web_status AS
SELECT group_concat(cs_uri_stem), sc_status FROM access_log group by sc_status; SELECT group_concat(cs_uri_stem), sc_status FROM access_log group by sc_status;
INSERT into lnav_view_filters VALUES ("log", 5, 0, "in", "credential status"); INSERT into lnav_view_filters VALUES ("log", 5, 0, "in", "regex", "credential status");

@ -353,23 +353,23 @@ command-option:2: error: Only the top view in the stack can be deleted
EOF EOF
run_test ${lnav_test} -n \ run_test ${lnav_test} -n \
-c ";INSERT INTO lnav_view_filters VALUES ('log', 0, 1, 'out', '')" \ -c ";INSERT INTO lnav_view_filters VALUES ('log', 0, 1, 'out', 'regex', '')" \
${test_dir}/logfile_access_log.0 ${test_dir}/logfile_access_log.0
check_error_output "inserted filter with an empty pattern?" <<EOF check_error_output "inserted filter with an empty pattern?" <<EOF
command-option:1: error: Expecting an non-empty pattern for column number 4 command-option:1: error: Expecting a non-empty pattern value
EOF EOF
run_test ${lnav_test} -n \ run_test ${lnav_test} -n \
-c ";INSERT INTO lnav_view_filters VALUES ('log', 0, 1, 'out', 'abc(')" \ -c ";INSERT INTO lnav_view_filters VALUES ('log', 0, 1, 'out', 'regex', 'abc(')" \
${test_dir}/logfile_access_log.0 ${test_dir}/logfile_access_log.0
check_error_output "inserted filter with an invalid pattern?" <<EOF check_error_output "inserted filter with an invalid pattern?" <<EOF
command-option:1: error: Invalid regular expression in column 4: missing ) at offset 4 command-option:1: error: Invalid regular expression for pattern: missing ) at offset 4
EOF EOF
run_test ${lnav_test} -n \ run_test ${lnav_test} -n \
-c ";INSERT INTO lnav_view_filters VALUES ('bad', 0, 1, 'out', 'abc')" \ -c ";INSERT INTO lnav_view_filters VALUES ('bad', 0, 1, 'out', 'regex', 'abc')" \
${test_dir}/logfile_access_log.0 ${test_dir}/logfile_access_log.0
check_error_output "inserted filter with an invalid view name?" <<EOF check_error_output "inserted filter with an invalid view name?" <<EOF
@ -377,7 +377,7 @@ command-option:1: error: Expecting an lnav view name for column number 0
EOF EOF
run_test ${lnav_test} -n \ run_test ${lnav_test} -n \
-c ";INSERT INTO lnav_view_filters VALUES (NULL, 0, 1, 'out', 'abc')" \ -c ";INSERT INTO lnav_view_filters VALUES (NULL, 0, 1, 'out', 'regex', 'abc')" \
${test_dir}/logfile_access_log.0 ${test_dir}/logfile_access_log.0
check_error_output "inserted filter with a null view name?" <<EOF check_error_output "inserted filter with a null view name?" <<EOF
@ -385,11 +385,11 @@ command-option:1: error: Expecting an lnav view name for column number 0
EOF EOF
run_test ${lnav_test} -n \ run_test ${lnav_test} -n \
-c ";INSERT INTO lnav_view_filters VALUES ('log', 0 , 1, 'bad', 'abc')" \ -c ";INSERT INTO lnav_view_filters VALUES ('log', 0 , 1, 'bad', 'regex', 'abc')" \
${test_dir}/logfile_access_log.0 ${test_dir}/logfile_access_log.0
check_error_output "inserted filter with an invalid filter type?" <<EOF check_error_output "inserted filter with an invalid filter type?" <<EOF
command-option:1: error: Expecting an filter type for column number 3 command-option:1: error: Expecting an value of 'in' or 'out' for column number 3
EOF EOF
run_test ${lnav_test} -n \ run_test ${lnav_test} -n \
@ -441,6 +441,14 @@ view_name,filter_id,hits
log,1,2 log,1,2
EOF EOF
run_test ${lnav_test} -n \
-c ";INSERT INTO lnav_view_filters (view_name, language, pattern) VALUES ('log', 'sql', ':sc_bytes = 134')" \
${test_dir}/logfile_access_log.0
check_output "inserted filter-out did not work?" <<EOF
192.168.202.254 - - [20/Jul/2009:22:59:26 +0000] "GET /vmw/cgi/tramp HTTP/1.0" 200 134 "-" "gPXE/0.9.7"
EOF
run_test ${lnav_test} -n \ run_test ${lnav_test} -n \
-c ";SELECT * FROM access_log LIMIT 0" \ -c ";SELECT * FROM access_log LIMIT 0" \
-c ':switch-to-view db' \ -c ':switch-to-view db' \

Loading…
Cancel
Save