[build] some clang-tidy fixes

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

@ -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'
...

@ -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;
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;
}
}
line_range intersection(const struct line_range& other) const;
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) {
}
if (delta < 1min) {
return "just now";
} else if (delta < 2min) {
}
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) {
}
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";
} else if (delta < 24h) {
fmt = "%d hours ago";
amount = std::chrono::duration_cast<std::chrono::hours>(delta).count();
} else if (delta < 48h) {
}
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";
} else if (delta < 365 * 24h) {
fmt = "%d days ago";
amount = delta / 24h;
} else if (delta < (2 * 365 * 24h)) {
}
if (delta < 365 * 24h) {
return fmt::format(FMT_STRING("{} days ago"), delta / 24h);
}
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);
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)

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;
}
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()]);
}
size_t get_min_count(size_t max) const;
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,362 +8,178 @@
#ifndef FMT_COMPILE_H_
#define FMT_COMPILE_H_
#include <vector>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace detail {
// A compile-time string which is compiled into fast formatting code.
class compiled_string {};
template <typename S>
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
/**
\rst
Converts a string literal *s* into a format string that will be parsed at
compile time and converted into efficient formatting code. Requires C++17
``constexpr if`` compiler support.
**Example**::
// Converts 42 into std::string using the most efficient method and no
// runtime format string processing.
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
*/
#define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::detail::compiled_string)
template <typename T, typename... Tail>
const T& first(const T& value, const Tail&...) {
return value;
}
// Part of a compiled format string. It can be either literal text or a
// replacement field.
template <typename Char> struct format_part {
enum class kind { arg_index, arg_name, text, replacement };
// An output iterator that counts the number of objects written to it and
// discards them.
class counting_iterator {
private:
size_t count_;
struct replacement {
arg_ref<Char> arg_id;
dynamic_format_specs<Char> specs;
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&) {}
};
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;
counting_iterator() : count_(0) {}
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
: part_kind(k), val(v) {}
size_t count() const { return count_; }
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);
counting_iterator& operator++() {
++count_;
return *this;
}
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
return format_part(kind::text, text);
counting_iterator operator++(int) {
auto it = *this;
++*this;
return it;
}
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
return format_part(kind::replacement, repl);
friend counting_iterator operator+(counting_iterator it, difference_type n) {
it.count_ += static_cast<size_t>(n);
return it;
}
value_type operator*() const { return {}; }
};
template <typename Char> struct part_counter {
unsigned num_parts = 0;
template <typename Char, typename InputIt>
inline counting_iterator copy_str(InputIt begin, InputIt end,
counting_iterator it) {
return it + (end - begin);
}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end) ++num_parts;
}
template <typename OutputIt> class truncating_iterator_base {
protected:
OutputIt out_;
size_t limit_;
size_t count_ = 0;
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;
}
truncating_iterator_base() : out_(), limit_(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;
}
truncating_iterator_base(OutputIt out, size_t limit)
: out_(out), limit_(limit) {}
FMT_CONSTEXPR void on_error(const char*) {}
public:
using iterator_category = std::output_iterator_tag;
using value_type = typename std::iterator_traits<OutputIt>::value_type;
using difference_type = std::ptrdiff_t;
using pointer = void;
using reference = void;
using _Unchecked_type =
truncating_iterator_base; // Mark iterator as checked.
OutputIt base() const { return out_; }
size_t count() const { return count_; }
};
// Counts the number of parts in a format string.
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>;
// 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;
PartHandler handler_;
part part_;
basic_string_view<Char> format_str_;
basic_format_parse_context<Char> parse_context_;
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:
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)}));
}
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
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;
}
truncating_iterator() = default;
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
part_ = part::make_arg_name(id);
return 0;
}
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
part_.arg_id_end = ptr;
handler_(part_);
truncating_iterator& operator++() {
if (this->count_++ < this->limit_) ++this->out_;
return *this;
}
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);
truncating_iterator operator++(int) {
auto it = *this;
++*this;
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;
}
}
value_type& operator*() const {
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
}
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>>;
template <typename OutputIt>
class truncating_iterator<OutputIt, std::true_type>
: public truncating_iterator_base<OutputIt> {
public:
truncating_iterator() = default;
parts_container compiled_parts;
truncating_iterator(OutputIt out, size_t limit)
: truncating_iterator_base<OutputIt>(out, limit) {}
explicit compiled_format_base(basic_string_view<char_type> format_str) {
compile_format_string<false>(format_str,
[this](const format_part<char_type>& part) {
compiled_parts.push_back(part);
});
template <typename T> truncating_iterator& operator=(T val) {
if (this->count_++ < this->limit_) *this->out_++ = val;
return *this;
}
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;
truncating_iterator& operator++() { return *this; }
truncating_iterator& operator++(int) { return *this; }
truncating_iterator& operator*() { return *this; }
};
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;
}
// A compile-time string which is compiled into fast formatting code.
class compiled_string {};
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>;
struct is_compiled_string : std::is_base_of<compiled_string, S> {};
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
/**
\rst
Converts a string literal *s* into a format string that will be parsed at
compile time and converted into efficient formatting code. Requires C++17
``constexpr if`` compiler support.
// Workaround for old compilers. Format string compilation will not be
// 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);
**Example**::
// Converts 42 into std::string using the most efficient method and no
// runtime format string processing.
std::string s = fmt::format(FMT_COMPILE("{}"), 42);
\endrst
*/
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
# define FMT_COMPILE(s) \
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
#else
static const unsigned num_format_parts = 1;
# define FMT_COMPILE(s) FMT_STRING(s)
#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;
#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 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) {}
};
template <typename T, typename... Tail>
const T& first(const T& value, const Tail&...) {
return value;
}
#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);
}
}
}
#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...));
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)>
OutputIt format_to(OutputIt out, const S&, const Args&... args) {
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
return format_to(out, compiled, args...);
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
detail::unknown_format>()) {
return format_to(out,
static_cast<basic_string_view<typename S::char_type>>(S()),
std::forward<Args>(args)...);
} else {
return format_to(out, compiled, std::forward<Args>(args)...);
}
}
#endif
template <typename OutputIt, typename CompiledFormat, typename... Args>
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...);
template <typename OutputIt, 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& 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_;
public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(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();
}
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
.. 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".
**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...));
}
};
/**
\rst
Constructs a :class:`std::system_error` object with the description
of the form
.. 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".
**Example**::
// 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
// 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 ostream final : private detail::buffer<char> {
/** 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;
/**
\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) {}
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')
printf_arg_formatter(OutputIt iter, format_specs& s, context_type& ctx)
: base{iter, s, locale_ref()}, context_(ctx) {}
OutputIt operator()(monostate value) { return base::operator()(value); }
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 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>
struct is_range_<
T, conditional_t<false,
conditional_helper<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>,
void>> : std::true_type {};
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 ? " \"{}\"" : "\"{}\"";
// 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 wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
return add_space ? L" \"{}\"" : L"\"{}\"";
inline auto needs_escape(uint32_t cp) -> bool {
return cp < 0x20 || cp == 0x7f || cp == '"' || cp == '\\' ||
!is_printable(cp);
}
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
return add_space ? " '{}'" : "'{}'";
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};
}
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'";
inline auto find_escape(const char* begin, const char* end)
-> find_escape_result<char> {
if (!is_utf8()) return find_escape<char>(begin, end);
auto result = find_escape_result<char>{end, nullptr, 0};
for_each_codepoint(string_view(begin, to_unsigned(end - begin)),
[&](uint32_t cp, string_view sv) {
if (needs_escape(cp)) {
result = {sv.begin(), sv.end(), cp};
return false;
}
return true;
});
return result;
}
template <typename Char, typename OutputIt>
auto write_range_entry(OutputIt out, basic_string_view<Char> str) -> OutputIt {
*out++ = '"';
auto begin = str.begin(), end = str.end();
do {
auto escape = find_escape(begin, end);
out = copy_str<Char>(begin, escape.begin, out);
begin = escape.end;
if (!begin) break;
auto c = static_cast<Char>(escape.cp);
switch (escape.cp) {
case '\n':
*out++ = '\\';
c = 'n';
break;
case '\r':
*out++ = '\\';
c = 'r';
break;
case '\t':
*out++ = '\\';
c = 't';
break;
case '"':
FMT_FALLTHROUGH;
case '\\':
*out++ = '\\';
break;
default:
if (is_utf8()) {
if (escape.cp < 0x100) {
out = format_to(out, "\\x{:02x}", escape.cp);
continue;
}
if (escape.cp < 0x10000) {
out = format_to(out, "\\u{:04x}", escape.cp);
continue;
}
if (escape.cp < 0x110000) {
out = format_to(out, "\\U{:08x}", escape.cp);
continue;
}
}
for (Char escape_char : basic_string_view<Char>(
escape.begin, to_unsigned(escape.end - escape.begin))) {
out = format_to(
out, "\\x{:02x}",
static_cast<typename std::make_unsigned<Char>::type>(escape_char));
}
continue;
}
*out++ = c;
} while (begin != end);
*out++ = '"';
return out;
}
template <typename Char, typename OutputIt, typename T,
FMT_ENABLE_IF(std::is_convertible<T, std_string_view<char>>::value)>
inline auto write_range_entry(OutputIt out, const T& str) -> OutputIt {
auto sv = std_string_view<Char>(str);
return write_range_entry<Char>(out, basic_string_view<Char>(sv));
}
template <typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(std::is_same<Arg, Char>::value)>
OutputIt write_range_entry(OutputIt out, const Arg v) {
*out++ = '\'';
*out++ = v;
*out++ = '\'';
return out;
}
template <
typename Char, typename OutputIt, typename Arg,
FMT_ENABLE_IF(!is_std_string_like<typename std::decay<Arg>::type>::value &&
!std::is_same<Arg, Char>::value)>
OutputIt write_range_entry(OutputIt out, const Arg& v) {
return write<Char>(out, v);
}
} // namespace detail
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);
return ctx.begin();
}
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();
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) {
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 (i > 0) out = detail::write_delimiter(out);
out = detail::write_range_entry<Char>(out, *it);
++i;
}
if (formatting.add_prepostfix_space) *out++ = ' ';
return detail::copy(formatting.postfix, out);
*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_arg_join : detail::view {
template <typename Char, typename... T> struct tuple_join_view : 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} {}
tuple_join_view(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> {
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 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

@ -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];
value_out.clear();
if (gmtime_r(&bucket.b_time, &bucket_tm) != nullptr) {
strftime(
tm_buffer, sizeof(tm_buffer), " %a %b %d %H:%M:%S ", &bucket_tm);
fmt::format_to(std::back_inserter(value_out),
FMT_STRING(" {:%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);
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},

@ -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();
snprintf(example_txt,
sizeof(example_txt),
SQL_EXAMPLE,
format_name,
format_name);
example_al.with_ansi_string(example_txt);
const auto* format_name = lf->get_format()->get_name().get();
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<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<pair<string, auto_mem<pcre>>> {
inline pair<string, auto_mem<pcre>> operator()(int argc,
sqlite3_value** val,
int argi)
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);
throw sqlite_func_error("Invalid SQL: {}", errmsg);
}
auto set_res = lnav_data.ld_log_source.set_sql_filter(
clause, stmt.release());
if (set_res.isErr()) {
throw sqlite_func_error("filter expression failed with: {}",
set_res.unwrapErr());
}
*iter = lnav_data.ld_log_source.get_sql_filter().value();
} else {
tf->lf_deleted = true;
tss->text_filters_changed();
auto pf = make_shared<pcre_filter>(
type, pattern.first, tf->get_index(), pattern.second.release());
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) {
pf->disable();
(*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…
Cancel
Save