mirror of https://github.com/tstack/lnav.git
[build] some clang-tidy fixes
This commit is contained in:
parent
eb42ef6e77
commit
6fff9d60f5
20
.clang-tidy
20
.clang-tidy
|
@ -4,6 +4,8 @@
|
|||
Checks: "*,\
|
||||
-google-readability-todo,\
|
||||
-altera-unroll-loops,\
|
||||
-altera-id-dependent-backward-branch,\
|
||||
-altera-struct-pack-align,\
|
||||
-fuchsia-*,\
|
||||
fuchsia-multiple-inheritance,\
|
||||
-llvm-header-guard,\
|
||||
|
@ -50,15 +52,15 @@ CheckOptions:
|
|||
- key: 'readability-identifier-naming.ClassCase'
|
||||
value: 'lower_case'
|
||||
- key: 'readability-identifier-naming.ClassConstantCase'
|
||||
value: 'lower_case'
|
||||
value: 'UPPER_CASE'
|
||||
- key: 'readability-identifier-naming.ClassMemberCase'
|
||||
value: 'lower_case'
|
||||
- key: 'readability-identifier-naming.ClassMethodCase'
|
||||
value: 'lower_case'
|
||||
- key: 'readability-identifier-naming.ConstantCase'
|
||||
value: 'lower_case'
|
||||
value: 'UPPER_CASE'
|
||||
- key: 'readability-identifier-naming.ConstantMemberCase'
|
||||
value: 'lower_case'
|
||||
value: 'UPPER_CASE'
|
||||
- key: 'readability-identifier-naming.ConstantParameterCase'
|
||||
value: 'lower_case'
|
||||
- key: 'readability-identifier-naming.ConstantPointerParameterCase'
|
||||
|
@ -76,7 +78,7 @@ CheckOptions:
|
|||
- key: 'readability-identifier-naming.FunctionCase'
|
||||
value: 'lower_case'
|
||||
- key: 'readability-identifier-naming.GlobalConstantCase'
|
||||
value: 'lower_case'
|
||||
value: 'UPPER_CASE'
|
||||
- key: 'readability-identifier-naming.GlobalConstantPointerCase'
|
||||
value: 'lower_case'
|
||||
- key: 'readability-identifier-naming.GlobalFunctionCase'
|
||||
|
@ -111,14 +113,10 @@ CheckOptions:
|
|||
value: 'lower_case'
|
||||
- key: 'readability-identifier-naming.PrivateMemberCase'
|
||||
value: 'lower_case'
|
||||
- key: 'readability-identifier-naming.PrivateMemberPrefix'
|
||||
value: 'm_'
|
||||
- key: 'readability-identifier-naming.PrivateMethodCase'
|
||||
value: 'lower_case'
|
||||
- key: 'readability-identifier-naming.ProtectedMemberCase'
|
||||
value: 'lower_case'
|
||||
- key: 'readability-identifier-naming.ProtectedMemberPrefix'
|
||||
value: 'm_'
|
||||
- key: 'readability-identifier-naming.ProtectedMethodCase'
|
||||
value: 'lower_case'
|
||||
- key: 'readability-identifier-naming.PublicMemberCase'
|
||||
|
@ -128,7 +126,7 @@ CheckOptions:
|
|||
- key: 'readability-identifier-naming.ScopedEnumConstantCase'
|
||||
value: 'lower_case'
|
||||
- key: 'readability-identifier-naming.StaticConstantCase'
|
||||
value: 'lower_case'
|
||||
value: 'UPPER_CASE'
|
||||
- key: 'readability-identifier-naming.StaticVariableCase'
|
||||
value: 'lower_case'
|
||||
- key: 'readability-identifier-naming.StructCase'
|
||||
|
@ -151,4 +149,8 @@ CheckOptions:
|
|||
value: 'lower_case'
|
||||
- key: 'readability-identifier-naming.VirtualMethodCase'
|
||||
value: 'lower_case'
|
||||
- key: 'readability-identifier-length.MinimumVariableNameLength'
|
||||
value: '2'
|
||||
- key: 'readability-identifier-length.MinimumParameterNameLength'
|
||||
value: '2'
|
||||
...
|
||||
|
|
9
NEWS
9
NEWS
|
@ -3,6 +3,15 @@ lnav v0.10.2:
|
|||
* Add initial support for pcap(3) files using tshark(1).
|
||||
* 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:
|
||||
Features:
|
||||
* Added ":show-only-this-file" command that hides all files except the
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
# 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
|
||||
|
|
|
@ -211,6 +211,7 @@ add_library(
|
|||
cppfmt STATIC
|
||||
fmtlib/format.cc
|
||||
fmtlib/os.cc
|
||||
fmtlib/fmt/args.h
|
||||
fmtlib/fmt/chrono.h
|
||||
fmtlib/fmt/color.h
|
||||
fmtlib/fmt/compile.h
|
||||
|
@ -220,10 +221,10 @@ add_library(
|
|||
fmtlib/fmt/locale.h
|
||||
fmtlib/fmt/os.h
|
||||
fmtlib/fmt/ostream.h
|
||||
fmtlib/fmt/posix.h
|
||||
fmtlib/fmt/printf.h
|
||||
fmtlib/fmt/ranges.h
|
||||
fmtlib/fmt/time.h)
|
||||
fmtlib/fmt/xchar.h
|
||||
)
|
||||
target_include_directories(cppfmt PUBLIC fmtlib)
|
||||
|
||||
add_library(lnavfileio STATIC
|
||||
|
@ -461,6 +462,7 @@ target_link_libraries(
|
|||
tailercommon
|
||||
logfmt
|
||||
yajlpp
|
||||
cppfmt
|
||||
${lnav_LIBS})
|
||||
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);
|
||||
|
||||
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;
|
||||
schema_ref.share(this->alv_schema_manager,
|
||||
this->alv_schema_buffer.data(),
|
||||
|
|
|
@ -58,7 +58,8 @@ private:
|
|||
logline_value_meta alv_msg_meta;
|
||||
logline_value_meta alv_schema_meta;
|
||||
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
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
line_range intersection(const struct line_range& other) const
|
||||
{
|
||||
int actual_end;
|
||||
line_range intersection(const struct line_range& other) const;
|
||||
|
||||
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)
|
||||
{
|
||||
|
@ -220,7 +195,7 @@ struct string_attr {
|
|||
|
||||
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;
|
||||
|
@ -230,7 +205,7 @@ struct string_attr {
|
|||
};
|
||||
|
||||
/** 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
|
||||
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
|
||||
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;
|
||||
|
||||
for (iter = sa.begin(); iter != sa.end(); ++iter) {
|
||||
auto& lr = iter->sa_range;
|
||||
for (auto iter = sa.begin(); iter != sa.end(); ++iter) {
|
||||
const auto& lr = iter->sa_range;
|
||||
|
||||
if (!lr.is_valid() || !lr.contains(near)) {
|
||||
continue;
|
||||
|
@ -320,11 +295,11 @@ template<typename T>
|
|||
inline string_attrs_t::const_iterator
|
||||
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;
|
||||
|
||||
for (iter = sa.begin(); iter != sa.end(); ++iter) {
|
||||
auto& lr = iter->sa_range;
|
||||
for (auto iter = sa.begin(); iter != sa.end(); ++iter) {
|
||||
const auto& lr = iter->sa_range;
|
||||
|
||||
if (lr.lr_start > near) {
|
||||
continue;
|
||||
|
@ -387,8 +362,6 @@ shift_string_attrs(string_attrs_t& sa, int32_t start, int32_t amount)
|
|||
}
|
||||
|
||||
struct text_wrap_settings {
|
||||
text_wrap_settings() : tws_indent(2), tws_width(80){};
|
||||
|
||||
text_wrap_settings& with_indent(int indent)
|
||||
{
|
||||
this->tws_indent = indent;
|
||||
|
@ -401,8 +374,8 @@ struct text_wrap_settings {
|
|||
return *this;
|
||||
};
|
||||
|
||||
int tws_indent;
|
||||
int tws_width;
|
||||
int tws_indent{2};
|
||||
int tws_width{80};
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -32,10 +32,10 @@
|
|||
#ifndef lnav_date_time_scanner_hh
|
||||
#define lnav_date_time_scanner_hh
|
||||
|
||||
#include <ctime>
|
||||
#include <string>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <time.h>
|
||||
|
||||
#include "time_util.hh"
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ public:
|
|||
|
||||
bool empty() const
|
||||
{
|
||||
return fr_size == 0;
|
||||
return this->fr_size == 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -42,11 +42,13 @@ template<>
|
|||
struct formatter<network::locality> {
|
||||
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:
|
||||
if (it != end && *it != '}')
|
||||
if (it != end && *it != '}') {
|
||||
throw format_error("invalid format");
|
||||
}
|
||||
|
||||
// Return an iterator past the end of the parsed range:
|
||||
return it;
|
||||
|
@ -71,11 +73,13 @@ template<>
|
|||
struct formatter<network::path> {
|
||||
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:
|
||||
if (it != end && *it != '}')
|
||||
if (it != end && *it != '}') {
|
||||
throw format_error("invalid format");
|
||||
}
|
||||
|
||||
// Return an iterator past the end of the parsed range:
|
||||
return it;
|
||||
|
|
|
@ -51,9 +51,6 @@ point::as_time_ago() const
|
|||
{
|
||||
struct timeval current_time
|
||||
= this->p_recent_point.value_or(current_timeval());
|
||||
const char* fmt;
|
||||
char buffer[64];
|
||||
int amount;
|
||||
|
||||
if (this->p_convert_to_local) {
|
||||
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);
|
||||
if (delta < 0s) {
|
||||
return "in the future";
|
||||
} else if (delta < 1min) {
|
||||
return "just now";
|
||||
} else if (delta < 2min) {
|
||||
return "one minute ago";
|
||||
} else if (delta < 1h) {
|
||||
fmt = "%d minutes ago";
|
||||
amount
|
||||
= std::chrono::duration_cast<std::chrono::minutes>(delta).count();
|
||||
} else if (delta < 2h) {
|
||||
return "one hour ago";
|
||||
} else if (delta < 24h) {
|
||||
fmt = "%d hours ago";
|
||||
amount = std::chrono::duration_cast<std::chrono::hours>(delta).count();
|
||||
} else if (delta < 48h) {
|
||||
return "one day ago";
|
||||
} else if (delta < 365 * 24h) {
|
||||
fmt = "%d days ago";
|
||||
amount = delta / 24h;
|
||||
} else if (delta < (2 * 365 * 24h)) {
|
||||
return "over a year ago";
|
||||
} else {
|
||||
fmt = "over %d years ago";
|
||||
amount = delta / (365 * 24h);
|
||||
}
|
||||
|
||||
snprintf(buffer, sizeof(buffer), fmt, amount);
|
||||
|
||||
return std::string(buffer);
|
||||
if (delta < 1min) {
|
||||
return "just now";
|
||||
}
|
||||
if (delta < 2min) {
|
||||
return "one minute ago";
|
||||
}
|
||||
if (delta < 1h) {
|
||||
return fmt::format(
|
||||
FMT_STRING("{} minutes ago"),
|
||||
std::chrono::duration_cast<std::chrono::minutes>(delta).count());
|
||||
}
|
||||
if (delta < 2h) {
|
||||
return "one hour ago";
|
||||
}
|
||||
if (delta < 24h) {
|
||||
return fmt::format(
|
||||
FMT_STRING("{} hours ago"),
|
||||
std::chrono::duration_cast<std::chrono::hours>(delta).count());
|
||||
}
|
||||
if (delta < 48h) {
|
||||
return "one day ago";
|
||||
}
|
||||
if (delta < 365 * 24h) {
|
||||
return fmt::format(FMT_STRING("{} days ago"), delta / 24h);
|
||||
}
|
||||
if (delta < (2 * 365 * 24h)) {
|
||||
return "over a year ago";
|
||||
}
|
||||
return fmt::format(FMT_STRING("over {} years ago"), delta / (365 * 24h));
|
||||
}
|
||||
|
||||
std::string
|
||||
|
@ -149,7 +148,7 @@ duration::to_string() const
|
|||
{0, "%lld%s", "d"},
|
||||
};
|
||||
|
||||
auto* curr_interval = intervals;
|
||||
const auto* curr_interval = intervals;
|
||||
auto usecs = std::chrono::duration_cast<std::chrono::microseconds>(
|
||||
std::chrono::seconds(this->d_timeval.tv_sec))
|
||||
+ std::chrono::microseconds(this->d_timeval.tv_usec);
|
||||
|
|
|
@ -140,7 +140,7 @@ struct string_fragment {
|
|||
|
||||
bool startswith(const char* prefix) const
|
||||
{
|
||||
auto iter = this->begin();
|
||||
const auto* iter = this->begin();
|
||||
|
||||
while (*prefix != '\0' && *prefix == *iter && iter < this->end()) {
|
||||
prefix += 1;
|
||||
|
|
|
@ -55,8 +55,8 @@ struct path {
|
|||
locality p_locality;
|
||||
std::string p_path;
|
||||
|
||||
path(locality l, std::string path)
|
||||
: p_locality(std::move(l)), p_path(std::move(path))
|
||||
path(locality loc, std::string path)
|
||||
: p_locality(std::move(loc)), p_path(std::move(path))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include <sys/types.h>
|
||||
|
||||
#include "base/lnav_log.hh"
|
||||
#include "fmt/format.h"
|
||||
|
||||
template<size_t COUNT, typename T = unsigned char>
|
||||
struct byte_array {
|
||||
|
@ -70,21 +71,21 @@ struct byte_array {
|
|||
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++) {
|
||||
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
|
||||
{
|
||||
char buffer[STRING_SIZE];
|
||||
std::string retval;
|
||||
|
||||
this->to_string(buffer);
|
||||
return std::string(buffer);
|
||||
retval.reserve(STRING_SIZE);
|
||||
this->to_string(std::back_inserter(retval));
|
||||
return retval;
|
||||
}
|
||||
|
||||
const unsigned char* in() const
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
#include "config.h"
|
||||
#include "log_level.hh"
|
||||
|
||||
#define MAX_ADDR_LEN 128
|
||||
constexpr int MAX_ADDR_LEN = 128;
|
||||
|
||||
static int
|
||||
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
|
||||
column_namer::add_column(const std::string& in_name)
|
||||
{
|
||||
std::string base_name = in_name, retval;
|
||||
size_t buf_size;
|
||||
auto base_name = in_name;
|
||||
std::string retval;
|
||||
int num = 0;
|
||||
|
||||
buf_size = in_name.length() + 64;
|
||||
char buffer[buf_size];
|
||||
if (in_name.empty()) {
|
||||
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);
|
||||
if (counter_iter != this->cn_name_counters.end()) {
|
||||
num = ++counter_iter->second;
|
||||
snprintf(buffer, buf_size, "%s_%d", base_name.c_str(), num);
|
||||
retval = buffer;
|
||||
retval = fmt::format(FMT_STRING("{}_{}"), base_name, num);
|
||||
}
|
||||
|
||||
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());
|
||||
snprintf(buffer, buf_size, "%s_%d", base_name.c_str(), num);
|
||||
retval = buffer;
|
||||
retval = fmt::format(FMT_STRING("{}_{}"), base_name, num);
|
||||
num += 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -38,15 +38,13 @@
|
|||
|
||||
class column_namer {
|
||||
public:
|
||||
column_namer() : cn_builtin_names({"col"}) {}
|
||||
|
||||
bool existing_name(const std::string& in_name) const;
|
||||
|
||||
std::string add_column(const std::string& in_name);
|
||||
|
||||
std::vector<std::string> cn_builtin_names;
|
||||
std::vector<std::string> cn_names;
|
||||
std::unordered_map<std::string, int> cn_name_counters;
|
||||
std::vector<std::string> cn_builtin_names{"col"};
|
||||
std::vector<std::string> cn_names{};
|
||||
std::unordered_map<std::string, int> cn_name_counters{};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -818,14 +818,10 @@ pipe_callback(exec_context& ec, const string& cmdline, auto_fd& fd)
|
|||
.expect("Cannot create temporary file for callback")
|
||||
.second);
|
||||
static int exec_count = 0;
|
||||
char desc[128];
|
||||
|
||||
lnav_data.ld_pipers.push_back(pp);
|
||||
snprintf(desc,
|
||||
sizeof(desc),
|
||||
"[%d] Output of %s",
|
||||
exec_count++,
|
||||
cmdline.c_str());
|
||||
auto desc = fmt::format(
|
||||
FMT_STRING("[{}] Output of {}"), exec_count++, cmdline);
|
||||
lnav_data.ld_active_files.fc_file_names[desc]
|
||||
.with_fd(pp->get_fd())
|
||||
.with_include_in_session(false)
|
||||
|
|
163356
src/data_scanner_re.cc
163356
src/data_scanner_re.cc
File diff suppressed because it is too large
Load Diff
|
@ -102,9 +102,9 @@ public:
|
|||
struct header_meta {
|
||||
explicit header_meta(std::string name)
|
||||
: 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
|
||||
{
|
||||
|
|
|
@ -103,10 +103,7 @@ map_elements_to_json2(yajl_gen gen,
|
|||
= dp.get_element_string(iter.e_sub_elements->front());
|
||||
|
||||
if (key_str.empty()) {
|
||||
char buffer[32];
|
||||
|
||||
snprintf(buffer, sizeof(buffer), "col_%d", col);
|
||||
key_str = buffer;
|
||||
key_str = fmt::format(FMT_STRING("col_{}"), col);
|
||||
col += 1;
|
||||
}
|
||||
root_map.gen(key_str);
|
||||
|
|
|
@ -40,7 +40,7 @@ using namespace std;
|
|||
|
||||
extern char** environ;
|
||||
|
||||
const char* ENVIRON_CREATE_STMT = R"(
|
||||
const char* const ENVIRON_CREATE_STMT = R"(
|
||||
-- Access lnav's environment variables through this table.
|
||||
CREATE TABLE environ (
|
||||
name text PRIMARY KEY,
|
||||
|
|
|
@ -34,6 +34,6 @@
|
|||
|
||||
int register_environ_vtab(sqlite3* db);
|
||||
|
||||
extern const char* ENVIRON_CREATE_STMT;
|
||||
extern const char* const ENVIRON_CREATE_STMT;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -332,16 +332,14 @@ field_overlay_source::build_field_lines(const listview_curses& lv)
|
|||
orig_tv.tv_usec / 1000,
|
||||
'T');
|
||||
if (offset_tv.tv_sec || offset_tv.tv_usec) {
|
||||
char offset_str[32];
|
||||
|
||||
time_str.append(" Pre-adjust Time: ");
|
||||
time_str.append(old_timestamp);
|
||||
snprintf(offset_str,
|
||||
sizeof(offset_str),
|
||||
" Offset: %+d.%03d",
|
||||
(int) offset_tv.tv_sec,
|
||||
(int) (offset_tv.tv_usec / 1000));
|
||||
time_str.append(offset_str);
|
||||
fmt::format_to(std::back_inserter(time_str),
|
||||
FMT_STRING(" Offset: {:+}.{:03}"),
|
||||
offset_tv.tv_sec,
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::microseconds(offset_tv.tv_usec))
|
||||
.count());
|
||||
}
|
||||
|
||||
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) {
|
||||
switch (pair.second.ofd_format) {
|
||||
case file_format_t::FF_UNKNOWN:
|
||||
case file_format_t::FF_ARCHIVE:
|
||||
case file_format_t::FF_PCAP:
|
||||
case file_format_t::FF_SQLITE_DB: {
|
||||
case file_format_t::UNKNOWN:
|
||||
case file_format_t::ARCHIVE:
|
||||
case file_format_t::PCAP:
|
||||
case file_format_t::SQLITE_DB: {
|
||||
auto bn = ghc::filesystem::path(pair.first).filename().string();
|
||||
if (bn.length() > this->fc_largest_path_length) {
|
||||
this->fc_largest_path_length = bn.length();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case file_format_t::FF_REMOTE: {
|
||||
case file_format_t::REMOTE: {
|
||||
if (pair.first.length() > this->fc_largest_path_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;
|
||||
switch (ff) {
|
||||
case file_format_t::FF_SQLITE_DB:
|
||||
case file_format_t::SQLITE_DB:
|
||||
retval.fc_other_files[filename].ofd_format = ff;
|
||||
break;
|
||||
|
||||
case file_format_t::FF_PCAP: {
|
||||
case file_format_t::PCAP: {
|
||||
auto res = pcap_manager::convert(filename);
|
||||
|
||||
if (res.isOk()) {
|
||||
|
@ -363,7 +363,7 @@ file_collection::watch_logfile(const std::string& filename,
|
|||
break;
|
||||
}
|
||||
|
||||
case file_format_t::FF_ARCHIVE: {
|
||||
case file_format_t::ARCHIVE: {
|
||||
nonstd::optional<
|
||||
std::list<archive_manager::extract_progress>::iterator>
|
||||
prog_iter_opt;
|
||||
|
@ -516,7 +516,7 @@ file_collection::expand_filename(
|
|||
|
||||
isc::to<tailer::looper&, services::remote_tailer_t>().send(
|
||||
[=](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()
|
||||
->sp_tailers[fmt::format("{}", rp.home())]
|
||||
|
|
|
@ -61,7 +61,7 @@ struct other_file_descriptor {
|
|||
file_format_t ofd_format;
|
||||
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 = "")
|
||||
: ofd_format(format), ofd_description(std::move(description))
|
||||
{
|
||||
|
|
|
@ -93,10 +93,10 @@ file_format_t
|
|||
detect_file_format(const ghc::filesystem::path& 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;
|
||||
|
||||
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);
|
||||
|
||||
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)) {
|
||||
retval = file_format_t::FF_PCAP;
|
||||
retval = file_format_t::PCAP;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,11 +36,11 @@
|
|||
#include "ghc/filesystem.hpp"
|
||||
|
||||
enum class file_format_t : int {
|
||||
FF_UNKNOWN,
|
||||
FF_SQLITE_DB,
|
||||
FF_ARCHIVE,
|
||||
FF_PCAP,
|
||||
FF_REMOTE,
|
||||
UNKNOWN,
|
||||
SQLITE_DB,
|
||||
ARCHIVE,
|
||||
PCAP,
|
||||
REMOTE,
|
||||
};
|
||||
|
||||
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";
|
||||
switch (ff) {
|
||||
case file_format_t::FF_SQLITE_DB:
|
||||
case file_format_t::SQLITE_DB:
|
||||
name = "\U0001F5C2 SQLite DB";
|
||||
break;
|
||||
case file_format_t::FF_ARCHIVE:
|
||||
case file_format_t::ARCHIVE:
|
||||
name = "\U0001F5C4 Archive";
|
||||
break;
|
||||
case file_format_t::FF_PCAP:
|
||||
case file_format_t::PCAP:
|
||||
name = "\U0001F5A5 Pcap";
|
||||
break;
|
||||
case file_format_t::FF_REMOTE:
|
||||
case file_format_t::REMOTE:
|
||||
name = "\U0001F5A5 Remote";
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -39,8 +39,6 @@ class files_sub_source
|
|||
public:
|
||||
files_sub_source();
|
||||
|
||||
~files_sub_source() override = default;
|
||||
|
||||
bool list_input_handle_key(listview_curses& lv, int ch) override;
|
||||
|
||||
void list_input_handle_scroll_out(listview_curses& lv) override;
|
||||
|
@ -62,8 +60,6 @@ public:
|
|||
int line,
|
||||
line_flags_t raw) override;
|
||||
|
||||
bool fss_editing{false};
|
||||
bool fss_filter_state{false};
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
public:
|
||||
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) {
|
||||
filter->revert_to_last(this->lfo_filter_state, rollback_size);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
void logline_new_lines(const logfile& lf,
|
||||
logfile::const_iterator ll_begin,
|
||||
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,
|
||||
uint32_t filter_out_mask,
|
||||
|
@ -65,37 +65,11 @@ public:
|
|||
bool filtered_out
|
||||
= (this->lfo_filter_state.tfs_mask[offset] & filter_out_mask) != 0;
|
||||
return !filtered_in || filtered_out;
|
||||
};
|
||||
}
|
||||
|
||||
size_t get_min_count(size_t max) const
|
||||
{
|
||||
size_t retval = max;
|
||||
size_t get_min_count(size_t max) const;
|
||||
|
||||
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 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);
|
||||
};
|
||||
void clear_deleted_filter_state();
|
||||
|
||||
filter_stack& lfo_filter_stack;
|
||||
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();
|
||||
filter_stack& fs = tss->get_filters();
|
||||
shared_ptr<text_filter> tf = *(fs.begin() + line);
|
||||
char hits[32];
|
||||
|
||||
value_out = " ";
|
||||
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()) {
|
||||
snprintf(hits, sizeof(hits), "%9s hits | ", "-");
|
||||
fmt::format_to(
|
||||
std::back_inserter(value_out), FMT_STRING("{:>9} hits | "), "-");
|
||||
} else {
|
||||
snprintf(hits,
|
||||
sizeof(hits),
|
||||
"%'9d hits | ",
|
||||
tss->get_filtered_count_for(tf->get_index()));
|
||||
fmt::format_to(std::back_inserter(value_out),
|
||||
FMT_STRING("{:>9L} hits | "),
|
||||
tss->get_filtered_count_for(tf->get_index()));
|
||||
}
|
||||
value_out.append(hits);
|
||||
|
||||
value_out.append(tf->get_id());
|
||||
}
|
||||
|
@ -384,6 +382,24 @@ filter_sub_source::text_attrs_for_line(textview_curses& tc,
|
|||
value_out.emplace_back(
|
||||
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
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
|
||||
noinst_HEADERS = \
|
||||
fmt/args.h \
|
||||
fmt/chrono.h \
|
||||
fmt/color.h \
|
||||
fmt/compile.h \
|
||||
|
@ -9,10 +10,9 @@ noinst_HEADERS = \
|
|||
fmt/locale.h \
|
||||
fmt/os.h \
|
||||
fmt/ostream.h \
|
||||
fmt/posix.h \
|
||||
fmt/printf.h \
|
||||
fmt/ranges.h \
|
||||
fmt/time.h
|
||||
fmt/xchar.h
|
||||
|
||||
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"
|
||||
|
||||
// __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_MODULE_EXPORT_BEGIN
|
||||
|
||||
enum class color : uint32_t {
|
||||
alice_blue = 0xF0F8FF, // rgb(240,248,255)
|
||||
|
@ -177,9 +185,13 @@ enum class terminal_color : uint8_t {
|
|||
|
||||
enum class emphasis : uint8_t {
|
||||
bold = 1,
|
||||
italic = 1 << 1,
|
||||
underline = 1 << 2,
|
||||
strikethrough = 1 << 3
|
||||
faint = 1 << 1,
|
||||
italic = 1 << 2,
|
||||
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.
|
||||
|
@ -198,7 +210,7 @@ struct rgb {
|
|||
uint8_t b;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
// color is a struct of either a rgb color or a terminal color.
|
||||
struct color_type {
|
||||
|
@ -221,9 +233,10 @@ struct color_type {
|
|||
uint32_t rgb_color;
|
||||
} 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 {
|
||||
public:
|
||||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
|
||||
|
@ -260,33 +273,14 @@ class text_style {
|
|||
return lhs |= rhs;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR text_style& operator&=(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;
|
||||
FMT_DEPRECATED_NONMSVC FMT_CONSTEXPR text_style& operator&=(
|
||||
const text_style& rhs) {
|
||||
return and_assign(rhs);
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR text_style operator&(text_style lhs,
|
||||
const text_style& rhs) {
|
||||
return lhs &= rhs;
|
||||
FMT_DEPRECATED_NONMSVC friend FMT_CONSTEXPR text_style
|
||||
operator&(text_style lhs, const text_style& rhs) {
|
||||
return lhs.and_assign(rhs);
|
||||
}
|
||||
|
||||
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)
|
||||
FMT_NOEXCEPT;
|
||||
|
||||
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
|
||||
FMT_NOEXCEPT;
|
||||
|
||||
|
@ -338,19 +358,22 @@ class text_style {
|
|||
emphasis ems;
|
||||
};
|
||||
|
||||
FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
|
||||
return text_style(/*is_foreground=*/true, foreground);
|
||||
/** Creates a text style from the foreground (text) color. */
|
||||
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 {
|
||||
return text_style(/*is_foreground=*/false, background);
|
||||
/** Creates a text style from the background color. */
|
||||
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;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
template <typename Char> struct ansi_color_escape {
|
||||
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
|
||||
// sequence.
|
||||
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;
|
||||
// Background ASCII codes are the same as the foreground ones but with
|
||||
// 10 more.
|
||||
|
@ -390,16 +413,18 @@ template <typename Char> struct ansi_color_escape {
|
|||
buffer[19] = static_cast<Char>(0);
|
||||
}
|
||||
FMT_CONSTEXPR ansi_color_escape(emphasis em) FMT_NOEXCEPT {
|
||||
uint8_t em_codes[4] = {};
|
||||
uint8_t em_bits = static_cast<uint8_t>(em);
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::bold)) em_codes[0] = 1;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::italic)) em_codes[1] = 3;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::underline)) em_codes[2] = 4;
|
||||
if (em_bits & static_cast<uint8_t>(emphasis::strikethrough))
|
||||
em_codes[3] = 9;
|
||||
uint8_t em_codes[num_emphases] = {};
|
||||
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
|
||||
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
|
||||
if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3;
|
||||
if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4;
|
||||
if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5;
|
||||
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;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
for (size_t i = 0; i < num_emphases; ++i) {
|
||||
if (!em_codes[i]) continue;
|
||||
buffer[index++] = static_cast<Char>('\x1b');
|
||||
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 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);
|
||||
}
|
||||
|
||||
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,
|
||||
char delimiter) FMT_NOEXCEPT {
|
||||
|
@ -425,18 +451,22 @@ template <typename Char> struct ansi_color_escape {
|
|||
out[2] = static_cast<Char>('0' + c % 10);
|
||||
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>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
||||
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>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
||||
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>
|
||||
|
@ -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 {
|
||||
fputs(detail::data::reset_color, stream);
|
||||
fputs("\x1b[0m", stream);
|
||||
}
|
||||
|
||||
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>
|
||||
inline void reset_color(buffer<Char>& buffer) FMT_NOEXCEPT {
|
||||
const char* begin = data::reset_color;
|
||||
const char* end = begin + sizeof(data::reset_color) - 1;
|
||||
buffer.append(begin, end);
|
||||
auto reset_color = string_view("\x1b[0m");
|
||||
buffer.append(reset_color.begin(), reset_color.end());
|
||||
}
|
||||
|
||||
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());
|
||||
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);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
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
|
||||
specify text formatting.
|
||||
Example:
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
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>>
|
||||
inline std::basic_string<Char> format(const text_style& ts, const S& format_str,
|
||||
const Args&... args) {
|
||||
return vformat(ts, to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
return fmt::vformat(ts, to_string_view(format_str),
|
||||
fmt::make_args_checked<Args...>(format_str, args...));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -571,7 +605,7 @@ template <typename OutputIt, typename Char,
|
|||
OutputIt vformat_to(
|
||||
OutputIt out, const text_style& ts, basic_string_view<Char> format_str,
|
||||
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);
|
||||
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_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_COLOR_H_
|
||||
|
|
|
@ -8,13 +8,135 @@
|
|||
#ifndef FMT_COMPILE_H_
|
||||
#define FMT_COMPILE_H_
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
|
||||
// An output iterator that counts the number of objects written to it and
|
||||
// discards them.
|
||||
class counting_iterator {
|
||||
private:
|
||||
size_t count_;
|
||||
|
||||
public:
|
||||
using iterator_category = std::output_iterator_tag;
|
||||
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&) {}
|
||||
};
|
||||
|
||||
counting_iterator() : count_(0) {}
|
||||
|
||||
size_t count() const { return count_; }
|
||||
|
||||
counting_iterator& operator++() {
|
||||
++count_;
|
||||
return *this;
|
||||
}
|
||||
counting_iterator operator++(int) {
|
||||
auto it = *this;
|
||||
++*this;
|
||||
return it;
|
||||
}
|
||||
|
||||
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, typename InputIt>
|
||||
inline counting_iterator copy_str(InputIt begin, InputIt end,
|
||||
counting_iterator it) {
|
||||
return it + (end - begin);
|
||||
}
|
||||
|
||||
template <typename OutputIt> class truncating_iterator_base {
|
||||
protected:
|
||||
OutputIt out_;
|
||||
size_t limit_;
|
||||
size_t count_ = 0;
|
||||
|
||||
truncating_iterator_base() : out_(), limit_(0) {}
|
||||
|
||||
truncating_iterator_base(OutputIt out, size_t limit)
|
||||
: out_(out), limit_(limit) {}
|
||||
|
||||
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_; }
|
||||
};
|
||||
|
||||
// An output iterator that truncates the output and counts the number of objects
|
||||
// written to it.
|
||||
template <typename OutputIt,
|
||||
typename Enable = typename std::is_void<
|
||||
typename std::iterator_traits<OutputIt>::value_type>::type>
|
||||
class truncating_iterator;
|
||||
|
||||
template <typename OutputIt>
|
||||
class truncating_iterator<OutputIt, std::false_type>
|
||||
: public truncating_iterator_base<OutputIt> {
|
||||
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
|
||||
|
||||
public:
|
||||
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
|
||||
|
||||
truncating_iterator() = default;
|
||||
|
||||
truncating_iterator(OutputIt out, size_t limit)
|
||||
: truncating_iterator_base<OutputIt>(out, limit) {}
|
||||
|
||||
truncating_iterator& operator++() {
|
||||
if (this->count_++ < this->limit_) ++this->out_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
truncating_iterator operator++(int) {
|
||||
auto it = *this;
|
||||
++*this;
|
||||
return it;
|
||||
}
|
||||
|
||||
value_type& operator*() const {
|
||||
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename OutputIt>
|
||||
class truncating_iterator<OutputIt, std::true_type>
|
||||
: public truncating_iterator_base<OutputIt> {
|
||||
public:
|
||||
truncating_iterator() = default;
|
||||
|
||||
truncating_iterator(OutputIt out, size_t limit)
|
||||
: truncating_iterator_base<OutputIt>(out, limit) {}
|
||||
|
||||
template <typename T> truncating_iterator& operator=(T val) {
|
||||
if (this->count_++ < this->limit_) *this->out_++ = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
truncating_iterator& operator++() { return *this; }
|
||||
truncating_iterator& operator++(int) { return *this; }
|
||||
truncating_iterator& operator*() { return *this; }
|
||||
};
|
||||
|
||||
// A compile-time string which is compiled into fast formatting code.
|
||||
class compiled_string {};
|
||||
|
||||
|
@ -34,336 +156,30 @@ struct is_compiled_string : std::is_base_of<compiled_string, S> {};
|
|||
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
|
||||
\endrst
|
||||
*/
|
||||
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string)
|
||||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
# define FMT_COMPILE(s) \
|
||||
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
|
||||
#else
|
||||
# define FMT_COMPILE(s) FMT_STRING(s)
|
||||
#endif
|
||||
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_PARAMETERS
|
||||
template <typename Char, size_t N,
|
||||
fmt::detail_exported::fixed_string<Char, N> Str>
|
||||
struct udl_compiled_string : compiled_string {
|
||||
using char_type = Char;
|
||||
constexpr operator basic_string_view<char_type>() const {
|
||||
return {Str.data, N - 1};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
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 {
|
||||
arg_ref<Char> arg_id;
|
||||
dynamic_format_specs<Char> specs;
|
||||
};
|
||||
|
||||
kind part_kind;
|
||||
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 = {})
|
||||
: part_kind(k), val(v) {}
|
||||
|
||||
static FMT_CONSTEXPR format_part make_arg_index(int index) {
|
||||
return format_part(kind::arg_index, index);
|
||||
}
|
||||
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) {
|
||||
return format_part(kind::text, text);
|
||||
}
|
||||
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
|
||||
return format_part(kind::replacement, repl);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char> struct part_counter {
|
||||
unsigned num_parts = 0;
|
||||
|
||||
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
|
||||
if (begin != end) ++num_parts;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 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*) {}
|
||||
|
||||
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*) {}
|
||||
};
|
||||
|
||||
// Counts the number of parts in a format string.
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
|
||||
part_counter<Char> counter;
|
||||
parse_format_string<true>(format_str, counter);
|
||||
return counter.num_parts;
|
||||
}
|
||||
|
||||
template <typename Char, typename PartHandler>
|
||||
class format_string_compiler : public error_handler {
|
||||
private:
|
||||
using part = format_part<Char>;
|
||||
|
||||
PartHandler handler_;
|
||||
part part_;
|
||||
basic_string_view<Char> format_str_;
|
||||
basic_format_parse_context<Char> parse_context_;
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
|
||||
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() {
|
||||
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) {
|
||||
part_ = part::make_arg_name(id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
|
||||
part_.arg_id_end = ptr;
|
||||
handler_(part_);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
|
||||
const Char* end) {
|
||||
auto repl = typename part::replacement();
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
// 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>
|
||||
void format_arg(
|
||||
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>
|
||||
struct compiled_format_base : basic_compiled_format {
|
||||
using char_type = char_t<S>;
|
||||
using parts_container = std::vector<detail::format_part<char_type>>;
|
||||
|
||||
parts_container compiled_parts;
|
||||
|
||||
explicit compiled_format_base(basic_string_view<char_type> format_str) {
|
||||
compile_format_string<false>(format_str,
|
||||
[this](const format_part<char_type>& part) {
|
||||
compiled_parts.push_back(part);
|
||||
});
|
||||
}
|
||||
|
||||
const parts_container& parts() const { return compiled_parts; }
|
||||
};
|
||||
|
||||
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>
|
||||
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
|
||||
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>
|
||||
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
|
||||
: basic_compiled_format {
|
||||
using char_type = char_t<S>;
|
||||
|
||||
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
|
||||
|
||||
// Workaround for old compilers. Format string compilation will not be
|
||||
// performed there anyway.
|
||||
#if FMT_USE_CONSTEXPR
|
||||
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
|
||||
constexpr_max(count_parts(to_string_view(S())), 1u);
|
||||
#else
|
||||
static const unsigned num_format_parts = 1;
|
||||
#endif
|
||||
|
||||
using parts_container = format_part<char_type>[num_format_parts];
|
||||
|
||||
const parts_container& parts() const {
|
||||
static FMT_CONSTEXPR_DECL const auto compiled_parts =
|
||||
compile_to_parts<char_type, num_format_parts>(
|
||||
detail::to_string_view(S()));
|
||||
return compiled_parts.data;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename S, typename... Args>
|
||||
class compiled_format : private compiled_format_base<S> {
|
||||
public:
|
||||
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 {};
|
||||
|
||||
// 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)
|
||||
return first;
|
||||
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... 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>
|
||||
|
@ -393,7 +216,7 @@ template <typename Char> struct text {
|
|||
using char_type = Char;
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
@ -412,11 +235,22 @@ template <typename Char> struct code_unit {
|
|||
using char_type = Char;
|
||||
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
// 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>
|
||||
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;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
OutputIt format(OutputIt out, const Args&... args) const {
|
||||
// This ensures that the argument type is convertile to `const T&`.
|
||||
const T& arg = get<N>(args...);
|
||||
return write<Char>(out, arg);
|
||||
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||
return write<Char>(out, get_arg_checked<T, N>(args...));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename T, int N>
|
||||
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.
|
||||
template <typename Char, typename T, int N> struct spec_field {
|
||||
using char_type = Char;
|
||||
mutable formatter<T, Char> fmt;
|
||||
formatter<T, Char> fmt;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
OutputIt format(OutputIt out, const Args&... args) const {
|
||||
// This ensures that the argument type is convertile to `const T&`.
|
||||
const T& arg = get<N>(args...);
|
||||
constexpr FMT_INLINE OutputIt format(OutputIt out,
|
||||
const Args&... args) const {
|
||||
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);
|
||||
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;
|
||||
|
||||
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...);
|
||||
return rhs.format(out, args...);
|
||||
}
|
||||
|
@ -508,14 +371,79 @@ template <typename T, typename Char> struct parse_specs_result {
|
|||
int next_arg_id;
|
||||
};
|
||||
|
||||
constexpr int manual_indexing_id = -1;
|
||||
|
||||
template <typename T, typename Char>
|
||||
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);
|
||||
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 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
|
||||
|
@ -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>
|
||||
constexpr auto compile_format_string(S format_str) {
|
||||
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 (POS + 1 == str.size())
|
||||
throw format_error("unmatched '{' in format string");
|
||||
if constexpr (POS + 1 == str.size())
|
||||
FMT_THROW(format_error("unmatched '{' in format string"));
|
||||
if constexpr (str[POS + 1] == '{') {
|
||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||
} else if constexpr (str[POS + 1] == '}') {
|
||||
using type = get_type<ID, Args>;
|
||||
return parse_tail<Args, POS + 2, ID + 1>(field<char_type, type, ID>(),
|
||||
format_str);
|
||||
} else if constexpr (str[POS + 1] == ':') {
|
||||
using type = get_type<ID, Args>;
|
||||
constexpr auto result = parse_specs<type>(str, POS + 2, ID);
|
||||
return parse_tail<Args, result.end, result.next_arg_id>(
|
||||
spec_field<char_type, type, ID>{result.fmt}, format_str);
|
||||
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
|
||||
static_assert(ID != manual_indexing_id,
|
||||
"cannot switch from manual to automatic argument indexing");
|
||||
constexpr auto next_id =
|
||||
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
|
||||
return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
|
||||
POS + 1, ID, next_id>(
|
||||
format_str);
|
||||
} 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] == '}') {
|
||||
if (POS + 1 == str.size())
|
||||
throw format_error("unmatched '}' in format string");
|
||||
if constexpr (POS + 1 == str.size())
|
||||
FMT_THROW(format_error("unmatched '}' in format string"));
|
||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||
} else {
|
||||
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,
|
||||
FMT_ENABLE_IF(is_compile_string<S>::value ||
|
||||
detail::is_compiled_string<S>::value)>
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
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) {
|
||||
return detail::make_text(str, 0, 0);
|
||||
} else {
|
||||
constexpr auto result =
|
||||
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
|
||||
format_str);
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(result)>,
|
||||
detail::unknown_format>()) {
|
||||
return detail::compiled_format<S, Args...>(to_string_view(format_str));
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#else
|
||||
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));
|
||||
}
|
||||
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
} // namespace detail
|
||||
|
||||
// DEPRECATED! use FMT_COMPILE instead.
|
||||
template <typename... Args>
|
||||
FMT_DEPRECATED auto compile(const Args&... args)
|
||||
-> decltype(detail::compile(args...)) {
|
||||
return detail::compile(args...);
|
||||
}
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
#if FMT_USE_CONSTEXPR
|
||||
# ifdef __cpp_if_constexpr
|
||||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
|
||||
template <typename CompiledFormat, typename... Args,
|
||||
typename Char = typename CompiledFormat::char_type,
|
||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
cf.format(detail::buffer_appender<Char>(buffer), args...);
|
||||
return to_string(buffer);
|
||||
auto s = std::basic_string<Char>();
|
||||
cf.format(std::back_inserter(s), args...);
|
||||
return s;
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||
const Args&... 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,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_INLINE std::basic_string<typename S::char_type> format(const S&,
|
||||
Args&&... args) {
|
||||
#ifdef __cpp_if_constexpr
|
||||
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
||||
constexpr basic_string_view<typename S::char_type> str = S();
|
||||
if (str.size() == 2 && str[0] == '{' && str[1] == '}')
|
||||
return fmt::to_string(detail::first(args...));
|
||||
constexpr auto str = basic_string_view<typename S::char_type>(S());
|
||||
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
constexpr auto compiled = detail::compile<Args...>(S());
|
||||
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()),
|
||||
std::forward<Args>(args)...);
|
||||
} else {
|
||||
return format(compiled, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
|
||||
constexpr auto compiled = detail::compile<Args...>(S());
|
||||
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
|
||||
constexpr auto compiled = detail::compile<Args...>(S());
|
||||
return format(compiled, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
|
||||
CompiledFormat>::value)>
|
||||
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,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
OutputIt format_to(OutputIt out, const S&, const Args&... args) {
|
||||
constexpr auto compiled = detail::compile<Args...>(S());
|
||||
return format_to(out, compiled, args...);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename CompiledFormat, typename... Args>
|
||||
auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf,
|
||||
const Args&... args) ->
|
||||
typename std::enable_if<
|
||||
detail::is_output_iterator<OutputIt,
|
||||
typename CompiledFormat::char_type>::value &&
|
||||
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...);
|
||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
||||
const S& format_str, Args&&... args) {
|
||||
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), format_str,
|
||||
std::forward<Args>(args)...);
|
||||
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)>
|
||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n, const S&,
|
||||
const Args&... args) {
|
||||
constexpr auto compiled = detail::compile<Args...>(S());
|
||||
auto it = format_to(detail::truncating_iterator<OutputIt>(out, n), compiled,
|
||||
args...);
|
||||
return {it.base(), it.count()};
|
||||
size_t formatted_size(const S& format_str, const Args&... args) {
|
||||
return format_to(detail::counting_iterator(), format_str, args...).count();
|
||||
}
|
||||
|
||||
template <typename CompiledFormat, typename... Args>
|
||||
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
|
||||
return format_to(detail::counting_iterator(), cf, args...).count();
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
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
|
||||
|
||||
#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
|
||||
//
|
||||
// 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_
|
||||
#include "xchar.h"
|
||||
#warning fmt/locale.h is deprecated, include fmt/format.h or fmt/xchar.h instead
|
||||
|
|
|
@ -8,16 +8,12 @@
|
|||
#ifndef 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 <clocale> // for locale_t
|
||||
#include <clocale> // locale_t
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <cstdlib> // for strtod_l
|
||||
#include <cstdlib> // strtod_l
|
||||
#include <system_error> // std::system_error
|
||||
|
||||
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||
|
@ -25,17 +21,20 @@
|
|||
|
||||
#include "format.h"
|
||||
|
||||
#ifndef FMT_USE_FCNTL
|
||||
// UWP doesn't provide _pipe.
|
||||
#if FMT_HAS_INCLUDE("winapifamily.h")
|
||||
# include <winapifamily.h>
|
||||
#endif
|
||||
#if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
||||
defined(__linux__)) && \
|
||||
(!defined(WINAPI_FAMILY) || (WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||
# include <fcntl.h> // for O_RDONLY
|
||||
# define FMT_USE_FCNTL 1
|
||||
#else
|
||||
# define FMT_USE_FCNTL 0
|
||||
# if FMT_HAS_INCLUDE("winapifamily.h")
|
||||
# include <winapifamily.h>
|
||||
# endif
|
||||
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
||||
defined(__linux__)) && \
|
||||
(!defined(WINAPI_FAMILY) || \
|
||||
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||
# include <fcntl.h> // for O_RDONLY
|
||||
# define FMT_USE_FCNTL 1
|
||||
# else
|
||||
# define FMT_USE_FCNTL 0
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef FMT_POSIX
|
||||
|
@ -74,6 +73,7 @@
|
|||
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
/**
|
||||
\rst
|
||||
|
@ -122,19 +122,28 @@ template <typename Char> class basic_cstring_view {
|
|||
using cstring_view = basic_cstring_view<char>;
|
||||
using wcstring_view = basic_cstring_view<wchar_t>;
|
||||
|
||||
// An error code.
|
||||
class error_code {
|
||||
private:
|
||||
int value_;
|
||||
template <typename Char> struct formatter<std::error_code, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
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
|
||||
namespace detail {
|
||||
FMT_API const std::error_category& system_category() FMT_NOEXCEPT;
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
// A converter from UTF-16 to UTF-8.
|
||||
// It is only provided for Windows since other systems support UTF-8 natively.
|
||||
class utf16_to_utf8 {
|
||||
|
@ -143,7 +152,7 @@ class utf16_to_utf8 {
|
|||
|
||||
public:
|
||||
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()); }
|
||||
size_t size() const { return buffer_.size() - 1; }
|
||||
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
|
||||
// throwing exception on conversion error. This method may still throw
|
||||
// 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,
|
||||
string_view message) FMT_NOEXCEPT;
|
||||
} // namespace detail
|
||||
const char* message) FMT_NOEXCEPT;
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
/** A Windows error. */
|
||||
class windows_error : public system_error {
|
||||
private:
|
||||
FMT_API void init(int error_code, string_view format_str, format_args args);
|
||||
FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
|
||||
format_args args);
|
||||
|
||||
public:
|
||||
/**
|
||||
\rst
|
||||
Constructs a :class:`fmt::windows_error` object with the description
|
||||
of the form
|
||||
/**
|
||||
\rst
|
||||
Constructs a :class:`std::system_error` object with the description
|
||||
of the form
|
||||
|
||||
.. parsed-literal::
|
||||
*<message>*: *<system-message>*
|
||||
.. parsed-literal::
|
||||
*<message>*: *<system-message>*
|
||||
|
||||
where *<message>* is the formatted message and *<system-message>* is the
|
||||
system message corresponding to the error code.
|
||||
*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
|
||||
will look like "error -1".
|
||||
where *<message>* is the formatted message and *<system-message>* is the
|
||||
system message corresponding to the error code.
|
||||
*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
|
||||
will look like "error -1".
|
||||
|
||||
**Example**::
|
||||
**Example**::
|
||||
|
||||
// This throws a windows_error with the description
|
||||
// cannot open file 'madeup': The system cannot find the file specified.
|
||||
// or similar (system message may vary).
|
||||
const char *filename = "madeup";
|
||||
LPOFSTRUCT of = LPOFSTRUCT();
|
||||
HFILE file = OpenFile(filename, &of, OF_READ);
|
||||
if (file == HFILE_ERROR) {
|
||||
throw fmt::windows_error(GetLastError(),
|
||||
"cannot open file '{}'", filename);
|
||||
}
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
windows_error(int error_code, string_view message, const Args&... args) {
|
||||
init(error_code, message, make_format_args(args...));
|
||||
}
|
||||
};
|
||||
// This throws a system_error with the description
|
||||
// cannot open file 'madeup': The system cannot find the file specified.
|
||||
// or similar (system message may vary).
|
||||
const char *filename = "madeup";
|
||||
LPOFSTRUCT of = LPOFSTRUCT();
|
||||
HFILE file = OpenFile(filename, &of, OF_READ);
|
||||
if (file == HFILE_ERROR) {
|
||||
throw fmt::windows_error(GetLastError(),
|
||||
"cannot open file '{}'", filename);
|
||||
}
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
std::system_error windows_error(int error_code, string_view message,
|
||||
const Args&... args) {
|
||||
return vwindows_error(error_code, message, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
// Reports a Windows error without throwing an exception.
|
||||
// Can be used to report errors from destructors.
|
||||
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
|
||||
|
||||
// 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.
|
||||
class buffered_file {
|
||||
private:
|
||||
|
@ -255,7 +273,7 @@ class buffered_file {
|
|||
|
||||
template <typename... 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.
|
||||
RDWR = FMT_POSIX(O_RDWR), // Open for reading and writing.
|
||||
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.
|
||||
|
@ -295,7 +314,8 @@ class file {
|
|||
|
||||
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();
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
|
@ -331,7 +351,7 @@ class file {
|
|||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// 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
|
||||
// and writing respectively.
|
||||
|
@ -345,9 +365,10 @@ class file {
|
|||
// Returns the memory page size.
|
||||
long getpagesize();
|
||||
|
||||
namespace detail {
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
struct buffer_size {
|
||||
buffer_size() = default;
|
||||
size_t value = 0;
|
||||
buffer_size operator=(size_t val) const {
|
||||
auto bs = buffer_size();
|
||||
|
@ -357,14 +378,14 @@ struct buffer_size {
|
|||
};
|
||||
|
||||
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;
|
||||
|
||||
ostream_params() {}
|
||||
|
||||
template <typename... T>
|
||||
ostream_params(T... params, int oflag) : ostream_params(params...) {
|
||||
this->oflag = oflag;
|
||||
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
|
||||
oflag = new_oflag;
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
|
@ -372,23 +393,27 @@ struct ostream_params {
|
|||
: ostream_params(params...) {
|
||||
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
|
||||
|
||||
// A fast output stream which is not thread-safe.
|
||||
class ostream final : private detail::buffer<char> {
|
||||
// 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. */
|
||||
class FMT_API ostream final : private detail::buffer<char> {
|
||||
private:
|
||||
file file_;
|
||||
|
||||
void flush() {
|
||||
if (size() == 0) return;
|
||||
file_.write(data(), size());
|
||||
clear();
|
||||
}
|
||||
|
||||
FMT_API void grow(size_t) override final;
|
||||
void grow(size_t) override;
|
||||
|
||||
ostream(cstring_view path, const detail::ostream_params& params)
|
||||
: file_(path, params.oflag) {
|
||||
|
@ -399,6 +424,7 @@ class ostream final : private detail::buffer<char> {
|
|||
ostream(ostream&& other)
|
||||
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
|
||||
file_(std::move(other.file_)) {
|
||||
other.clear();
|
||||
other.set(nullptr, 0);
|
||||
}
|
||||
~ostream() {
|
||||
|
@ -406,6 +432,12 @@ class ostream final : private detail::buffer<char> {
|
|||
delete[] data();
|
||||
}
|
||||
|
||||
void flush() {
|
||||
if (size() == 0) return;
|
||||
file_.write(data(), size());
|
||||
clear();
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
friend ostream output_file(cstring_view path, T... params);
|
||||
|
||||
|
@ -414,16 +446,30 @@ class ostream final : private detail::buffer<char> {
|
|||
file_.close();
|
||||
}
|
||||
|
||||
template <typename S, typename... Args>
|
||||
void print(const S& format_str, const Args&... args) {
|
||||
format_to(detail::buffer_appender<char>(*this), format_str, args...);
|
||||
/**
|
||||
Formats ``args`` according to specifications in ``fmt`` and writes the
|
||||
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`:
|
||||
* ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default)
|
||||
\rst
|
||||
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
|
||||
|
||||
**Example**::
|
||||
|
||||
auto out = fmt::output_file("guide.txt");
|
||||
out.print("Don't {}", "Panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
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
|
||||
// of the parsed input.
|
||||
double strtod(const char*& str) const {
|
||||
FMT_DEPRECATED double strtod(const char*& str) const {
|
||||
char* end = nullptr;
|
||||
double result = strtod_l(str, &end, locale_);
|
||||
str = end;
|
||||
|
@ -475,6 +521,7 @@ class locale {
|
|||
};
|
||||
using Locale FMT_DEPRECATED_ALIAS = locale;
|
||||
#endif // FMT_LOCALE
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_OS_H_
|
||||
|
|
|
@ -14,81 +14,44 @@
|
|||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
template <typename Char> class basic_printf_parse_context;
|
||||
template <typename OutputIt, typename Char> class basic_printf_context;
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <class Char> class formatbuf : public std::basic_streambuf<Char> {
|
||||
private:
|
||||
using int_type = typename std::basic_streambuf<Char>::int_type;
|
||||
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 {
|
||||
// Checks if T has a user-defined operator<<.
|
||||
template <typename T, typename Char, typename Enable = void>
|
||||
class is_streamable {
|
||||
private:
|
||||
template <typename U>
|
||||
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
|
||||
<< std::declval<U>()),
|
||||
void_t<>>::value>
|
||||
test(int);
|
||||
static auto test(int)
|
||||
-> bool_constant<sizeof(std::declval<std::basic_ostream<Char>&>()
|
||||
<< std::declval<U>()) != 0>;
|
||||
|
||||
template <typename> static std::false_type test(...);
|
||||
template <typename> static auto test(...) -> std::false_type;
|
||||
|
||||
using result = decltype(test<T>(0));
|
||||
|
||||
public:
|
||||
is_streamable() = default;
|
||||
|
||||
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.
|
||||
// It is a separate function rather than a part of vprint to simplify testing.
|
||||
template <typename Char>
|
||||
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||
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>
|
||||
void format_value(buffer<Char>& buf, const T& value,
|
||||
locale_ref loc = locale_ref()) {
|
||||
formatbuf<Char> format_buf(buf);
|
||||
std::basic_ostream<Char> output(&format_buf);
|
||||
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
|
||||
auto&& output = std::basic_ostream<Char>(&format_buf);
|
||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||
if (loc) output.imbue(loc.get<std::locale>());
|
||||
#endif
|
||||
|
@ -120,39 +83,33 @@ void format_value(buffer<Char>& buf, const T& value,
|
|||
template <typename T, typename Char>
|
||||
struct fallback_formatter<T, Char, enable_if_t<is_streamable<T, Char>::value>>
|
||||
: private formatter<basic_string_view<Char>, Char> {
|
||||
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
|
||||
-> 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();
|
||||
}
|
||||
using formatter<basic_string_view<Char>, Char>::parse;
|
||||
|
||||
template <typename OutputIt>
|
||||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
|
||||
-> OutputIt {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
auto buffer = basic_memory_buffer<Char>();
|
||||
format_value(buffer, value, ctx.locale());
|
||||
basic_string_view<Char> str(buffer.data(), buffer.size());
|
||||
return formatter<basic_string_view<Char>, Char>::format(str, ctx);
|
||||
return formatter<basic_string_view<Char>, Char>::format(
|
||||
{buffer.data(), buffer.size()}, ctx);
|
||||
}
|
||||
|
||||
// DEPRECATED!
|
||||
template <typename OutputIt>
|
||||
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
|
||||
-> OutputIt {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
auto buffer = basic_memory_buffer<Char>();
|
||||
format_value(buffer, value, ctx.locale());
|
||||
return std::copy(buffer.begin(), buffer.end(), ctx.out());
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
FMT_MODULE_EXPORT
|
||||
template <typename Char>
|
||||
void vprint(std::basic_ostream<Char>& os, basic_string_view<Char> format_str,
|
||||
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::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");
|
||||
\endrst
|
||||
*/
|
||||
FMT_MODULE_EXPORT
|
||||
template <typename S, typename... Args,
|
||||
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) {
|
||||
|
|
|
@ -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 <limits> // std::numeric_limits
|
||||
#include <ostream>
|
||||
|
||||
#include "ostream.h"
|
||||
#include "format.h"
|
||||
|
||||
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
|
||||
// signed and unsigned integers.
|
||||
|
@ -178,81 +221,38 @@ template <typename Char> class printf_width_handler {
|
|||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename Context>
|
||||
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
|
||||
*/
|
||||
// The ``printf`` argument formatter.
|
||||
template <typename OutputIt, typename Char>
|
||||
class printf_arg_formatter : public detail::arg_formatter_base<OutputIt, Char> {
|
||||
public:
|
||||
using iterator = OutputIt;
|
||||
|
||||
class printf_arg_formatter : public arg_formatter<Char> {
|
||||
private:
|
||||
using char_type = Char;
|
||||
using base = detail::arg_formatter_base<OutputIt, Char>;
|
||||
using base = arg_formatter<Char>;
|
||||
using context_type = basic_printf_context<OutputIt, Char>;
|
||||
using format_specs = basic_format_specs<Char>;
|
||||
|
||||
context_type& context_;
|
||||
|
||||
void write_null_pointer(char) {
|
||||
this->specs()->type = 0;
|
||||
this->write("(nil)");
|
||||
}
|
||||
|
||||
void write_null_pointer(wchar_t) {
|
||||
this->specs()->type = 0;
|
||||
this->write(L"(nil)");
|
||||
OutputIt write_null_pointer(bool is_string = false) {
|
||||
auto s = this->specs;
|
||||
s.type = presentation_type::none;
|
||||
return write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
|
||||
}
|
||||
|
||||
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
|
||||
Constructs an argument formatter object.
|
||||
*buffer* is a reference to the output buffer and *specs* contains format
|
||||
specifier information for standard argument types.
|
||||
\endrst
|
||||
*/
|
||||
printf_arg_formatter(iterator iter, format_specs& specs, context_type& ctx)
|
||||
: base(iter, &specs, detail::locale_ref()), context_(ctx) {}
|
||||
OutputIt operator()(monostate value) { return base::operator()(value); }
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(fmt::detail::is_integral<T>::value)>
|
||||
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')
|
||||
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
|
||||
OutputIt operator()(T value) {
|
||||
// MSVC2013 fails to compile separate overloads for bool and Char so use
|
||||
// std::is_same instead.
|
||||
if (std::is_same<T, Char>::value) {
|
||||
format_specs fmt_specs = this->specs;
|
||||
if (fmt_specs.type != presentation_type::none &&
|
||||
fmt_specs.type != presentation_type::chr) {
|
||||
return (*this)(static_cast<int>(value));
|
||||
}
|
||||
fmt_specs.sign = sign::none;
|
||||
fmt_specs.alt = false;
|
||||
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
|
||||
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
|
||||
fmt_specs.align = align::right;
|
||||
return base::operator()(value);
|
||||
} else {
|
||||
return base::operator()(value);
|
||||
return write<Char>(this->out, static_cast<Char>(value), fmt_specs);
|
||||
}
|
||||
return this->out();
|
||||
return base::operator()(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);
|
||||
}
|
||||
|
||||
/** Formats a null-terminated C string. */
|
||||
iterator operator()(const char* value) {
|
||||
if (value)
|
||||
base::operator()(value);
|
||||
else if (this->specs()->type == 'p')
|
||||
write_null_pointer(char_type());
|
||||
else
|
||||
this->write("(null)");
|
||||
return this->out();
|
||||
OutputIt operator()(const char* value) {
|
||||
if (value) return base::operator()(value);
|
||||
return write_null_pointer(this->specs.type != presentation_type::pointer);
|
||||
}
|
||||
|
||||
/** Formats a null-terminated wide C string. */
|
||||
iterator operator()(const wchar_t* value) {
|
||||
if (value)
|
||||
base::operator()(value);
|
||||
else if (this->specs()->type == 'p')
|
||||
write_null_pointer(char_type());
|
||||
else
|
||||
this->write(L"(null)");
|
||||
return this->out();
|
||||
OutputIt operator()(const wchar_t* value) {
|
||||
if (value) return base::operator()(value);
|
||||
return write_null_pointer(this->specs.type != presentation_type::pointer);
|
||||
}
|
||||
|
||||
iterator operator()(basic_string_view<char_type> value) {
|
||||
OutputIt operator()(basic_string_view<Char> value) {
|
||||
return base::operator()(value);
|
||||
}
|
||||
|
||||
iterator operator()(monostate value) { return base::operator()(value); }
|
||||
|
||||
/** Formats a pointer. */
|
||||
iterator operator()(const void* value) {
|
||||
if (value) return base::operator()(value);
|
||||
this->specs()->type = 0;
|
||||
write_null_pointer(char_type());
|
||||
return this->out();
|
||||
OutputIt operator()(const void* value) {
|
||||
return value ? base::operator()(value) : write_null_pointer();
|
||||
}
|
||||
|
||||
/** Formats an argument of a custom (user-defined) type. */
|
||||
iterator operator()(typename basic_format_arg<context_type>::handle handle) {
|
||||
handle.format(context_.parse_context(), context_);
|
||||
return this->out();
|
||||
OutputIt operator()(typename basic_format_arg<context_type>::handle handle) {
|
||||
auto parse_ctx =
|
||||
basic_printf_parse_context<Char>(basic_string_view<Char>());
|
||||
handle.format(parse_ctx, context_);
|
||||
return this->out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct printf_formatter {
|
||||
printf_formatter() = delete;
|
||||
|
||||
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) {
|
||||
template <typename Char>
|
||||
void parse_flags(basic_format_specs<Char>& specs, const Char*& it,
|
||||
const Char* end) {
|
||||
for (; it != end; ++it) {
|
||||
switch (*it) {
|
||||
case '-':
|
||||
|
@ -417,35 +328,24 @@ void basic_printf_context<OutputIt, Char>::parse_flags(format_specs& specs,
|
|||
}
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char>
|
||||
typename basic_printf_context<OutputIt, Char>::format_arg
|
||||
basic_printf_context<OutputIt, Char>::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(*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) {
|
||||
template <typename Char, typename GetArg>
|
||||
int parse_header(const Char*& it, const Char* end,
|
||||
basic_format_specs<Char>& specs, GetArg get_arg) {
|
||||
int arg_index = -1;
|
||||
char_type c = *it;
|
||||
Char c = *it;
|
||||
if (c >= '0' && c <= '9') {
|
||||
// Parse an argument index (if followed by '$') or a width possibly
|
||||
// preceded with '0' flag(s).
|
||||
detail::error_handler eh;
|
||||
int value = parse_nonnegative_int(it, end, eh);
|
||||
int value = parse_nonnegative_int(it, end, -1);
|
||||
if (it != end && *it == '$') { // value is an argument index
|
||||
++it;
|
||||
arg_index = value;
|
||||
arg_index = value != -1 ? value : max_value<int>();
|
||||
} else {
|
||||
if (c == '0') specs.fill[0] = '0';
|
||||
if (value != 0) {
|
||||
// Nonzero value means that we parsed width and don't need to
|
||||
// parse it or flags again, so return now.
|
||||
if (value == -1) FMT_THROW(format_error("number is too big"));
|
||||
specs.width = value;
|
||||
return arg_index;
|
||||
}
|
||||
|
@ -455,58 +355,76 @@ int basic_printf_context<OutputIt, Char>::parse_header(const Char*& it,
|
|||
// Parse width.
|
||||
if (it != end) {
|
||||
if (*it >= '0' && *it <= '9') {
|
||||
detail::error_handler eh;
|
||||
specs.width = parse_nonnegative_int(it, end, eh);
|
||||
specs.width = parse_nonnegative_int(it, end, -1);
|
||||
if (specs.width == -1) FMT_THROW(format_error("number is too big"));
|
||||
} else if (*it == '*') {
|
||||
++it;
|
||||
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;
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char>
|
||||
template <typename ArgFormatter>
|
||||
OutputIt basic_printf_context<OutputIt, Char>::format() {
|
||||
auto out = this->out();
|
||||
const Char* start = parse_ctx_.begin();
|
||||
const Char* end = parse_ctx_.end();
|
||||
template <typename Char, typename Context>
|
||||
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||
basic_format_args<Context> args) {
|
||||
using OutputIt = buffer_appender<Char>;
|
||||
auto out = OutputIt(buf);
|
||||
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;
|
||||
while (it != end) {
|
||||
char_type c = *it++;
|
||||
if (c != '%') continue;
|
||||
if (!detail::find<false, Char>(it, end, '%', it)) {
|
||||
it = end; // detail::find leaves it == nullptr if it doesn't find '%'
|
||||
break;
|
||||
}
|
||||
Char c = *it++;
|
||||
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;
|
||||
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;
|
||||
|
||||
// Parse argument index, flags and width.
|
||||
int arg_index = parse_header(it, end, specs);
|
||||
if (arg_index == 0) on_error("argument not found");
|
||||
int arg_index = parse_header(it, end, specs, get_arg);
|
||||
if (arg_index == 0) parse_ctx.on_error("argument not found");
|
||||
|
||||
// Parse precision.
|
||||
if (it != end && *it == '.') {
|
||||
++it;
|
||||
c = it != end ? *it : 0;
|
||||
if ('0' <= c && c <= '9') {
|
||||
detail::error_handler eh;
|
||||
specs.precision = parse_nonnegative_int(it, end, eh);
|
||||
specs.precision = parse_nonnegative_int(it, end, 0);
|
||||
} else if (c == '*') {
|
||||
++it;
|
||||
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 {
|
||||
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
|
||||
// specified, the '0' flag is ignored
|
||||
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_end = str + specs.precision;
|
||||
auto nul = std::find(str, str_end, Char());
|
||||
arg = detail::make_arg<basic_printf_context>(basic_string_view<Char>(
|
||||
str,
|
||||
detail::to_unsigned(nul != str_end ? nul - str : specs.precision)));
|
||||
arg = detail::make_arg<basic_printf_context<OutputIt, Char>>(
|
||||
basic_string_view<Char>(
|
||||
str, detail::to_unsigned(nul != str_end ? nul - str
|
||||
: specs.precision)));
|
||||
}
|
||||
if (specs.alt && visit_format_arg(detail::is_zero_int(), arg))
|
||||
specs.alt = false;
|
||||
|
@ -532,7 +451,7 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
|||
|
||||
// Parse length and convert the argument to the required type.
|
||||
c = it != end ? *it++ : 0;
|
||||
char_type t = it != end ? *it : 0;
|
||||
Char t = it != end ? *it : 0;
|
||||
using detail::convert_arg;
|
||||
switch (c) {
|
||||
case 'h':
|
||||
|
@ -573,28 +492,34 @@ OutputIt basic_printf_context<OutputIt, Char>::format() {
|
|||
|
||||
// Parse type.
|
||||
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()) {
|
||||
// Normalize type.
|
||||
switch (specs.type) {
|
||||
switch (type) {
|
||||
case 'i':
|
||||
case 'u':
|
||||
specs.type = 'd';
|
||||
type = 'd';
|
||||
break;
|
||||
case 'c':
|
||||
visit_format_arg(detail::char_converter<basic_printf_context>(arg),
|
||||
arg);
|
||||
visit_format_arg(
|
||||
detail::char_converter<basic_printf_context<OutputIt, Char>>(arg),
|
||||
arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
specs.type = parse_presentation_type(type);
|
||||
if (specs.type == presentation_type::none)
|
||||
parse_ctx.on_error("invalid type specifier");
|
||||
|
||||
start = it;
|
||||
|
||||
// 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>
|
||||
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`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
inline format_arg_store<printf_context, Args...> make_printf_args(
|
||||
const Args&... args) {
|
||||
template <typename... T>
|
||||
inline auto make_printf_args(const T&... args)
|
||||
-> format_arg_store<printf_context, T...> {
|
||||
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`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
inline format_arg_store<wprintf_context, Args...> make_wprintf_args(
|
||||
const Args&... args) {
|
||||
template <typename... T>
|
||||
inline auto make_wprintf_args(const T&... args)
|
||||
-> format_arg_store<wprintf_context, T...> {
|
||||
return {args...};
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline std::basic_string<Char> vsprintf(
|
||||
const S& format,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||
inline auto vsprintf(
|
||||
const S& fmt,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> std::basic_string<Char> {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
vprintf(buffer, to_string_view(format), args);
|
||||
vprintf(buffer, to_string_view(fmt), args);
|
||||
return to_string(buffer);
|
||||
}
|
||||
|
||||
|
@ -648,19 +574,20 @@ inline std::basic_string<Char> vsprintf(
|
|||
std::string message = fmt::sprintf("The answer is %d", 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
template <typename S, typename... T,
|
||||
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>;
|
||||
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>>
|
||||
inline int vfprintf(
|
||||
std::FILE* f, const S& format,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||
inline auto vfprintf(
|
||||
std::FILE* f, const S& fmt,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> int {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
vprintf(buffer, to_string_view(format), args);
|
||||
vprintf(buffer, to_string_view(fmt), args);
|
||||
size_t size = buffer.size();
|
||||
return std::fwrite(buffer.data(), sizeof(Char), size, f) < size
|
||||
? -1
|
||||
|
@ -676,19 +603,19 @@ inline int vfprintf(
|
|||
fmt::fprintf(stderr, "Don't %s!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||
inline int fprintf(std::FILE* f, const S& format, const Args&... args) {
|
||||
template <typename S, typename... T, typename Char = char_t<S>>
|
||||
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
|
||||
using context = basic_printf_context_t<Char>;
|
||||
return vfprintf(f, to_string_view(format),
|
||||
make_format_args<context>(args...));
|
||||
return vfprintf(f, to_string_view(fmt),
|
||||
fmt::make_format_args<context>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline int vprintf(
|
||||
const S& format,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||
return vfprintf(stdout, to_string_view(format), args);
|
||||
inline auto vprintf(
|
||||
const S& fmt,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> 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);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
inline int printf(const S& format_str, const Args&... args) {
|
||||
using context = basic_printf_context_t<char_t<S>>;
|
||||
return vprintf(to_string_view(format_str),
|
||||
make_format_args<context>(args...));
|
||||
template <typename S, typename... T, FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
inline auto printf(const S& fmt, const T&... args) -> int {
|
||||
return vprintf(
|
||||
to_string_view(fmt),
|
||||
fmt::make_format_args<basic_printf_context_t<char_t<S>>>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename Char = char_t<S>>
|
||||
inline int vfprintf(
|
||||
std::basic_ostream<Char>& os, const S& format,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args) {
|
||||
FMT_DEPRECATED auto vfprintf(
|
||||
std::basic_ostream<Char>& os, const S& fmt,
|
||||
basic_format_args<basic_printf_context_t<type_identity_t<Char>>> args)
|
||||
-> int {
|
||||
basic_memory_buffer<Char> buffer;
|
||||
vprintf(buffer, to_string_view(format), args);
|
||||
detail::write_buffer(os, buffer);
|
||||
vprintf(buffer, to_string_view(fmt), args);
|
||||
os.write(buffer.data(), static_cast<std::streamsize>(buffer.size()));
|
||||
return static_cast<int>(buffer.size());
|
||||
}
|
||||
|
||||
/** Formats arguments and writes the output to the range. */
|
||||
template <typename ArgFormatter, typename Char,
|
||||
typename Context =
|
||||
basic_printf_context<typename ArgFormatter::iterator, Char>>
|
||||
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;
|
||||
template <typename S, typename... T, typename Char = char_t<S>>
|
||||
FMT_DEPRECATED auto fprintf(std::basic_ostream<Char>& os, const S& fmt,
|
||||
const T&... args) -> int {
|
||||
return vfprintf(os, to_string_view(fmt),
|
||||
fmt::make_format_args<basic_printf_context_t<Char>>(args...));
|
||||
}
|
||||
|
||||
/**
|
||||
\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_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_PRINTF_H_
|
||||
|
|
|
@ -13,47 +13,13 @@
|
|||
#define FMT_RANGES_H_
|
||||
|
||||
#include <initializer_list>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#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
|
||||
|
||||
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 {
|
||||
|
||||
template <typename RangeT, typename OutputIterator>
|
||||
|
@ -75,8 +41,14 @@ OutputIterator copy(char ch, OutputIterator out) {
|
|||
return out;
|
||||
}
|
||||
|
||||
/// Return true value if T has std::string interface, like std::string_view.
|
||||
template <typename T> class is_like_std_string {
|
||||
template <typename OutputIterator>
|
||||
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>
|
||||
static auto check(U* p)
|
||||
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
||||
|
@ -84,26 +56,118 @@ template <typename T> class is_like_std_string {
|
|||
|
||||
public:
|
||||
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>
|
||||
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 T, typename _ = void> struct is_range_ : std::false_type {};
|
||||
|
||||
#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 is_range_<
|
||||
T, conditional_t<false,
|
||||
conditional_helper<decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end())>,
|
||||
void>> : std::true_type {};
|
||||
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>
|
||||
auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
|
||||
decltype(end(static_cast<T&&>(rng)))> {
|
||||
return end(static_cast<T&&>(rng));
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
/// tuple_size and tuple_element check.
|
||||
// tuple_size and tuple_element check.
|
||||
template <typename T> class is_tuple_like_ {
|
||||
template <typename U>
|
||||
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>
|
||||
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<
|
||||
typename std::decay<Arg>::type>::value)>
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||
return add_space ? " {}" : "{}";
|
||||
template <typename OutputIt> OutputIt write_delimiter(OutputIt out) {
|
||||
*out++ = ',';
|
||||
*out++ = ' ';
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename Arg, FMT_ENABLE_IF(is_like_std_string<
|
||||
typename std::decay<Arg>::type>::value)>
|
||||
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&) {
|
||||
return add_space ? " \"{}\"" : "\"{}\"";
|
||||
struct singleton {
|
||||
unsigned char upper;
|
||||
unsigned char lower_count;
|
||||
};
|
||||
|
||||
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*) {
|
||||
return add_space ? " \"{}\"" : "\"{}\"";
|
||||
}
|
||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
|
||||
return add_space ? L" \"{}\"" : L"\"{}\"";
|
||||
// Returns true iff the code point cp is printable.
|
||||
// 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 char* format_str_quoted(bool add_space, const char) {
|
||||
return add_space ? " '{}'" : "'{}'";
|
||||
inline auto needs_escape(uint32_t cp) -> bool {
|
||||
return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
|
||||
!is_printable(cp);
|
||||
}
|
||||
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
|
||||
return add_space ? L" '{}'" : L"'{}'";
|
||||
|
||||
template <typename Char> struct find_escape_result {
|
||||
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};
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
template <typename T> struct is_tuple_like {
|
||||
|
@ -195,55 +547,37 @@ template <typename T> struct is_tuple_like {
|
|||
template <typename TupleT, typename Char>
|
||||
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
|
||||
private:
|
||||
// C++11 generic lambda for format()
|
||||
// C++11 generic lambda for format().
|
||||
template <typename FormatContext> struct format_each {
|
||||
template <typename T> void operator()(const T& v) {
|
||||
if (i > 0) {
|
||||
if (formatting.add_prepostfix_space) {
|
||||
*out++ = ' ';
|
||||
}
|
||||
out = detail::copy(formatting.delimiter, out);
|
||||
}
|
||||
out = format_to(out,
|
||||
detail::format_str_quoted(
|
||||
(formatting.add_delimiter_spaces && i > 0), v),
|
||||
v);
|
||||
if (i > 0) out = detail::write_delimiter(out);
|
||||
out = detail::write_range_entry<Char>(out, v);
|
||||
++i;
|
||||
}
|
||||
|
||||
formatting_tuple<Char>& formatting;
|
||||
size_t& i;
|
||||
typename std::add_lvalue_reference<decltype(
|
||||
std::declval<FormatContext>().out())>::type out;
|
||||
int i;
|
||||
typename FormatContext::iterator& out;
|
||||
};
|
||||
|
||||
public:
|
||||
formatting_tuple<Char> formatting;
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return formatting.parse(ctx);
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext = format_context>
|
||||
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
size_t i = 0;
|
||||
detail::copy(formatting.prefix, out);
|
||||
|
||||
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
|
||||
if (formatting.add_prepostfix_space) {
|
||||
*out++ = ' ';
|
||||
}
|
||||
detail::copy(formatting.postfix, out);
|
||||
|
||||
return ctx.out();
|
||||
*out++ = '(';
|
||||
detail::for_each(values, format_each<FormatContext>{0, out});
|
||||
*out++ = ')';
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char> struct is_range {
|
||||
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_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>
|
||||
struct formatter<
|
||||
T, Char,
|
||||
enable_if_t<fmt::is_range<T, Char>::value
|
||||
// Workaround a bug in MSVC 2017 and earlier.
|
||||
#if !FMT_MSC_VER || FMT_MSC_VER >= 1927
|
||||
&&
|
||||
(has_formatter<detail::value_type<T>, format_context>::value ||
|
||||
detail::has_fallback_formatter<detail::value_type<T>,
|
||||
format_context>::value)
|
||||
enable_if_t<
|
||||
fmt::is_range<T, Char>::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
|
||||
>> {
|
||||
formatting_range<Char> formatting;
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return formatting.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
|
||||
auto out = detail::copy(formatting.prefix, ctx.out());
|
||||
size_t i = 0;
|
||||
auto it = values.begin();
|
||||
auto end = values.end();
|
||||
for (; it != end; ++it) {
|
||||
if (i > 0) {
|
||||
if (formatting.add_prepostfix_space) *out++ = ' ';
|
||||
out = detail::copy(formatting.delimiter, out);
|
||||
}
|
||||
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++ = ' ';
|
||||
return detail::copy(formatting.postfix, out);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T> struct tuple_arg_join : detail::view {
|
||||
const std::tuple<T...>& tuple;
|
||||
basic_string_view<Char> sep;
|
||||
|
||||
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
|
||||
: tuple{t}, sep{s} {}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T>
|
||||
struct formatter<tuple_arg_join<Char, T...>, Char> {
|
||||
>> {
|
||||
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& 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) {
|
||||
if (i > 0) out = detail::write_delimiter(out);
|
||||
out = detail::write_range_entry<Char>(out, *it);
|
||||
++i;
|
||||
}
|
||||
*out++ = postfix;
|
||||
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_join_view : detail::view {
|
||||
const std::tuple<T...>& tuple;
|
||||
basic_string_view<Char> sep;
|
||||
|
||||
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
|
||||
: tuple(t), sep{s} {}
|
||||
};
|
||||
|
||||
template <typename Char, typename... T>
|
||||
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>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
typename FormatContext::iterator format(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
|
||||
return format(value, ctx, detail::make_index_sequence<sizeof...(T)>{});
|
||||
auto format(const tuple_join_view<Char, T...>& value,
|
||||
FormatContext& ctx) const -> typename FormatContext::iterator {
|
||||
return do_format(value, ctx,
|
||||
std::integral_constant<size_t, sizeof...(T)>());
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename FormatContext, size_t... N>
|
||||
typename FormatContext::iterator format(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
||||
detail::index_sequence<N...>) {
|
||||
return format_args(value, ctx, std::get<N>(value.tuple)...);
|
||||
std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
|
||||
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>
|
||||
typename FormatContext::iterator format_args(
|
||||
const tuple_arg_join<Char, T...>&, FormatContext& ctx) {
|
||||
// NOTE: for compilers that support C++17, this empty function instantiation
|
||||
// can be replaced with a constexpr branch in the variadic overload.
|
||||
auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
|
||||
std::integral_constant<size_t, 0>) const ->
|
||||
typename FormatContext::iterator {
|
||||
return ctx.out();
|
||||
}
|
||||
|
||||
template <typename FormatContext, typename Arg, typename... Args>
|
||||
typename FormatContext::iterator format_args(
|
||||
const tuple_arg_join<Char, T...>& value, FormatContext& ctx,
|
||||
const Arg& arg, const Args&... args) {
|
||||
using base = formatter<typename std::decay<Arg>::type, Char>;
|
||||
auto out = ctx.out();
|
||||
out = base{}.format(arg, ctx);
|
||||
if (sizeof...(Args) > 0) {
|
||||
template <typename FormatContext, size_t N>
|
||||
auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
||||
std::integral_constant<size_t, N>) const ->
|
||||
typename FormatContext::iterator {
|
||||
auto out = std::get<sizeof...(T) - N>(formatters_)
|
||||
.format(std::get<sizeof...(T) - N>(value.tuple), ctx);
|
||||
if (N > 1) {
|
||||
out = std::copy(value.sep.begin(), value.sep.end(), 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;
|
||||
}
|
||||
};
|
||||
|
||||
FMT_MODULE_EXPORT_BEGIN
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns an object that formats `tuple` with elements separated by `sep`.
|
||||
|
@ -357,14 +758,15 @@ struct formatter<tuple_arg_join<Char, T...>, Char> {
|
|||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
|
||||
string_view sep) {
|
||||
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple, string_view sep)
|
||||
-> tuple_join_view<char, T...> {
|
||||
return {tuple, sep};
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
|
||||
wstring_view sep) {
|
||||
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
|
||||
basic_string_view<wchar_t> sep)
|
||||
-> tuple_join_view<wchar_t, T...> {
|
||||
return {tuple, sep};
|
||||
}
|
||||
|
||||
|
@ -380,17 +782,12 @@ FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
|
|||
\endrst
|
||||
*/
|
||||
template <typename T>
|
||||
arg_join<const T*, const T*, char> join(std::initializer_list<T> list,
|
||||
string_view sep) {
|
||||
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) {
|
||||
auto join(std::initializer_list<T> list, string_view sep)
|
||||
-> join_view<const T*, const T*> {
|
||||
return join(std::begin(list), std::end(list), sep);
|
||||
}
|
||||
|
||||
FMT_MODULE_EXPORT_END
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#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
|
||||
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>
|
||||
int format_float(char* buf, std::size_t size, const char* format, int precision,
|
||||
T value) {
|
||||
|
@ -28,35 +74,8 @@ template FMT_API dragonbox::decimal_fp<float> dragonbox::to_decimal(float x)
|
|||
FMT_NOEXCEPT;
|
||||
template FMT_API dragonbox::decimal_fp<double> dragonbox::to_decimal(double x)
|
||||
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
|
||||
|
||||
template struct FMT_INSTANTIATION_DEF_API detail::basic_data<void>;
|
||||
|
||||
// Workaround a bug in MSVC2013 that prevents instantiation of format_float.
|
||||
int (*instantiate_format_float)(double, int, detail::float_specs,
|
||||
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.
|
||||
|
||||
template FMT_API std::string detail::grouping_impl<char>(locale_ref);
|
||||
template FMT_API char detail::thousands_sep_impl(locale_ref);
|
||||
template FMT_API auto detail::thousands_sep_impl(locale_ref)
|
||||
-> thousands_sep_result<char>;
|
||||
template FMT_API char detail::decimal_point_impl(locale_ref);
|
||||
|
||||
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(
|
||||
detail::buffer<char>&, string_view,
|
||||
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.
|
||||
|
||||
template FMT_API std::string detail::grouping_impl<wchar_t>(locale_ref);
|
||||
template FMT_API wchar_t detail::thousands_sep_impl(locale_ref);
|
||||
template FMT_API auto 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 void detail::buffer<wchar_t>::append(const wchar_t*,
|
||||
const wchar_t*);
|
||||
|
||||
template struct detail::basic_data<void>;
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
|
131
src/fmtlib/os.cc
131
src/fmtlib/os.cc
|
@ -25,21 +25,18 @@
|
|||
# define WIN32_LEAN_AND_MEAN
|
||||
# endif
|
||||
# include <io.h>
|
||||
# include <windows.h>
|
||||
|
||||
# define O_CREAT _O_CREAT
|
||||
# define O_TRUNC _O_TRUNC
|
||||
|
||||
# ifndef S_IRUSR
|
||||
# define S_IRUSR _S_IREAD
|
||||
# endif
|
||||
|
||||
# ifndef S_IWUSR
|
||||
# define S_IWUSR _S_IWRITE
|
||||
# endif
|
||||
|
||||
# ifdef __MINGW32__
|
||||
# define _SH_DENYNO 0x40
|
||||
# ifndef S_IRGRP
|
||||
# define S_IRGRP 0
|
||||
# endif
|
||||
# ifndef S_IROTH
|
||||
# define S_IROTH 0
|
||||
# endif
|
||||
# endif // _WIN32
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
@ -55,7 +52,7 @@
|
|||
namespace {
|
||||
#ifdef _WIN32
|
||||
// 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
|
||||
// it from size_t preventing integer overflow.
|
||||
|
@ -64,7 +61,7 @@ inline unsigned convert_rwcount(std::size_t count) {
|
|||
}
|
||||
#elif FMT_USE_FCNTL
|
||||
// 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; }
|
||||
#endif
|
||||
|
@ -73,14 +70,14 @@ inline std::size_t convert_rwcount(std::size_t count) { return count; }
|
|||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
#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)) {
|
||||
FMT_THROW(windows_error(error_code,
|
||||
"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;
|
||||
int s_size = static_cast<int>(s.size());
|
||||
if (s_size == 0) {
|
||||
|
@ -101,46 +98,85 @@ int detail::utf16_to_utf8::convert(wstring_view s) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void windows_error::init(int err_code, string_view format_str,
|
||||
format_args args) {
|
||||
error_code_ = err_code;
|
||||
memory_buffer buffer;
|
||||
detail::format_windows_error(buffer, err_code, vformat(format_str, args));
|
||||
std::runtime_error& base = *this;
|
||||
base = std::runtime_error(to_string(buffer));
|
||||
namespace detail {
|
||||
|
||||
class system_message {
|
||||
system_message(const system_message&) = delete;
|
||||
void operator=(const system_message&) = delete;
|
||||
|
||||
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,
|
||||
string_view message) FMT_NOEXCEPT {
|
||||
const char* message) FMT_NOEXCEPT {
|
||||
FMT_TRY {
|
||||
wmemory_buffer buf;
|
||||
buf.resize(inline_buffer_size);
|
||||
for (;;) {
|
||||
wchar_t* system_message = &buf[0];
|
||||
int result = FormatMessageW(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr,
|
||||
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;
|
||||
system_message msg(error_code);
|
||||
if (msg) {
|
||||
utf16_to_utf8 utf8_message;
|
||||
if (utf8_message.convert(msg) == ERROR_SUCCESS) {
|
||||
format_to(buffer_appender<char>(out), "{}: {}", message, utf8_message);
|
||||
return;
|
||||
}
|
||||
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
||||
break; // Can't get error message, report error code instead.
|
||||
buf.resize(buf.size() * 2);
|
||||
}
|
||||
}
|
||||
FMT_CATCH(...) {}
|
||||
format_error_code(out, error_code, message);
|
||||
}
|
||||
|
||||
void report_windows_error(int error_code,
|
||||
fmt::string_view message) FMT_NOEXCEPT {
|
||||
void report_windows_error(int error_code, const char* message) FMT_NOEXCEPT {
|
||||
report_error(detail::format_windows_error, error_code, message);
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
@ -175,7 +211,10 @@ int buffered_file::fileno() const {
|
|||
|
||||
#if FMT_USE_FCNTL
|
||||
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__)
|
||||
fd_ = -1;
|
||||
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) {
|
||||
RWResult result = 0;
|
||||
rwresult result = 0;
|
||||
FMT_RETRY(result, FMT_POSIX_CALL(read(fd_, buffer, convert_rwcount(count))));
|
||||
if (result < 0) FMT_THROW(system_error(errno, "cannot read from file"));
|
||||
return detail::to_unsigned(result);
|
||||
}
|
||||
|
||||
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))));
|
||||
if (result < 0) FMT_THROW(system_error(errno, "cannot write to file"));
|
||||
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;
|
||||
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) {
|
||||
|
|
|
@ -36,16 +36,15 @@
|
|||
#include "base/string_util.hh"
|
||||
#include "config.h"
|
||||
#include "fmt/format.h"
|
||||
#include "fmt/printf.h"
|
||||
#include "readline_highlighters.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
std::multimap<std::string, help_text*> help_text::TAGGED;
|
||||
|
||||
static vector<help_text*>
|
||||
static std::vector<help_text*>
|
||||
get_related(const help_text& ht)
|
||||
{
|
||||
vector<help_text*> retval;
|
||||
std::vector<help_text*> retval;
|
||||
|
||||
for (const auto& tag : ht.ht_tags) {
|
||||
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(":")
|
||||
.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(" ");
|
||||
if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) {
|
||||
out.append("[");
|
||||
|
@ -138,7 +137,7 @@ format_help_text_for_term(const help_text& ht,
|
|||
out.append(body_indent, ' ')
|
||||
.append(ht.ht_name, &view_curses::VC_STYLE, A_BOLD)
|
||||
.append("(");
|
||||
for (auto& param : ht.ht_parameters) {
|
||||
for (const auto& param : ht.ht_parameters) {
|
||||
if (!param.ht_flag_name && needs_comma) {
|
||||
out.append(", ");
|
||||
}
|
||||
|
@ -187,7 +186,7 @@ format_help_text_for_term(const help_text& ht,
|
|||
.append(body_indent, ' ')
|
||||
.append(";")
|
||||
.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(" ");
|
||||
if (param.ht_nargs == help_nargs_t::HN_OPTIONAL) {
|
||||
out.append("[");
|
||||
|
@ -226,7 +225,7 @@ format_help_text_for_term(const help_text& ht,
|
|||
out.append(body_indent, ' ')
|
||||
.append(
|
||||
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
|
||||
|| (int) (out.get_string().length() - start_index
|
||||
- line_start + 10)
|
||||
|
@ -338,7 +337,7 @@ format_help_text_for_term(const help_text& ht,
|
|||
if (!synopsis_only && !ht.ht_parameters.empty()) {
|
||||
size_t max_param_name_width = 0;
|
||||
|
||||
for (auto& param : ht.ht_parameters) {
|
||||
for (const auto& param : ht.ht_parameters) {
|
||||
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)
|
||||
.append("\n");
|
||||
|
||||
for (auto& param : ht.ht_parameters) {
|
||||
for (const auto& param : ht.ht_parameters) {
|
||||
if (!param.ht_summary) {
|
||||
continue;
|
||||
}
|
||||
|
@ -367,7 +366,7 @@ format_help_text_for_term(const help_text& ht,
|
|||
if (!synopsis_only && !ht.ht_results.empty()) {
|
||||
size_t max_result_name_width = 0;
|
||||
|
||||
for (auto& result : ht.ht_results) {
|
||||
for (const auto& result : ht.ht_results) {
|
||||
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)
|
||||
.append("\n");
|
||||
|
||||
for (auto& result : ht.ht_results) {
|
||||
for (const auto& result : ht.ht_results) {
|
||||
if (!result.ht_summary) {
|
||||
continue;
|
||||
}
|
||||
|
@ -395,10 +394,10 @@ format_help_text_for_term(const help_text& ht,
|
|||
}
|
||||
if (!synopsis_only && !ht.ht_tags.empty()) {
|
||||
auto related_help = get_related(ht);
|
||||
auto related_refs = vector<string>();
|
||||
auto related_refs = std::vector<std::string>();
|
||||
|
||||
for (auto related : related_help) {
|
||||
string name = related->ht_name;
|
||||
for (const auto* related : related_help) {
|
||||
std::string name = related->ht_name;
|
||||
switch (related->ht_context) {
|
||||
case help_context_t::HC_COMMAND:
|
||||
name = ":" + name;
|
||||
|
@ -451,7 +450,7 @@ format_example_text_for_term(const help_text& ht,
|
|||
&view_curses::VC_STYLE,
|
||||
A_UNDERLINE)
|
||||
.append("\n");
|
||||
for (auto& ex : ht.ht_example) {
|
||||
for (const auto& ex : ht.ht_example) {
|
||||
attr_line_t ex_line(ex.he_cmd);
|
||||
size_t keyword_offset = 0;
|
||||
const char* space = strchr(ex.he_cmd, ' ');
|
||||
|
@ -482,7 +481,7 @@ format_example_text_for_term(const help_text& ht,
|
|||
}
|
||||
|
||||
out.append("#")
|
||||
.append(to_string(count))
|
||||
.append(fmt::to_string(count))
|
||||
.append(" ")
|
||||
.append(ex.he_description, &tws.with_indent(3))
|
||||
.append(":\n ")
|
||||
|
@ -504,7 +503,7 @@ link_name(const help_text& ht)
|
|||
const static std::regex SCRUBBER("[^\\w_]");
|
||||
|
||||
bool is_sql_infix = ht.ht_context == help_context_t::HC_SQL_INFIX;
|
||||
string scrubbed_name;
|
||||
std::string scrubbed_name;
|
||||
|
||||
if (is_sql_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) {
|
||||
scrubbed_name += "_agg";
|
||||
}
|
||||
for (auto& param : ht.ht_parameters) {
|
||||
for (const auto& param : ht.ht_parameters) {
|
||||
if (!is_sql_infix && param.ht_name[0]) {
|
||||
continue;
|
||||
}
|
||||
|
@ -565,66 +564,66 @@ format_help_text_for_rst(const help_text& ht,
|
|||
break;
|
||||
}
|
||||
|
||||
fprintf(rst_file, "\n.. _%s:\n\n", link_name(ht).c_str());
|
||||
out_count += fprintf(rst_file, "%s%s", prefix, ht.ht_name);
|
||||
fmt::print(rst_file, FMT_STRING("\n.. _{}:\n\n"), link_name(ht));
|
||||
out_count += fmt::fprintf(rst_file, "%s%s", prefix, ht.ht_name);
|
||||
if (is_sql_func) {
|
||||
out_count += fprintf(rst_file, "(");
|
||||
out_count += fmt::fprintf(rst_file, "(");
|
||||
}
|
||||
bool needs_comma = false;
|
||||
for (auto& param : ht.ht_parameters) {
|
||||
for (const auto& param : ht.ht_parameters) {
|
||||
if (needs_comma) {
|
||||
if (param.ht_flag_name) {
|
||||
out_count += fprintf(rst_file, " ");
|
||||
out_count += fmt::fprintf(rst_file, " ");
|
||||
} else {
|
||||
out_count += fprintf(rst_file, ", ");
|
||||
out_count += fmt::fprintf(rst_file, ", ");
|
||||
}
|
||||
}
|
||||
if (!is_sql_func) {
|
||||
out_count += fprintf(rst_file, " ");
|
||||
out_count += fmt::fprintf(rst_file, " ");
|
||||
}
|
||||
|
||||
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]) {
|
||||
out_count += fprintf(rst_file, "*");
|
||||
out_count += fmt::fprintf(rst_file, "*");
|
||||
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) {
|
||||
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) {
|
||||
needs_comma = true;
|
||||
}
|
||||
}
|
||||
if (is_sql_func) {
|
||||
out_count += fprintf(rst_file, ")");
|
||||
out_count += fmt::fprintf(rst_file, ")");
|
||||
}
|
||||
fprintf(rst_file, "\n");
|
||||
fprintf(rst_file, "%s\n\n", string(out_count, '^').c_str());
|
||||
fmt::fprintf(rst_file, "\n");
|
||||
fmt::print(rst_file, FMT_STRING("{0:^^{1}}\n\n"), "", out_count);
|
||||
|
||||
fprintf(rst_file, " %s\n", ht.ht_summary);
|
||||
fprintf(rst_file, "\n");
|
||||
if (ht.ht_description) {
|
||||
fprintf(rst_file, " %s\n", ht.ht_description);
|
||||
fmt::fprintf(rst_file, " %s\n", ht.ht_summary);
|
||||
fmt::fprintf(rst_file, "\n");
|
||||
if (ht.ht_description != nullptr) {
|
||||
fmt::fprintf(rst_file, " %s\n", ht.ht_description);
|
||||
}
|
||||
|
||||
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]) {
|
||||
param_count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (param_count > 0) {
|
||||
fprintf(rst_file, " **Parameters**\n");
|
||||
for (auto& param : ht.ht_parameters) {
|
||||
fmt::fprintf(rst_file, " **Parameters**\n");
|
||||
for (const auto& param : ht.ht_parameters) {
|
||||
if (param.ht_summary && param.ht_summary[0]) {
|
||||
fprintf(
|
||||
fmt::fprintf(
|
||||
rst_file,
|
||||
" * **%s%s** --- %s\n",
|
||||
param.ht_name,
|
||||
|
@ -632,44 +631,44 @@ format_help_text_for_rst(const help_text& ht,
|
|||
param.ht_summary);
|
||||
}
|
||||
}
|
||||
fprintf(rst_file, "\n");
|
||||
fmt::fprintf(rst_file, "\n");
|
||||
}
|
||||
if (is_sql) {
|
||||
prefix = ";";
|
||||
}
|
||||
if (!ht.ht_example.empty()) {
|
||||
fprintf(rst_file, " **Examples**\n");
|
||||
for (auto& example : ht.ht_example) {
|
||||
fprintf(rst_file, " %s:\n\n", example.he_description);
|
||||
fprintf(rst_file,
|
||||
" .. code-block:: %s\n\n",
|
||||
is_sql ? "custsqlite" : "lnav");
|
||||
fmt::fprintf(rst_file, " **Examples**\n");
|
||||
for (const auto& example : ht.ht_example) {
|
||||
fmt::fprintf(rst_file, " %s:\n\n", example.he_description);
|
||||
fmt::fprintf(rst_file,
|
||||
" .. code-block:: %s\n\n",
|
||||
is_sql ? "custsqlite" : "lnav");
|
||||
if (ht.ht_context == help_context_t::HC_COMMAND) {
|
||||
fprintf(rst_file,
|
||||
" %s%s %s\n",
|
||||
prefix,
|
||||
ht.ht_name,
|
||||
example.he_cmd);
|
||||
fmt::fprintf(rst_file,
|
||||
" %s%s %s\n",
|
||||
prefix,
|
||||
ht.ht_name,
|
||||
example.he_cmd);
|
||||
} 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);
|
||||
if (!result.empty()) {
|
||||
vector<attr_line_t> lines;
|
||||
std::vector<attr_line_t> lines;
|
||||
|
||||
result.split_lines(lines);
|
||||
for (auto& line : lines) {
|
||||
fprintf(rst_file, " %s\n", line.get_string().c_str());
|
||||
for (const auto& line : lines) {
|
||||
fmt::fprintf(rst_file, " %s\n", line.get_string());
|
||||
}
|
||||
}
|
||||
fprintf(rst_file, "\n");
|
||||
fmt::fprintf(rst_file, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
fmt::format(":ref:`{}`", link_name(*related)));
|
||||
}
|
||||
|
@ -680,5 +679,5 @@ format_help_text_for_rst(const help_text& ht,
|
|||
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 "config.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
const char* hist_source2::LINE_FORMAT
|
||||
= " %8d normal %8d errors %8d warnings %8d marks";
|
||||
#include "fmt/chrono.h"
|
||||
|
||||
nonstd::optional<vis_line_t>
|
||||
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);
|
||||
struct tm bucket_tm;
|
||||
char tm_buffer[128];
|
||||
char line[256];
|
||||
|
||||
if (gmtime_r(&bucket.b_time, &bucket_tm) != nullptr) {
|
||||
strftime(
|
||||
tm_buffer, sizeof(tm_buffer), " %a %b %d %H:%M:%S ", &bucket_tm);
|
||||
} else {
|
||||
log_error("no time?");
|
||||
tm_buffer[0] = '\0';
|
||||
}
|
||||
snprintf(line,
|
||||
sizeof(line),
|
||||
LINE_FORMAT,
|
||||
(int) rint(bucket.b_values[HT_NORMAL].hv_value),
|
||||
(int) rint(bucket.b_values[HT_ERROR].hv_value),
|
||||
(int) rint(bucket.b_values[HT_WARNING].hv_value),
|
||||
(int) rint(bucket.b_values[HT_MARK].hv_value));
|
||||
|
||||
value_out.clear();
|
||||
value_out.append(tm_buffer);
|
||||
value_out.append(line);
|
||||
if (gmtime_r(&bucket.b_time, &bucket_tm) != nullptr) {
|
||||
fmt::format_to(std::back_inserter(value_out),
|
||||
FMT_STRING(" {:%a %b %d %H:%M:%S} "),
|
||||
bucket_tm);
|
||||
} else {
|
||||
log_error("no time?");
|
||||
}
|
||||
fmt::format_to(
|
||||
std::back_inserter(value_out),
|
||||
FMT_STRING(" {:8L} normal {:8L} errors {:8L} warnings {:8L} marks"),
|
||||
rint(bucket.b_values[HT_NORMAL].hv_value),
|
||||
rint(bucket.b_values[HT_ERROR].hv_value),
|
||||
rint(bucket.b_values[HT_WARNING].hv_value),
|
||||
rint(bucket.b_values[HT_MARK].hv_value));
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -351,7 +351,7 @@ public:
|
|||
|
||||
size_t text_line_width(textview_curses& curses) override
|
||||
{
|
||||
return strlen(LINE_FORMAT) + 8 * 4;
|
||||
return 48 + 8 * 4;
|
||||
};
|
||||
|
||||
void clear();
|
||||
|
@ -382,8 +382,6 @@ public:
|
|||
struct timeval tv_bucket) override;
|
||||
|
||||
private:
|
||||
static const char* LINE_FORMAT;
|
||||
|
||||
struct hist_value {
|
||||
double hv_value;
|
||||
};
|
||||
|
|
|
@ -194,11 +194,8 @@ handle_paging_key(int ch)
|
|||
text_sub_source* tc_tss = tc->get_sub_source();
|
||||
bookmarks<vis_line_t>::type& bm = tc->get_bookmarks();
|
||||
|
||||
char keyseq[16];
|
||||
|
||||
snprintf(keyseq, sizeof(keyseq), "x%02x", ch);
|
||||
|
||||
if (handle_keyseq(keyseq)) {
|
||||
auto keyseq = fmt::format(FMT_STRING("x{:02x}"), ch);
|
||||
if (handle_keyseq(keyseq.c_str())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -739,13 +736,17 @@ handle_paging_key(int ch)
|
|||
}
|
||||
|
||||
if (log_line_index != -1) {
|
||||
char linestr[64];
|
||||
fmt::memory_buffer linestr;
|
||||
int line_number = (int) tc->get_top();
|
||||
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++) {
|
||||
if (strcmp(dls.dls_rows[row][log_line_index], linestr)
|
||||
if (strcmp(dls.dls_rows[row][log_line_index],
|
||||
linestr.data())
|
||||
== 0) {
|
||||
vis_line_t db_line(row);
|
||||
|
||||
|
|
|
@ -29,10 +29,9 @@
|
|||
* @file json-extension-functions.cc
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "mapbox/variant.hpp"
|
||||
#include "sqlite-extension-func.hh"
|
||||
|
@ -43,17 +42,16 @@
|
|||
#include "yajlpp/json_op.hh"
|
||||
#include "yajlpp/yajlpp.hh"
|
||||
|
||||
using namespace std;
|
||||
using namespace mapbox;
|
||||
|
||||
#define JSON_SUBTYPE 74 /* Ascii for "J" */
|
||||
|
||||
class sql_json_op : public json_op {
|
||||
public:
|
||||
sql_json_op(json_ptr& ptr) : json_op(ptr){};
|
||||
explicit sql_json_op(json_ptr& ptr) : json_op(ptr){};
|
||||
|
||||
int sjo_type{-1};
|
||||
string sjo_str;
|
||||
std::string sjo_str;
|
||||
int64_t sjo_int{0};
|
||||
double sjo_float{0.0};
|
||||
};
|
||||
|
@ -120,7 +118,7 @@ json_contains(vtab_types::nullable<const char> nullable_json_in,
|
|||
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);
|
||||
yajl_callbacks cb;
|
||||
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) {
|
||||
sjo->sjo_type = SQLITE3_TEXT;
|
||||
sjo->sjo_str = string((char*) stringVal, len);
|
||||
sjo->sjo_str = std::string((char*) stringVal, len);
|
||||
} else {
|
||||
sjo->jo_ptr_error_code = yajl_gen_string(gen, stringVal, len);
|
||||
}
|
||||
|
@ -375,7 +373,7 @@ struct concat_context {
|
|||
static int
|
||||
concat_gen_null(void* ctx)
|
||||
{
|
||||
auto cc = static_cast<concat_context*>(ctx);
|
||||
auto* cc = static_cast<concat_context*>(ctx);
|
||||
|
||||
if (cc->cc_depth > 0) {
|
||||
return yajl_gen_null(cc->cc_gen_handle) == yajl_gen_status_ok;
|
||||
|
@ -387,7 +385,7 @@ concat_gen_null(void* ctx)
|
|||
static int
|
||||
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;
|
||||
}
|
||||
|
@ -395,7 +393,7 @@ concat_gen_boolean(void* ctx, int val)
|
|||
static int
|
||||
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;
|
||||
}
|
||||
|
@ -403,7 +401,7 @@ concat_gen_number(void* ctx, const char* val, size_t len)
|
|||
static int
|
||||
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;
|
||||
}
|
||||
|
@ -411,7 +409,7 @@ concat_gen_string(void* ctx, const unsigned char* val, size_t len)
|
|||
static int
|
||||
concat_gen_start_map(void* ctx)
|
||||
{
|
||||
auto cc = static_cast<concat_context*>(ctx);
|
||||
auto* cc = static_cast<concat_context*>(ctx);
|
||||
|
||||
cc->cc_depth += 1;
|
||||
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
|
||||
concat_gen_end_map(void* ctx)
|
||||
{
|
||||
auto cc = static_cast<concat_context*>(ctx);
|
||||
auto* cc = static_cast<concat_context*>(ctx);
|
||||
|
||||
cc->cc_depth -= 1;
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
@ -437,7 +435,7 @@ concat_gen_map_key(void* ctx, const unsigned char* key, size_t len)
|
|||
static int
|
||||
concat_gen_start_array(void* ctx)
|
||||
{
|
||||
auto cc = static_cast<concat_context*>(ctx);
|
||||
auto* cc = static_cast<concat_context*>(ctx);
|
||||
|
||||
cc->cc_depth += 1;
|
||||
if (cc->cc_depth == 1) {
|
||||
|
@ -449,7 +447,7 @@ concat_gen_start_array(void* ctx)
|
|||
static int
|
||||
concat_gen_end_array(void* ctx)
|
||||
{
|
||||
auto cc = static_cast<concat_context*>(ctx);
|
||||
auto* cc = static_cast<concat_context*>(ctx);
|
||||
|
||||
cc->cc_depth -= 1;
|
||||
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
|
||||
|| 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);
|
||||
|
||||
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
|
||||
json_concat(nonstd::optional<const char*> json_in,
|
||||
const vector<sqlite3_value*>& values)
|
||||
const std::vector<sqlite3_value*>& values)
|
||||
{
|
||||
yajlpp_gen gen;
|
||||
|
||||
|
@ -618,7 +616,7 @@ sql_json_group_object_final(sqlite3_context* context)
|
|||
json_agg_context* jac
|
||||
= (json_agg_context*) sqlite3_aggregate_context(context, 0);
|
||||
|
||||
if (jac == NULL) {
|
||||
if (jac == nullptr) {
|
||||
sqlite3_result_text(context, "{}", -1, SQLITE_STATIC);
|
||||
} else {
|
||||
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(
|
||||
context, sizeof(json_agg_context));
|
||||
|
||||
if (jac->jac_yajl_gen == NULL) {
|
||||
jac->jac_yajl_gen = yajl_gen_alloc(NULL);
|
||||
if (jac->jac_yajl_gen == nullptr) {
|
||||
jac->jac_yajl_gen = yajl_gen_alloc(nullptr);
|
||||
yajl_gen_config(jac->jac_yajl_gen, yajl_gen_beautify, false);
|
||||
|
||||
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.")
|
||||
.one_or_more())
|
||||
.with_tags({"json"})
|
||||
.with_example({"To append the number 4 to null",
|
||||
"SELECT json_concat(NULL, 4)"})
|
||||
.with_example({"To append 4 and 5 to the array [1, 2, 3]",
|
||||
"SELECT json_concat('[1, 2, 3]', 4, 5)"})
|
||||
.with_example(
|
||||
{"To concatenate two arrays together",
|
||||
"SELECT json_concat('[1, 2, 3]', json('[4, 5]'))"})),
|
||||
.with_example({
|
||||
"To append the number 4 to null",
|
||||
"SELECT json_concat(NULL, 4)",
|
||||
})
|
||||
.with_example({
|
||||
"To append 4 and 5 to the array [1, 2, 3]",
|
||||
"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(
|
||||
help_text("json_contains",
|
||||
|
@ -814,10 +817,11 @@ json_extension_functions(struct FuncDef** basic_funcs,
|
|||
.with_tags({"json"})
|
||||
.with_example({"To create an object from arguments",
|
||||
"SELECT json_group_object('a', 1, 'b', 2)"})
|
||||
.with_example(
|
||||
{"To create an object from a pair of columns",
|
||||
"SELECT json_group_object(column1, column2) FROM "
|
||||
"(VALUES ('a', 1), ('b', 2))"}),
|
||||
.with_example({
|
||||
"To create an object from a pair of columns",
|
||||
"SELECT json_group_object(column1, column2) FROM "
|
||||
"(VALUES ('a', 1), ('b', 2))",
|
||||
}),
|
||||
},
|
||||
{
|
||||
"json_group_array",
|
||||
|
@ -833,11 +837,15 @@ json_extension_functions(struct FuncDef** basic_funcs,
|
|||
help_text("value", "The values to append to the array")
|
||||
.one_or_more())
|
||||
.with_tags({"json"})
|
||||
.with_example({"To create an array from arguments",
|
||||
"SELECT json_group_array('one', 2, 3.4)"})
|
||||
.with_example({"To create an array from a column of values",
|
||||
"SELECT json_group_array(column1) FROM (VALUES "
|
||||
"(1), (2), (3))"}),
|
||||
.with_example({
|
||||
"To create an array from arguments",
|
||||
"SELECT json_group_array('one', 2, 3.4)",
|
||||
})
|
||||
.with_example({
|
||||
"To create an array from a column of values",
|
||||
"SELECT json_group_array(column1) FROM (VALUES "
|
||||
"(1), (2), (3))",
|
||||
}),
|
||||
},
|
||||
|
||||
{nullptr},
|
||||
|
|
15
src/lnav.cc
15
src/lnav.cc
|
@ -951,7 +951,7 @@ update_active_files(file_collection& new_files)
|
|||
}
|
||||
for (const auto& other_pair : new_files.fc_other_files) {
|
||||
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);
|
||||
break;
|
||||
default:
|
||||
|
@ -989,7 +989,7 @@ rescan_files(bool req)
|
|||
mlooper.get_port().process_for(delay);
|
||||
if (lnav_data.ld_flags & LNF_HEADLESS) {
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1910,7 +1910,7 @@ looper()
|
|||
lnav_data.ld_active_files.fc_other_files.end(),
|
||||
[](const auto& pair) {
|
||||
return pair.second.ofd_format
|
||||
== file_format_t::FF_SQLITE_DB;
|
||||
== file_format_t::SQLITE_DB;
|
||||
}))
|
||||
{
|
||||
ensure_view(&lnav_data.ld_views[LNV_SCHEMA]);
|
||||
|
@ -2132,7 +2132,7 @@ main(int argc, char* argv[])
|
|||
}
|
||||
|
||||
(void) signal(SIGPIPE, SIG_IGN);
|
||||
setlocale(LC_ALL, "");
|
||||
std::locale::global(std::locale(""));
|
||||
umask(027);
|
||||
|
||||
/* 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")
|
||||
.second);
|
||||
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(
|
||||
fifo_out_fd);
|
||||
lnav_data.ld_pipers.push_back(fifo_piper);
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
#include "base/paths.hh"
|
||||
#include "base/string_util.hh"
|
||||
#include "bound_tags.hh"
|
||||
#include "fmt/printf.h"
|
||||
#include "command_executor.hh"
|
||||
#include "config.h"
|
||||
#include "curl_looper.hh"
|
||||
|
@ -705,9 +706,9 @@ csv_write_string(FILE* outfile, const string& str)
|
|||
if (csv_needs_quoting(str)) {
|
||||
string quoted_str = csv_quote_string(str);
|
||||
|
||||
fprintf(outfile, "%s", quoted_str.c_str());
|
||||
fmt::fprintf(outfile, "%s", quoted_str);
|
||||
} 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) {
|
||||
for (int lpc = 0; lpc < (int) gl->gl_pathc; lpc++) {
|
||||
char* git_dir = dirname(gl->gl_pathv[lpc]);
|
||||
char pull_cmd[1024];
|
||||
|
||||
printf("Updating formats in %s\n", git_dir);
|
||||
snprintf(pull_cmd, sizeof(pull_cmd), "cd %s && git pull", git_dir);
|
||||
int ret = system(pull_cmd);
|
||||
auto pull_cmd
|
||||
= fmt::format(FMT_STRING("cd '{}' && git pull"), git_dir);
|
||||
int ret = system(pull_cmd.c_str());
|
||||
if (ret == -1) {
|
||||
std::cerr << "Failed to spawn command "
|
||||
<< "\"" << pull_cmd << "\": " << strerror(errno)
|
||||
|
@ -314,7 +314,7 @@ read_repo_path(yajlpp_parse_context* ypc, const unsigned char* str, size_t len)
|
|||
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)};
|
||||
|
||||
void
|
||||
|
@ -324,23 +324,17 @@ install_extra_formats()
|
|||
auto_fd fd;
|
||||
|
||||
if (access(config_root.c_str(), R_OK) == 0) {
|
||||
char pull_cmd[1024];
|
||||
|
||||
printf("Updating lnav remote config repo...\n");
|
||||
snprintf(pull_cmd,
|
||||
sizeof(pull_cmd),
|
||||
"cd '%s' && git pull",
|
||||
config_root.c_str());
|
||||
log_perror(system(pull_cmd));
|
||||
auto pull_cmd = fmt::format(FMT_STRING("cd '{}' && git pull"),
|
||||
config_root.string());
|
||||
log_perror(system(pull_cmd.c_str()));
|
||||
} else {
|
||||
char clone_cmd[1024];
|
||||
|
||||
printf("Cloning lnav remote config repo...\n");
|
||||
snprintf(clone_cmd,
|
||||
sizeof(clone_cmd),
|
||||
"git clone https://github.com/tstack/lnav-config.git %s",
|
||||
config_root.c_str());
|
||||
log_perror(system(clone_cmd));
|
||||
auto clone_cmd = fmt::format(
|
||||
FMT_STRING(
|
||||
"git clone https://github.com/tstack/lnav-config.git {}"),
|
||||
config_root.string());
|
||||
log_perror(system(clone_cmd.c_str()));
|
||||
}
|
||||
|
||||
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")
|
||||
.with_synopsis("<command>")
|
||||
.with_description(
|
||||
|
@ -407,7 +401,7 @@ static struct json_path_container key_command_handlers = {
|
|||
"The help message to display after the key is pressed.")
|
||||
.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})+)")
|
||||
.with_synopsis("<utf8-key-code-in-hex>")
|
||||
.with_description(
|
||||
|
@ -431,7 +425,7 @@ static struct json_path_container keymap_def_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\\-]+)")
|
||||
.with_description("The keymap definitions")
|
||||
.with_obj_provider<key_map, _lnav_config>(
|
||||
|
@ -449,7 +443,7 @@ static struct json_path_container keymap_defs_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+)")
|
||||
.with_synopsis("<name>")
|
||||
.with_description(
|
||||
|
@ -463,7 +457,7 @@ static struct json_path_container global_var_handlers = {
|
|||
})
|
||||
.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{
|
||||
yajlpp::property_handler("color")
|
||||
.with_synopsis("#hex|color_name")
|
||||
|
@ -493,7 +487,7 @@ static struct json_path_container style_config_handlers =
|
|||
}
|
||||
.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")
|
||||
.with_description("Styling for identifiers in logs")
|
||||
.with_obj_provider<style_config, lnav_theme>(
|
||||
|
@ -601,7 +595,7 @@ static struct json_path_container theme_styles_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")
|
||||
.with_description("Styling for keywords in source files")
|
||||
.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)};
|
||||
|
||||
static struct json_path_container theme_status_styles_handlers = {
|
||||
static const struct json_path_container theme_status_styles_handlers = {
|
||||
yajlpp::property_handler("text")
|
||||
.with_description("Styling for status bars")
|
||||
.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),
|
||||
};
|
||||
|
||||
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(
|
||||
"(?<level>trace|debug5|debug4|debug3|debug2|debug|info|stats|notice|"
|
||||
"warning|error|critical|fatal|invalid)")
|
||||
|
@ -796,7 +790,7 @@ static struct json_path_container theme_log_level_styles_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")
|
||||
.with_synopsis("regular expression")
|
||||
.with_description("The regular expression to highlight")
|
||||
|
@ -812,7 +806,7 @@ static struct json_path_container highlighter_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+)")
|
||||
.with_obj_provider<highlighter_config, lnav_theme>(
|
||||
[](const yajlpp_provider_context& ypc, lnav_theme* root) {
|
||||
|
@ -831,7 +825,7 @@ static struct json_path_container theme_highlights_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+)")
|
||||
.with_synopsis("name")
|
||||
.with_description("A theme variable definition")
|
||||
|
@ -843,7 +837,7 @@ static struct json_path_container theme_vars_handlers
|
|||
})
|
||||
.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")
|
||||
.with_description("Variables definitions that are used in this theme.")
|
||||
.with_children(theme_vars_handlers),
|
||||
|
@ -869,7 +863,7 @@ static struct json_path_container theme_def_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\\-]+)")
|
||||
.with_description("Theme definitions")
|
||||
.with_obj_provider<lnav_theme, _lnav_config>(
|
||||
|
@ -888,7 +882,7 @@ static struct json_path_container theme_defs_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")
|
||||
.with_synopsis("format")
|
||||
.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),
|
||||
};
|
||||
|
||||
static struct json_path_container archive_handlers = {
|
||||
static const struct json_path_container archive_handlers = {
|
||||
yajlpp::property_handler("min-free-space")
|
||||
.with_synopsis("<bytes>")
|
||||
.with_description(
|
||||
|
@ -945,7 +939,7 @@ static struct json_path_container archive_handlers = {
|
|||
&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")
|
||||
.with_synopsis("<bytes>")
|
||||
.with_description(
|
||||
|
@ -955,7 +949,7 @@ static struct json_path_container file_vtab_handlers = {
|
|||
&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")
|
||||
.with_synopsis("<lines>")
|
||||
.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),
|
||||
};
|
||||
|
||||
static struct json_path_container ssh_config_handlers = {
|
||||
static const struct json_path_container ssh_config_handlers = {
|
||||
yajlpp::pattern_property_handler("(?<config_name>\\w+)")
|
||||
.with_synopsis("name")
|
||||
.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),
|
||||
};
|
||||
|
||||
static struct json_path_container ssh_option_handlers = {
|
||||
static const struct json_path_container ssh_option_handlers = {
|
||||
yajlpp::pattern_property_handler("(?<option_name>\\w+)")
|
||||
.with_synopsis("name")
|
||||
.with_description("Set an option to be passed to the SSH command")
|
||||
.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")
|
||||
.with_synopsis("ssh-command")
|
||||
.with_description("The SSH command to execute")
|
||||
|
@ -1012,7 +1006,7 @@ static struct json_path_container ssh_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")
|
||||
.with_synopsis("<duration>")
|
||||
.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),
|
||||
};
|
||||
|
||||
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")
|
||||
.with_synopsis("<command>")
|
||||
.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_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")
|
||||
.with_synopsis("<command>")
|
||||
.with_description("The command that checks")
|
||||
|
@ -1065,7 +1059,7 @@ static struct json_path_container sysclip_impl_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\\-]+)")
|
||||
.with_synopsis("<name>")
|
||||
.with_description("Clipboard implementation")
|
||||
|
@ -1086,13 +1080,13 @@ static struct json_path_container sysclip_impls_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")
|
||||
.with_description("Clipboard implementations")
|
||||
.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")
|
||||
.with_description("Settings related to opening archive files")
|
||||
.with_children(archive_handlers),
|
||||
|
@ -1110,14 +1104,14 @@ static struct json_path_container tuning_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",
|
||||
};
|
||||
|
||||
const char* DEFAULT_FORMAT_SCHEMA
|
||||
= "https://lnav.org/schemas/format-v1.schema.json";
|
||||
|
||||
set<string> SUPPORTED_FORMAT_SCHEMAS = {
|
||||
const set<string> SUPPORTED_FORMAT_SCHEMAS = {
|
||||
DEFAULT_FORMAT_SCHEMA,
|
||||
};
|
||||
|
||||
|
@ -1139,25 +1133,24 @@ read_id(yajlpp_parse_context* ypc, const unsigned char* str, size_t len)
|
|||
return 1;
|
||||
}
|
||||
|
||||
struct json_path_container lnav_config_handlers
|
||||
= json_path_container{json_path_handler("$schema", read_id)
|
||||
.with_synopsis(
|
||||
"The URI of the schema for this file")
|
||||
.with_description(
|
||||
"Specifies the type of this file"),
|
||||
const auto lnav_config_handlers = json_path_container {
|
||||
json_path_handler("$schema", read_id)
|
||||
.with_synopsis("The URI of the schema for this file")
|
||||
.with_description("Specifies the type of this file"),
|
||||
|
||||
yajlpp::property_handler("tuning")
|
||||
.with_description("Internal settings")
|
||||
.with_children(tuning_handlers),
|
||||
yajlpp::property_handler("tuning")
|
||||
.with_description("Internal settings")
|
||||
.with_children(tuning_handlers),
|
||||
|
||||
yajlpp::property_handler("ui")
|
||||
.with_description("User-interface settings")
|
||||
.with_children(ui_handlers),
|
||||
yajlpp::property_handler("ui")
|
||||
.with_description("User-interface settings")
|
||||
.with_children(ui_handlers),
|
||||
|
||||
yajlpp::property_handler("global")
|
||||
.with_description("Global variable definitions")
|
||||
.with_children(global_var_handlers)}
|
||||
.with_schema_id(*SUPPORTED_CONFIG_SCHEMAS.cbegin());
|
||||
yajlpp::property_handler("global")
|
||||
.with_description("Global variable definitions")
|
||||
.with_children(global_var_handlers),
|
||||
}
|
||||
.with_schema_id(*SUPPORTED_CONFIG_SCHEMAS.cbegin());
|
||||
|
||||
class active_key_map_listener : public lnav_config_listener {
|
||||
public:
|
||||
|
@ -1234,13 +1227,9 @@ load_config_from(_lnav_config& lconfig,
|
|||
ypc.with_error_reporter(config_error_reporter);
|
||||
if ((fd = lnav::filesystem::openp(path, O_RDONLY)) == -1) {
|
||||
if (errno != ENOENT) {
|
||||
char errmsg[1024];
|
||||
|
||||
snprintf(errmsg,
|
||||
sizeof(errmsg),
|
||||
"error: unable to open format file -- %s",
|
||||
path.c_str());
|
||||
errors.emplace_back(errmsg);
|
||||
errors.emplace_back(fmt::format(
|
||||
FMT_STRING("error: unable to open format file -- {}"),
|
||||
path.string()));
|
||||
}
|
||||
} else {
|
||||
auto_mem<yajl_handle_t> handle(yajl_free);
|
||||
|
@ -1257,8 +1246,10 @@ load_config_from(_lnav_config& lconfig,
|
|||
if (rc == 0) {
|
||||
break;
|
||||
} else if (rc == -1) {
|
||||
errors.push_back(path.string() + ":unable to read file -- "
|
||||
+ string(strerror(errno)));
|
||||
errors.emplace_back(
|
||||
fmt::format(FMT_STRING("{}:unable to read file -- {}"),
|
||||
path.string(),
|
||||
strerror(errno)));
|
||||
break;
|
||||
}
|
||||
if (ypc.parse((const unsigned char*) buffer, rc) != yajl_status_ok)
|
||||
|
@ -1446,19 +1437,13 @@ reload_config(vector<string>& errors)
|
|||
return;
|
||||
}
|
||||
|
||||
char msg[1024];
|
||||
|
||||
snprintf(msg,
|
||||
sizeof(msg),
|
||||
"%s:%d:%s",
|
||||
loc_iter->second.sl_source.get(),
|
||||
loc_iter->second.sl_line_number,
|
||||
errmsg.c_str());
|
||||
|
||||
errors.emplace_back(msg);
|
||||
errors.emplace_back(fmt::format(FMT_STRING("{}:{}:{}"),
|
||||
loc_iter->second.sl_source,
|
||||
loc_iter->second.sl_line_number,
|
||||
errmsg));
|
||||
};
|
||||
|
||||
for (auto& jph : lnav_config_handlers.jpc_children) {
|
||||
for (const auto& jph : lnav_config_handlers.jpc_children) {
|
||||
jph.walk(cb, &lnav_config);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -106,7 +106,7 @@ extern struct _lnav_config lnav_config;
|
|||
extern struct _lnav_config rollback_lnav_config;
|
||||
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 {
|
||||
FORMAT,
|
||||
|
@ -126,6 +126,6 @@ void reload_config(std::vector<std::string>& errors);
|
|||
std::string save_config();
|
||||
|
||||
extern const char* DEFAULT_FORMAT_SCHEMA;
|
||||
extern std::set<std::string> SUPPORTED_FORMAT_SCHEMAS;
|
||||
extern const std::set<std::string> SUPPORTED_FORMAT_SCHEMAS;
|
||||
|
||||
#endif
|
||||
|
|
|
@ -42,8 +42,6 @@
|
|||
#include "fmt/format.h"
|
||||
#include "view_curses.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
bool
|
||||
change_to_parent_dir()
|
||||
{
|
||||
|
|
|
@ -2354,7 +2354,7 @@ external_log_format::match_name(const string& filename)
|
|||
bool
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -363,7 +363,7 @@ public:
|
|||
|
||||
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 false;
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
static pcrepp RDNS_PATTERN(
|
||||
static const pcrepp RDNS_PATTERN(
|
||||
"^(?:com|net|org|edu|[a-z][a-z])"
|
||||
"(\\.\\w+)+(.+)");
|
||||
|
||||
|
@ -125,23 +125,24 @@ class generic_log_format : public log_format {
|
|||
pcre_format("^(?:\\*\\*\\*\\s+)?\\[(?<timestamp>[\\w: ,+/-]+)\\] "
|
||||
"\\(\\d+\\) (.*)"),
|
||||
|
||||
pcre_format()};
|
||||
pcre_format(),
|
||||
};
|
||||
|
||||
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);
|
||||
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");
|
||||
};
|
||||
|
||||
void scrub(string& line)
|
||||
void scrub(string& line) override
|
||||
{
|
||||
pcre_context_static<30> context;
|
||||
pcre_input pi(line);
|
||||
|
@ -161,7 +162,7 @@ class generic_log_format : public log_format {
|
|||
scan_result_t scan(logfile& lf,
|
||||
vector<logline>& dst,
|
||||
const line_info& li,
|
||||
shared_buffer_ref& sbr)
|
||||
shared_buffer_ref& sbr) override
|
||||
{
|
||||
struct exttm log_time;
|
||||
struct timeval log_tv;
|
||||
|
@ -201,7 +202,7 @@ class generic_log_format : public log_format {
|
|||
shared_buffer_ref& line,
|
||||
string_attrs_t& sa,
|
||||
std::vector<logline_value>& values,
|
||||
bool annotate_module) const
|
||||
bool annotate_module) const override
|
||||
{
|
||||
int pat_index = this->pattern_index_for_line(line_number);
|
||||
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);
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
@ -299,17 +300,17 @@ struct separated_string {
|
|||
size_t ss_separator_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)
|
||||
{
|
||||
this->ss_separator = sep;
|
||||
this->ss_separator_len = strlen(sep);
|
||||
return *this;
|
||||
};
|
||||
}
|
||||
|
||||
struct iterator {
|
||||
const separated_string& i_parent;
|
||||
|
@ -379,12 +380,12 @@ struct separated_string {
|
|||
iterator begin()
|
||||
{
|
||||
return {*this, this->ss_str};
|
||||
};
|
||||
}
|
||||
|
||||
iterator end()
|
||||
{
|
||||
return {*this, this->ss_str + this->ss_len};
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
class bro_log_format : public log_format {
|
||||
|
@ -423,14 +424,14 @@ public:
|
|||
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"));
|
||||
|
||||
return this->blf_format_name.empty() ? name : this->blf_format_name;
|
||||
};
|
||||
|
||||
virtual void clear()
|
||||
void clear() override
|
||||
{
|
||||
this->log_format::clear();
|
||||
this->blf_format_name.clear();
|
||||
|
@ -518,9 +519,9 @@ public:
|
|||
scan_result_t scan(logfile& lf,
|
||||
std::vector<logline>& dst,
|
||||
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()) {
|
||||
return this->scan_int(dst, li, sbr);
|
||||
|
@ -583,10 +584,7 @@ public:
|
|||
} else if (directive == "#unset_field") {
|
||||
this->blf_unset_field = intern_string::lookup(*iter);
|
||||
} else if (directive == "#path") {
|
||||
string path = to_string(*iter);
|
||||
char full_name[128];
|
||||
snprintf(
|
||||
full_name, sizeof(full_name), "bro_%s_log", path.c_str());
|
||||
auto full_name = fmt::format(FMT_STRING("bro_{}_log"), *iter);
|
||||
this->blf_format_name = intern_string::lookup(full_name);
|
||||
} else if (directive == "#fields") {
|
||||
do {
|
||||
|
@ -674,7 +672,7 @@ public:
|
|||
shared_buffer_ref& sbr,
|
||||
string_attrs_t& sa,
|
||||
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 UID = intern_string::lookup("bro_uid");
|
||||
|
@ -714,17 +712,16 @@ public:
|
|||
};
|
||||
|
||||
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;
|
||||
|
||||
for (size_t lpc = 0; lpc < this->blf_field_defs.size(); lpc++) {
|
||||
if (this->blf_field_defs[lpc].fd_meta.lvm_name == name) {
|
||||
if (this->blf_field_defs[lpc].fd_numeric_index < 0) {
|
||||
for (const auto& blf_field_def : this->blf_field_defs) {
|
||||
if (blf_field_def.fd_meta.lvm_name == name) {
|
||||
if (blf_field_def.fd_numeric_index < 0) {
|
||||
break;
|
||||
}
|
||||
retval = &this->lf_value_stats[this->blf_field_defs[lpc]
|
||||
.fd_numeric_index];
|
||||
retval = &this->lf_value_stats[blf_field_def.fd_numeric_index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -732,7 +729,7 @@ public:
|
|||
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);
|
||||
};
|
||||
|
@ -782,7 +779,7 @@ public:
|
|||
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()) {
|
||||
return nullptr;
|
||||
|
@ -802,7 +799,7 @@ public:
|
|||
|
||||
void get_subline(const logline& ll,
|
||||
shared_buffer_ref& sbr,
|
||||
bool full_message)
|
||||
bool full_message) override
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1129,8 +1126,8 @@ public:
|
|||
const line_info& li,
|
||||
shared_buffer_ref& sbr) override
|
||||
{
|
||||
static auto W3C_LOG_NAME = intern_string::lookup("w3c_log");
|
||||
static auto X_FIELDS_NAME = intern_string::lookup("x_fields");
|
||||
static const auto* W3C_LOG_NAME = intern_string::lookup("w3c_log");
|
||||
static const auto* X_FIELDS_NAME = intern_string::lookup("x_fields");
|
||||
static auto X_FIELDS_IDX = 0;
|
||||
|
||||
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[]
|
||||
= {{
|
||||
"application/vnd.tcpdump.pcap",
|
||||
file_format_t::FF_PCAP,
|
||||
file_format_t::PCAP,
|
||||
},
|
||||
|
||||
json_path_handler_base::ENUM_TERMINATOR};
|
||||
|
@ -885,14 +885,11 @@ write_sample_file()
|
|||
struct script_metadata meta;
|
||||
auto sf = bsf.to_string_fragment();
|
||||
auto_fd script_fd;
|
||||
char path[2048];
|
||||
struct stat st;
|
||||
|
||||
extract_metadata(sf.data(), sf.length(), meta);
|
||||
snprintf(path,
|
||||
sizeof(path),
|
||||
"formats/default/%s.lnav",
|
||||
meta.sm_name.c_str());
|
||||
auto path
|
||||
= fmt::format(FMT_STRING("formats/default/{}.lnav"), meta.sm_name);
|
||||
auto script_path = lnav::paths::dotlnav() / path;
|
||||
if (lnav::filesystem::statp(script_path, &st) == 0
|
||||
&& st.st_size == sf.length()) {
|
||||
|
|
|
@ -132,7 +132,7 @@ struct logfile_open_options {
|
|||
bool loo_non_utf_is_visible{true};
|
||||
ssize_t loo_visible_size_limit{-1};
|
||||
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
|
||||
|
|
|
@ -584,8 +584,8 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
|
|||
|
||||
auto sql_filter_opt = this->get_sql_filter();
|
||||
if (sql_filter_opt) {
|
||||
auto sf = (sql_filter*) sql_filter_opt.value().get();
|
||||
int color;
|
||||
auto* sf = (sql_filter*) sql_filter_opt.value().get();
|
||||
log_debug("eval sql %p %p", &this->tss_filters, sf);
|
||||
auto eval_res = this->eval_sql_filter(sf->sf_filter_stmt.in(),
|
||||
this->lss_token_file_data,
|
||||
this->lss_token_line);
|
||||
|
@ -593,7 +593,7 @@ logfile_sub_source::text_attrs_for_line(textview_curses& lv,
|
|||
auto msg = fmt::format(
|
||||
"filter expression evaluation failed with -- {}",
|
||||
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}, &view_curses::VC_BACKGROUND, color);
|
||||
|
@ -1101,7 +1101,7 @@ logfile_sub_source::text_filters_changed()
|
|||
}
|
||||
|
||||
for (auto& ld : *this) {
|
||||
auto lf = ld->get_file_ptr();
|
||||
auto* lf = ld->get_file_ptr();
|
||||
|
||||
if (lf != nullptr) {
|
||||
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>
|
||||
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()) {
|
||||
auto top_cl = this->at(0_vl);
|
||||
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();
|
||||
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) {
|
||||
auto existing_iter = std::find(this->tss_filters.begin(),
|
||||
this->tss_filters.end(),
|
||||
|
|
|
@ -425,7 +425,7 @@ public:
|
|||
{
|
||||
int retval = 0;
|
||||
|
||||
for (auto& ld : this->lss_files) {
|
||||
for (const auto& ld : this->lss_files) {
|
||||
retval += ld->ld_filter_state.lfo_filter_state
|
||||
.tfs_filter_hits[filter_index];
|
||||
}
|
||||
|
@ -451,6 +451,8 @@ public:
|
|||
return "";
|
||||
}
|
||||
|
||||
nonstd::optional<std::shared_ptr<text_filter>> get_sql_filter();
|
||||
|
||||
std::string get_sql_marker_text() const
|
||||
{
|
||||
return this->lss_marker_stmt_text;
|
||||
|
@ -588,9 +590,9 @@ public:
|
|||
bool ld_visible;
|
||||
};
|
||||
|
||||
typedef std::vector<std::unique_ptr<logfile_data>>::iterator iterator;
|
||||
typedef std::vector<std::unique_ptr<logfile_data>>::const_iterator
|
||||
const_iterator;
|
||||
using iterator = std::vector<std::unique_ptr<logfile_data>>::iterator;
|
||||
using const_iterator
|
||||
= std::vector<std::unique_ptr<logfile_data>>::const_iterator;
|
||||
|
||||
iterator begin()
|
||||
{
|
||||
|
@ -868,8 +870,6 @@ private:
|
|||
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);
|
||||
|
||||
size_t lss_basename_width = 0;
|
||||
|
|
|
@ -129,20 +129,15 @@ rl_set_help()
|
|||
case LNM_SQL: {
|
||||
textview_curses& log_view = lnav_data.ld_views[LNV_LOG];
|
||||
auto lss = (logfile_sub_source*) log_view.get_sub_source();
|
||||
char example_txt[1024];
|
||||
attr_line_t example_al;
|
||||
|
||||
if (log_view.get_inner_height() > 0) {
|
||||
auto cl = lss->at(log_view.get_top());
|
||||
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,
|
||||
sizeof(example_txt),
|
||||
SQL_EXAMPLE,
|
||||
format_name,
|
||||
format_name);
|
||||
example_al.with_ansi_string(example_txt);
|
||||
example_al.with_ansi_string(
|
||||
SQL_EXAMPLE, format_name, format_name);
|
||||
readline_sqlite_highlighter(example_al, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -354,7 +354,6 @@ nonstd::optional<session_pair_t>
|
|||
scan_sessions()
|
||||
{
|
||||
static_root_mem<glob_t, globfree> view_info_list;
|
||||
char view_info_pattern_base[128];
|
||||
|
||||
cleanup_session_data();
|
||||
|
||||
|
@ -367,10 +366,8 @@ scan_sessions()
|
|||
|
||||
session_file_names.clear();
|
||||
|
||||
snprintf(view_info_pattern_base,
|
||||
sizeof(view_info_pattern_base),
|
||||
"view-info-%s.*.json",
|
||||
session_id.value().c_str());
|
||||
auto view_info_pattern_base
|
||||
= fmt::format(FMT_STRING("view-info-{}.*.json"), session_id.value());
|
||||
auto view_info_pattern = lnav::paths::dotlnav() / view_info_pattern_base;
|
||||
if (glob(view_info_pattern.c_str(), 0, nullptr, view_info_list.inout())
|
||||
== 0) {
|
||||
|
@ -1424,7 +1421,7 @@ save_time_bookmarks()
|
|||
}
|
||||
|
||||
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);
|
||||
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());
|
||||
|
||||
char view_base_name[256];
|
||||
snprintf(view_base_name,
|
||||
sizeof(view_base_name),
|
||||
"view-info-%s.ts%ld.ppid%d.json",
|
||||
session_id.c_str(),
|
||||
lnav_data.ld_session_time,
|
||||
getppid());
|
||||
|
||||
auto view_base_name
|
||||
= fmt::format(FMT_STRING("view-info-{}.ts{}.ppid{}.json"),
|
||||
session_id,
|
||||
lnav_data.ld_session_time,
|
||||
getppid());
|
||||
auto view_file_name = lnav::paths::dotlnav() / view_base_name;
|
||||
auto view_file_tmp_name = view_file_name.string() + ".tmp";
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
#define lnav_tailerpp_hh
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "auto_mem.hh"
|
||||
#include "base/result.h"
|
||||
|
|
|
@ -39,23 +39,23 @@ text_format_t
|
|||
detect_text_format(const char* str, size_t len)
|
||||
{
|
||||
// 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*try:[^\\n]*$"
|
||||
")",
|
||||
PCRE_MULTILINE);
|
||||
|
||||
static pcrepp RUST_MATCHERS = pcrepp(R"(
|
||||
static const pcrepp RUST_MATCHERS = pcrepp(R"(
|
||||
(?:
|
||||
^\s*use\s+[\w+:\{\}]+;$|
|
||||
^\s*(?:pub)?\s+(?:const|enum|fn)\s+\w+.*$|
|
||||
^\s*impl\s+\w+.*$
|
||||
)
|
||||
)",
|
||||
PCRE_MULTILINE);
|
||||
PCRE_MULTILINE);
|
||||
|
||||
static pcrepp JAVA_MATCHERS = pcrepp(
|
||||
static const pcrepp JAVA_MATCHERS = pcrepp(
|
||||
"(?:"
|
||||
"^package\\s+|"
|
||||
"^import\\s+|"
|
||||
|
@ -63,7 +63,7 @@ detect_text_format(const char* str, size_t len)
|
|||
")",
|
||||
PCRE_MULTILINE);
|
||||
|
||||
static pcrepp C_LIKE_MATCHERS = pcrepp(
|
||||
static const pcrepp C_LIKE_MATCHERS = pcrepp(
|
||||
"(?:"
|
||||
"^#\\s*include\\s+|"
|
||||
"^#\\s*define\\s+|"
|
||||
|
@ -72,14 +72,14 @@ detect_text_format(const char* str, size_t len)
|
|||
")",
|
||||
PCRE_MULTILINE);
|
||||
|
||||
static pcrepp SQL_MATCHERS = pcrepp(
|
||||
static const pcrepp SQL_MATCHERS = pcrepp(
|
||||
"(?:"
|
||||
"select\\s+.+\\s+from\\s+|"
|
||||
"insert\\s+into\\s+.+\\s+values"
|
||||
")",
|
||||
PCRE_MULTILINE | PCRE_CASELESS);
|
||||
|
||||
static pcrepp XML_MATCHERS = pcrepp(
|
||||
static const pcrepp XML_MATCHERS = pcrepp(
|
||||
"(?:"
|
||||
R"(<\?xml(\s+\w+\s*=\s*"[^"]*")*\?>|)"
|
||||
R"(</?\w+(\s+\w+\s*=\s*"[^"]*")*\s*>)"
|
||||
|
|
|
@ -247,7 +247,7 @@ public:
|
|||
|
||||
class filter_stack {
|
||||
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) {}
|
||||
|
||||
|
|
|
@ -27,9 +27,10 @@
|
|||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "views_vtab.hh"
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "base/injector.bind.hh"
|
||||
|
@ -40,8 +41,6 @@
|
|||
#include "sql_util.hh"
|
||||
#include "view_curses.hh"
|
||||
|
||||
using namespace std;
|
||||
|
||||
template<>
|
||||
struct from_sqlite<lnav_view_t> {
|
||||
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) {
|
||||
return text_filter::INCLUDE;
|
||||
} else if (strcasecmp(type_name, "out") == 0) {
|
||||
}
|
||||
if (strcasecmp(type_name, "out") == 0) {
|
||||
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<pair<string, auto_mem<pcre>>> {
|
||||
inline pair<string, auto_mem<pcre>> operator()(int argc,
|
||||
sqlite3_value** val,
|
||||
int argi)
|
||||
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<>
|
||||
struct from_sqlite<std::pair<std::string, auto_mem<pcre>>> {
|
||||
inline std::pair<std::string, auto_mem<pcre>> operator()(
|
||||
int argc, sqlite3_value** val, int argi)
|
||||
{
|
||||
const char* pattern = (const char*) sqlite3_value_text(val[argi]);
|
||||
const char* errptr;
|
||||
|
@ -87,20 +103,19 @@ struct from_sqlite<pair<string, auto_mem<pcre>>> {
|
|||
int eoff;
|
||||
|
||||
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);
|
||||
|
||||
if (code == nullptr) {
|
||||
throw sqlite_func_error(
|
||||
"Invalid regular expression in column {}: {} at offset {}",
|
||||
argi,
|
||||
"Invalid regular expression for pattern: {} at offset {}",
|
||||
errptr,
|
||||
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)
|
||||
{
|
||||
lnav_view_t view_index
|
||||
= (lnav_view_t) distance(std::begin(lnav_data.ld_views), vc.iter);
|
||||
lnav_view_t view_index = (lnav_view_t) std::distance(
|
||||
std::begin(lnav_data.ld_views), vc.iter);
|
||||
textview_curses& tc = *vc.iter;
|
||||
unsigned long width;
|
||||
vis_line_t height;
|
||||
|
@ -284,7 +299,7 @@ CREATE TABLE lnav_views (
|
|||
};
|
||||
|
||||
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* CREATE_STMT = R"(
|
||||
|
@ -358,7 +373,7 @@ struct lnav_view_filter_base {
|
|||
using value_type = text_filter;
|
||||
using pointer = 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;
|
||||
int i_filter_index;
|
||||
|
@ -440,11 +455,12 @@ struct lnav_view_filters
|
|||
static constexpr const char* CREATE_STMT = R"(
|
||||
-- Access lnav's filters through this table.
|
||||
CREATE TABLE lnav_view_filters (
|
||||
view_name TEXT, -- The name of the view.
|
||||
filter_id INTEGER DEFAULT 0, -- The filter identifier.
|
||||
enabled INTEGER DEFAULT 1, -- Indicates if the filter is enabled/disabled.
|
||||
type TEXT DEFAULT 'out', -- The type of filter (i.e. in/out).
|
||||
pattern TEXT -- The filter pattern.
|
||||
view_name TEXT, -- The name of the view.
|
||||
filter_id INTEGER DEFAULT 0, -- The filter identifier.
|
||||
enabled INTEGER DEFAULT 1, -- Indicates if the filter is enabled/disabled.
|
||||
type TEXT DEFAULT 'out', -- The type of filter (i.e. in/out).
|
||||
language TEXT DEFAULT 'regex', -- The filter language.
|
||||
pattern TEXT -- The filter pattern.
|
||||
);
|
||||
)";
|
||||
|
||||
|
@ -481,6 +497,18 @@ CREATE TABLE lnav_view_filters (
|
|||
}
|
||||
break;
|
||||
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(
|
||||
ctx, tf->get_id().c_str(), -1, SQLITE_TRANSIENT);
|
||||
break;
|
||||
|
@ -495,23 +523,75 @@ CREATE TABLE lnav_view_filters (
|
|||
nonstd::optional<int64_t> _filter_id,
|
||||
nonstd::optional<bool> enabled,
|
||||
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];
|
||||
text_sub_source* tss = tc.get_sub_source();
|
||||
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) {
|
||||
throw sqlite_func_error("Too many filters");
|
||||
}
|
||||
auto pf = make_shared<pcre_filter>(
|
||||
type.value_or(text_filter::type_t::EXCLUDE),
|
||||
pattern.first,
|
||||
*filter_index,
|
||||
pattern.second.release());
|
||||
fs.add_filter(pf);
|
||||
std::shared_ptr<text_filter> tf;
|
||||
switch (lang.value_or(filter_lang_t::REGEX)) {
|
||||
case filter_lang_t::REGEX: {
|
||||
auto pattern
|
||||
= from_sqlite<std::pair<std::string, auto_mem<pcre>>>()(
|
||||
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)) {
|
||||
pf->disable();
|
||||
tf->disable();
|
||||
}
|
||||
tss->text_filters_changed();
|
||||
tc.set_needs_update();
|
||||
|
@ -545,13 +625,14 @@ CREATE TABLE lnav_view_filters (
|
|||
int64_t new_filter_id,
|
||||
bool enabled,
|
||||
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 filter_index = rowid & 0xffffffffLL;
|
||||
textview_curses& tc = lnav_data.ld_views[view_index];
|
||||
text_sub_source* tss = tc.get_sub_source();
|
||||
filter_stack& fs = tss->get_filters();
|
||||
auto& fs = tss->get_filters();
|
||||
auto iter = fs.begin();
|
||||
for (; iter != fs.end(); ++iter) {
|
||||
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) {
|
||||
tab->zErrMsg
|
||||
|
@ -567,17 +648,55 @@ CREATE TABLE lnav_view_filters (
|
|||
return SQLITE_ERROR;
|
||||
}
|
||||
|
||||
tf->lf_deleted = true;
|
||||
tss->text_filters_changed();
|
||||
if (lang == filter_lang_t::SQL && tf->get_index() == 0) {
|
||||
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);
|
||||
|
||||
auto pf = make_shared<pcre_filter>(
|
||||
type, pattern.first, tf->get_index(), pattern.second.release());
|
||||
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();
|
||||
|
||||
if (!enabled) {
|
||||
pf->disable();
|
||||
auto pattern
|
||||
= 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) {
|
||||
(*iter)->disable();
|
||||
}
|
||||
|
||||
*iter = pf;
|
||||
tss->text_filters_changed();
|
||||
tc.set_needs_update();
|
||||
|
||||
|
|
|
@ -656,7 +656,7 @@ struct vtab_module : public vtab_module_base {
|
|||
rowid,
|
||||
argv,
|
||||
std::make_index_sequence<sizeof...(Args)>{});
|
||||
} catch (from_sqlite_conversion_error& e) {
|
||||
} catch (const from_sqlite_conversion_error& e) {
|
||||
tab->zErrMsg = sqlite3_mprintf(
|
||||
"Expecting an %s for column number %d", e.e_type, e.e_argi);
|
||||
return SQLITE_ERROR;
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
#endif
|
||||
|
||||
#include "config.h"
|
||||
#include "fmt/format.h"
|
||||
#include "yajl/api/yajl_gen.h"
|
||||
#include "yajlpp/json_ptr.hh"
|
||||
|
||||
|
@ -450,10 +451,9 @@ json_ptr_walk::current_ptr()
|
|||
if (this->jpw_array_indexes[lpc] == -1) {
|
||||
retval.append(this->jpw_keys[lpc]);
|
||||
} else {
|
||||
char num[64];
|
||||
|
||||
snprintf(num, sizeof(num), "%d", this->jpw_array_indexes[lpc]);
|
||||
retval.append(num);
|
||||
fmt::format_to(std::back_inserter(retval),
|
||||
FMT_STRING("{}"),
|
||||
this->jpw_array_indexes[lpc]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
std::string source, const struct json_path_container* handlers)
|
||||
: ypc_source(std::move(source)), ypc_handlers(handlers)
|
||||
|
@ -1073,3 +1113,13 @@ dump_schema_to(const json_path_container& jpc,
|
|||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
};
|
||||
nonstd::optional<int> to_enum_value(const string_fragment& sf) const;
|
||||
|
||||
yajl_gen_status gen(yajlpp_gen_context& ygc, yajl_gen handle) const;
|
||||
yajl_gen_status gen_schema(yajlpp_gen_context& ygc) const;
|
||||
|
@ -183,31 +172,7 @@ struct json_path_handler_base {
|
|||
OBJECT,
|
||||
};
|
||||
|
||||
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::vector<schema_type_t> get_types() const;
|
||||
|
||||
std::string jph_property;
|
||||
pcrepp jph_regex;
|
||||
|
@ -607,15 +572,7 @@ public:
|
|||
return this->yg_handle.in();
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
string_fragment to_string_fragment();
|
||||
|
||||
private:
|
||||
auto_mem<yajl_gen_t> yg_handle;
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
CREATE VIEW web_status AS
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
run_test ${lnav_test} -n \
|
||||
|
@ -441,6 +441,14 @@ view_name,filter_id,hits
|
|||
log,1,2
|
||||
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 \
|
||||
-c ";SELECT * FROM access_log LIMIT 0" \
|
||||
-c ':switch-to-view db' \
|
||||
|
|
Loading…
Reference in New Issue