[services] initial pass at injecting services and a bunch of other stuff

This commit is contained in:
Timothy Stack 2021-01-23 13:00:51 -08:00
parent 8633afdff3
commit cac1175973
64 changed files with 11427 additions and 6307 deletions

View File

@ -234,6 +234,7 @@ add_library(diag STATIC
ansi_scrubber.cc
archive_manager.cc
attr_line.cc
base/isc.cc
bin2c.hh
bookmarks.cc
bottom_status_source.cc
@ -392,6 +393,7 @@ add_library(diag STATIC
base/injector.bind.hh
base/intern_string.hh
base/is_utf8.hh
base/isc.hh
k_merge_tree.h
log_actions.hh
log_data_helper.hh
@ -471,19 +473,21 @@ add_library(diag STATIC
ghc/fs_std_fwd.hpp
ghc/fs_std_impl.hpp
fmtlib/posix.cc
fmtlib/fmt/time.h
fmtlib/fmt/ostream.h
fmtlib/fmt/format-inl.h
fmtlib/fmt/ranges.h
fmtlib/fmt/core.h
fmtlib/fmt/locale.h
fmtlib/format.cc
fmtlib/os.cc
fmtlib/fmt/chrono.h
fmtlib/fmt/color.h
fmtlib/fmt/printf.h
fmtlib/fmt/posix.h
fmtlib/fmt/compile.h
fmtlib/fmt/core.h
fmtlib/fmt/format-inl.h
fmtlib/fmt/format.h
fmtlib/format.cc
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
ww898/cp_utf8.hpp

View File

@ -380,17 +380,6 @@ noinst_HEADERS = \
ghc/fs_std.hpp \
ghc/fs_std_fwd.hpp \
ghc/fs_std_impl.hpp \
fmtlib/fmt/time.h \
fmtlib/fmt/ostream.h \
fmtlib/fmt/format-inl.h \
fmtlib/fmt/ranges.h \
fmtlib/fmt/core.h \
fmtlib/fmt/locale.h \
fmtlib/fmt/chrono.h \
fmtlib/fmt/color.h \
fmtlib/fmt/printf.h \
fmtlib/fmt/posix.h \
fmtlib/fmt/format.h \
ww898/cp_utf8.hpp
nodist_libdiag_a_SOURCES = \

View File

@ -31,7 +31,6 @@
#include "config.h"
#include <glob.h>
#include <unistd.h>
#if HAVE_ARCHIVE_H
@ -44,6 +43,7 @@
#include "fmt/format.h"
#include "base/injector.hh"
#include "base/lnav_log.hh"
#include "base/humanize.hh"
#include "lnav_util.hh"
#include "archive_manager.hh"
@ -53,8 +53,6 @@ namespace fs = ghc::filesystem;
namespace archive_manager {
const static size_t MIN_FREE_SPACE = 32 * 1024 * 1024;
class archive_lock {
public:
class guard {
@ -184,10 +182,25 @@ copy_data(const std::string& filename,
{
int r;
const void *buff;
size_t size, last_space_check = 0, total = 0;
size_t size, total = 0, next_space_check = 0;
la_int64_t offset;
for (;;) {
if (total >= next_space_check) {
auto& cfg = injector::get<const config&>();
auto tmp_space = fs::space(entry_path);
if (tmp_space.available < cfg.amc_min_free_space) {
return Err(fmt::format(
FMT_STRING("available space on disk ({}) is below the minimum-free threshold ({}). Unable to unpack '{}' to '{}'"),
humanize::file_size(tmp_space.available),
humanize::file_size(cfg.amc_min_free_space),
entry_path.filename().string(),
entry_path.parent_path().string()));
}
next_space_check += 1024 * 1024;
}
r = archive_read_data_block(ar, &buff, &size, &offset);
if (r == ARCHIVE_EOF) {
return Ok();
@ -207,17 +220,6 @@ copy_data(const std::string& filename,
total += size;
ep->ep_out_size.fetch_add(size);
if ((total - last_space_check) > (1024 * 1024)) {
auto tmp_space = fs::space(entry_path);
if (tmp_space.available < MIN_FREE_SPACE) {
return Err(fmt::format(
"{} -- available space too low: %lld",
entry_path.string(),
tmp_space.available));
}
}
}
}
@ -353,7 +355,7 @@ void cleanup_cache()
(void) std::async(std::launch::async, []() {
auto now = std::chrono::system_clock::now();
auto cache_path = archive_cache_path();
auto cfg = injector::get<const config&>();
auto& cfg = injector::get<const config&>();
std::vector<fs::path> to_remove;
log_debug("cache-ttl %d", cfg.amc_cache_ttl.count());

View File

@ -37,7 +37,8 @@
namespace archive_manager {
struct config {
std::chrono::seconds amc_cache_ttl;
int64_t amc_min_free_space{32 * 1024 * 1024};
std::chrono::seconds amc_cache_ttl{std::chrono::hours(48)};
};
}

View File

@ -17,6 +17,7 @@ noinst_HEADERS = \
injector.bind.hh \
intern_string.hh \
is_utf8.hh \
isc.hh \
lnav_log.hh \
math_util.hh \
opt_util.hh \
@ -29,6 +30,7 @@ libbase_a_SOURCES = \
humanize.cc \
intern_string.cc \
is_utf8.cc \
isc.cc \
lnav_log.cc \
string_util.cc \
time_util.cc

View File

@ -39,8 +39,6 @@
#include "time_util.hh"
extern const char *std_time_fmt[];
struct date_time_scanner {
date_time_scanner() : dts_keep_base_tz(false),
dts_local_time(false),

View File

@ -36,6 +36,33 @@
namespace injector {
namespace details {
template<typename I, typename R, typename ...Args>
std::function<std::shared_ptr<I>()> create_factory(R (*)(Args...)) {
return []() {
return std::make_shared<I>(::injector::get<Args>()...);
};
}
template<typename I,
std::enable_if_t<has_injectable<I>::value, bool> = true>
std::function<std::shared_ptr<I>()> create_factory() {
typename I::injectable *i = nullptr;
return create_factory<I>(i);
}
template<typename I,
std::enable_if_t<!has_injectable<I>::value, bool> = true>
std::function<std::shared_ptr<I>()> create_factory() noexcept {
return []() {
return std::make_shared<I>();
};
}
}
template<typename T, typename...Annotations>
struct bind : singleton_storage<T, Annotations...> {
static bool to_singleton() noexcept {
@ -51,11 +78,15 @@ struct bind : singleton_storage<T, Annotations...> {
return true;
}
static bool to_instance(T* data) noexcept {
singleton_storage<T, Annotations...>::ss_data = data;
return true;
}
template<typename I>
static bool to(Impl<I> i) {
singleton_storage<T, Annotations...>::ss_factory = []() {
return std::make_shared<I>();
};
static bool to() noexcept {
singleton_storage<T, Annotations...>::ss_factory =
details::create_factory<I>();
return true;
}
};
@ -64,32 +95,32 @@ template<typename T>
struct bind_multiple : multiple_storage<T> {
bind_multiple() noexcept = default;
template<typename I, typename R, typename ...Args>
bind_multiple& add(R (*)(Args...)) {
multiple_storage<T>::ms_factories[typeid(I).name()] = []() {
return std::make_shared<I>(::injector::get<Args>()...);
};
return *this;
}
template<typename I,
std::enable_if_t<has_injectable<I>::value, bool> = true>
bind_multiple& add() {
typename I::injectable *i = nullptr;
return this->add<I>(i);
}
template<typename I,
std::enable_if_t<!has_injectable<I>::value, bool> = true>
template<typename I>
bind_multiple& add() noexcept {
multiple_storage<T>::ms_factories[typeid(I).name()] = []() {
return std::make_shared<I>();
multiple_storage<T>::ms_factories[typeid(I).name()] =
details::create_factory<I>();
return *this;
}
template<typename I, typename...Annotations>
bind_multiple& add_singleton() noexcept {
auto factory = details::create_factory<I>();
auto single = factory();
if (sizeof...(Annotations) > 0) {
bind<T, Annotations...>::to_instance(single.get());
}
bind<I, Annotations...>::to_instance(single.get());
multiple_storage<T>::ms_factories[typeid(I).name()] = [single]() {
return single;
};
return *this;
}
private:
};
}

View File

@ -127,11 +127,11 @@ struct is_vector : std::false_type {};
template<class T>
struct is_vector<std::vector<T>> : std::true_type {};
template<typename T,
template<typename T, typename...Annotations,
std::enable_if_t<is_shared_ptr<T>::value, bool> = true>
T get()
{
return singleton_storage<typename T::element_type>::create();
return singleton_storage<typename T::element_type, Annotations...>::create();
}
template<typename T,

54
src/base/isc.cc Normal file
View File

@ -0,0 +1,54 @@
/**
* Copyright (c) 2021, Timothy Stack
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Timothy Stack nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @file isc.cc
*/
#include "config.h"
#include "isc.hh"
namespace isc {
void *service::run()
{
log_info("isc thread started");
while (this->s_looping) {
mstime_t current_time = getmstime();
auto timeout = this->compute_timeout(current_time);
this->s_port.process_for(timeout);
this->loop_body();
}
log_info("isc thread exiting");
return nullptr;
}
}

188
src/base/isc.hh Normal file
View File

@ -0,0 +1,188 @@
/**
* Copyright (c) 2021, Timothy Stack
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Timothy Stack nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @file isc.hh
*/
#include <deque>
#include <chrono>
#include <mutex>
#include <thread>
#include <condition_variable>
#include "injector.hh"
#include "time_util.hh"
#include "safe/safe.h"
#ifndef lnav_isc_hh
#define lnav_isc_hh
namespace isc {
struct msg {
std::function<void()> m_callback;
};
inline msg empty_msg() {
return { []() { } };
}
class msg_port {
public:
msg_port() = default;
void send(msg&& m) {
safe::WriteAccess<safe_message_list, std::unique_lock> writable_msgs(this->mp_messages);
writable_msgs->emplace_back(m);
this->sp_cond.notify_all();
}
template<class Rep, class Period>
void process_for(const std::chrono::duration<Rep, Period>& rel_time) {
std::deque<msg> tmp_msgs;
{
safe::WriteAccess<safe_message_list, std::unique_lock> writable_msgs(
this->mp_messages);
if (writable_msgs->empty()) {
this->sp_cond.template wait_for(writable_msgs.lock, rel_time);
}
tmp_msgs.swap(*writable_msgs);
}
while (!tmp_msgs.empty()) {
auto &m = tmp_msgs.front();
m.m_callback();
tmp_msgs.pop_front();
}
}
private:
using message_list = std::deque<msg>;
using safe_message_list = safe::Safe<message_list>;
std::condition_variable sp_cond;
safe_message_list mp_messages;
};
class service {
public:
virtual ~service() = default;
void start() {
log_debug("starting service thread");
this->s_thread = std::thread(&service::run, this);
this->s_started = true;
};
void stop() {
if (this->s_started) {
this->s_looping = false;
this->s_port.send(empty_msg());
log_debug("waiting for service thread");
this->s_thread.join();
log_debug("service thread joined");
this->s_started = false;
}
};
msg_port& get_port() { return this->s_port; };
protected:
void *run();
virtual void loop_body() {};
virtual std::chrono::milliseconds compute_timeout(mstime_t current_time) const {
using namespace std::literals::chrono_literals;
return 1s;
};
bool s_started{false};
std::thread s_thread;
std::atomic<bool> s_looping{true};
msg_port s_port;
};
using service_list = std::vector<std::shared_ptr<service>>;
struct service_guard {
service_guard(service_list servs)
: sg_service_list(servs) {
for (auto& serv : servs) {
serv->start();
}
};
~service_guard() {
for (auto& serv : this->sg_service_list) {
serv->stop();
}
}
private:
service_list sg_service_list;
};
template<typename T, typename Service, typename...Annotations>
struct to {
void send(std::function<void(T)> cb) {
auto& service = injector::get<isc::service&, Service>();
service.get_port().send({
[cb]() {
cb(injector::get<T, Service, Annotations...>());
}
});
}
template<class Rep, class Period>
void send_and_wait(std::function<void(T)> cb,
const std::chrono::duration<Rep, Period>& rel_time) {
auto& service = injector::get<isc::service&, Service>();
msg_port reply_port;
service.get_port().send({
[cb, &reply_port]() {
cb(injector::get<T, Service, Annotations...>());
reply_port.send(empty_msg());
}
});
reply_port.template process_for(rel_time);
}
void send_and_wait(std::function<void(T)> cb) {
using namespace std::literals::chrono_literals;
this->send_and_wait(cb, 48h);
}
};
}
#endif

View File

@ -71,7 +71,7 @@ void bottom_status_source::update_line_number(listview_curses *lc)
void bottom_status_source::update_search_term(textview_curses &tc)
{
auto &sf = this->bss_fields[BSF_SEARCH_TERM];
auto search_term = tc.get_last_search();
auto search_term = tc.get_current_search();
if (search_term.empty()) {
sf.clear();
@ -115,7 +115,7 @@ void bottom_status_source::update_marks(listview_curses *lc)
if (bm.find(&textview_curses::BM_SEARCH) != bm.end()) {
bookmark_vector<vis_line_t> &bv = bm[&textview_curses::BM_SEARCH];
if (!bv.empty() || !tc->get_last_search().empty()) {
if (!bv.empty() || !tc->get_current_search().empty()) {
bookmark_vector<vis_line_t>::iterator lb;
lb = std::lower_bound(bv.begin(), bv.end(), tc->get_top());

View File

@ -40,6 +40,7 @@
#include "lnav_util.hh"
#include "sql_util.hh"
#include "lnav_config.hh"
#include "service_tags.hh"
#include "command_executor.hh"
#include "db_sub_source.hh"
@ -685,7 +686,10 @@ void execute_init_commands(exec_context &ec, vector<pair<Result<string, string>,
lnav_data.ld_pt_max_time);
lnav_data.ld_active_files.fc_file_names[lnav_data.ld_pt_search]
.with_fd(pt->copy_fd());
lnav_data.ld_curl_looper.add_request(pt);
isc::to<curl_looper&, services::curl_streamer_t>()
.send([pt](auto& clooper) {
clooper.add_request(pt);
});
#endif
}

View File

@ -91,30 +91,9 @@ int curl_request::debug_cb(CURL *handle,
return 0;
}
void *curl_looper::run()
{
log_info("curl looper thread started");
while (this->cl_looping) {
this->loop_body();
}
log_info("curl looper thread exiting");
return nullptr;
}
void curl_looper::loop_body()
{
mstime_t current_time = getmstime();
int timeout = this->compute_timeout(current_time);
if (this->cl_handle_to_request.empty()) {
std::unique_lock<std::mutex> mg(this->cl_mutex);
if (this->cl_new_requests.empty() && this->cl_close_requests.empty()) {
mstime_t deadline = current_time + timeout;
this->cl_cond.wait_for(mg, std::chrono::milliseconds(deadline));
}
}
this->perform_io();
@ -132,13 +111,13 @@ void curl_looper::perform_io()
}
mstime_t current_time = getmstime();
int timeout = this->compute_timeout(current_time);
auto timeout = this->compute_timeout(current_time);
int running_handles;
curl_multi_wait(this->cl_curl_multi,
nullptr,
0,
timeout,
timeout.count(),
nullptr);
curl_multi_perform(this->cl_curl_multi, &running_handles);
}
@ -158,8 +137,6 @@ void curl_looper::requeue_requests(mstime_t up_to_time)
}
void curl_looper::check_for_new_requests() {
std::unique_lock<std::mutex> mg(this->cl_mutex);
while (!this->cl_new_requests.empty()) {
auto cr = this->cl_new_requests.back();
@ -204,8 +181,6 @@ void curl_looper::check_for_new_requests() {
}
this->cl_close_requests.pop_back();
this->cl_cond.notify_all();
}
}
@ -233,16 +208,11 @@ void curl_looper::check_for_finished_requests()
if (delay_ms < 0) {
log_info("%s:curl_request %p finished, deleting...",
cr->get_name().c_str(), cr.get());
{
std::unique_lock<std::mutex> mg(this->cl_mutex);
auto all_iter = find(this->cl_all_requests.begin(),
this->cl_all_requests.end(),
cr);
if (all_iter != this->cl_all_requests.end()) {
this->cl_all_requests.erase(all_iter);
}
this->cl_cond.notify_all();
auto all_iter = find(this->cl_all_requests.begin(),
this->cl_all_requests.end(),
cr);
if (all_iter != this->cl_all_requests.end()) {
this->cl_all_requests.erase(all_iter);
}
}
else {
@ -257,4 +227,21 @@ void curl_looper::check_for_finished_requests()
}
}
std::chrono::milliseconds curl_looper::compute_timeout(mstime_t current_time) const
{
std::chrono::milliseconds retval = 1s;
if (!this->cl_handle_to_request.empty()) {
retval = 1ms;
} else if (!this->cl_poll_queue.empty()) {
retval = std::max(
1ms,
std::chrono::milliseconds(this->cl_poll_queue.front().first - current_time));
}
ensure(retval.count() > 0);
return retval;
}
#endif

View File

@ -38,6 +38,8 @@
#include <utility>
#include <vector>
#include "base/isc.hh"
#if !defined(HAVE_LIBCURL)
typedef int CURLcode;
@ -141,39 +143,13 @@ protected:
int cr_completions;
};
class curl_looper {
class curl_looper : public isc::service {
public:
curl_looper()
: cl_started(false),
cl_looping(true),
cl_curl_multi(curl_multi_cleanup) {
: cl_curl_multi(curl_multi_cleanup) {
this->cl_curl_multi.reset(curl_multi_init());
};
~curl_looper() {
this->stop();
}
void start() {
this->cl_thread = std::thread(&curl_looper::run, this);
this->cl_started = true;
};
void stop() {
if (this->cl_started) {
this->cl_looping = false;
{
std::unique_lock<std::mutex> mg(this->cl_mutex);
this->cl_cond.notify_all();
}
log_debug("waiting for curl_looper thread");
this->cl_thread.join();
log_debug("curl_looper thread joined");
this->cl_started = false;
}
};
void process_all() {
this->check_for_new_requests();
@ -186,58 +162,33 @@ public:
}
};
void add_request(std::shared_ptr<curl_request> cr) {
std::unique_lock<std::mutex> mg(this->cl_mutex);
void add_request(const std::shared_ptr<curl_request>& cr) {
require(cr != nullptr);
this->cl_all_requests.push_back(cr);
this->cl_new_requests.push_back(cr);
this->cl_cond.notify_all();
this->cl_all_requests.emplace_back(cr);
this->cl_new_requests.emplace_back(cr);
};
void close_request(const std::string &name) {
std::unique_lock<std::mutex> mg(this->cl_mutex);
this->cl_close_requests.push_back(name);
this->cl_cond.notify_all();
this->cl_close_requests.emplace_back(name);
};
private:
protected:
void loop_body() override;
void *run();
void loop_body();
private:
void perform_io();
void check_for_new_requests();
void check_for_finished_requests();
void requeue_requests(mstime_t up_to_time);
std::chrono::milliseconds compute_timeout(mstime_t current_time) const override;
int compute_timeout(mstime_t current_time) const {
int retval = 1000;
if (!this->cl_poll_queue.empty()) {
retval = std::max(
(mstime_t) 1,
this->cl_poll_queue.front().first - current_time);
}
ensure(retval > 0);
return retval;
};
bool cl_started;
std::thread cl_thread;
std::atomic<bool> cl_looping;
auto_mem<CURLM> cl_curl_multi;
std::mutex cl_mutex;
std::condition_variable cl_cond;
std::vector<std::shared_ptr<curl_request>> cl_all_requests;
std::vector<std::shared_ptr<curl_request>> cl_new_requests;
std::vector<std::string> cl_close_requests;
std::map<CURL *, std::shared_ptr<curl_request>> cl_handle_to_request;
std::vector<std::pair<mstime_t, std::shared_ptr<curl_request>>> cl_poll_queue;
};
#endif

View File

@ -75,6 +75,13 @@ void file_collection::regenerate_unique_file_names()
upg.generate();
this->fc_largest_path_length = 0;
for (const auto &pair : this->fc_name_to_errors) {
auto path = ghc::filesystem::path(pair.first).filename().string();
if (path.length() > this->fc_largest_path_length) {
this->fc_largest_path_length = path.length();
}
}
for (const auto &lf : this->fc_files) {
const auto &path = lf->get_unique_path();

View File

@ -146,7 +146,9 @@ size_t files_sub_source::text_line_count()
{
const auto &fc = lnav_data.ld_active_files;
return fc.fc_other_files.size() + fc.fc_files.size();
return fc.fc_name_to_errors.size() +
fc.fc_other_files.size() +
fc.fc_files.size();
}
size_t files_sub_source::text_line_width(textview_curses &curses)
@ -164,6 +166,21 @@ void files_sub_source::text_value_for_line(textview_curses &tc, int line,
std::min(fc.fc_largest_path_length,
std::max((size_t) 40, dim.second - 30));
if (line < fc.fc_name_to_errors.size()) {
auto iter = fc.fc_name_to_errors.begin();
std::advance(iter, line);
auto path = ghc::filesystem::path(iter->first);
auto fn = path.filename().string();
truncate_to(fn, filename_width);
value_out = fmt::format(
FMT_STRING(" {:<{}} {}"),
fn, filename_width, iter->second);
return;
}
line -= fc.fc_name_to_errors.size();
if (line < fc.fc_other_files.size()) {
auto iter = fc.fc_other_files.begin();
std::advance(iter, line);
@ -201,7 +218,6 @@ void files_sub_source::text_attrs_for_line(textview_curses &tc, int line,
string_attrs_t &value_out)
{
bool selected = lnav_data.ld_mode == LNM_FILES && line == tc.get_selection();
int bg = selected ? COLOR_WHITE : COLOR_BLACK;
const auto &fc = lnav_data.ld_active_files;
auto &vcolors = view_colors::singleton();
const auto dim = tc.get_dimensions();
@ -209,13 +225,30 @@ void files_sub_source::text_attrs_for_line(textview_curses &tc, int line,
std::min(fc.fc_largest_path_length,
std::max((size_t) 40, dim.second - 30));
int fg = selected ? COLOR_BLACK : COLOR_WHITE;
value_out.emplace_back(line_range{0, -1}, &view_curses::VC_FOREGROUND,
vcolors.ansi_to_theme_color(fg));
value_out.emplace_back(line_range{0, -1}, &view_curses::VC_BACKGROUND,
vcolors.ansi_to_theme_color(bg));
if (selected) {
value_out.emplace_back(line_range{0, 1}, &view_curses::VC_GRAPHIC, ACS_RARROW);
}
if (line < fc.fc_name_to_errors.size()) {
if (selected) {
value_out.emplace_back(line_range{0, -1},
&view_curses::VC_ROLE,
view_colors::VCR_DISABLED_FOCUSED);
}
value_out.emplace_back(line_range{4 + (int) filename_width, -1},
&view_curses::VC_ROLE_FG,
view_colors::VCR_ERROR);
return;
}
line -= fc.fc_name_to_errors.size();
if (line < fc.fc_other_files.size()) {
if (selected) {
value_out.emplace_back(line_range{0, -1},
&view_curses::VC_ROLE,
view_colors::VCR_DISABLED_FOCUSED);
}
if (line == fc.fc_other_files.size() - 1) {
value_out.emplace_back(line_range{0, -1},
&view_curses::VC_STYLE,
@ -226,6 +259,12 @@ void files_sub_source::text_attrs_for_line(textview_curses &tc, int line,
line -= fc.fc_other_files.size();
if (selected) {
value_out.emplace_back(line_range{0, -1},
&view_curses::VC_ROLE,
view_colors::VCR_FOCUSED);
}
auto& lss = lnav_data.ld_log_source;
auto &lf = fc.fc_files[line];
auto ld_opt = lss.find_data(lf);
@ -240,10 +279,6 @@ void files_sub_source::text_attrs_for_line(textview_curses &tc, int line,
vcolors.ansi_to_theme_color(COLOR_GREEN));
}
if (selected) {
value_out.emplace_back(line_range{0, 1}, &view_curses::VC_GRAPHIC, ACS_RARROW);
}
auto lr = line_range{
(int) filename_width + 3 + 4,
(int) filename_width + 3 + 10,

View File

@ -278,6 +278,13 @@ size_t filter_help_status_source::statusview_fields()
auto &lv = lnav_data.ld_files_view;
auto sel = (int) lv.get_selection();
if (sel < fc.fc_name_to_errors.size()) {
this->fss_help.clear();
return;
}
sel -= fc.fc_name_to_errors.size();
if (sel < fc.fc_other_files.size()) {
this->fss_help.clear();
return;

View File

@ -333,7 +333,6 @@ void filter_sub_source::text_attrs_for_line(textview_curses &tc, int line,
shared_ptr<text_filter> tf = *(fs.begin() + line);
bool selected =
lnav_data.ld_mode == LNM_FILTER && line == tc.get_selection();
int bg = selected ? COLOR_WHITE : COLOR_BLACK;
if (selected) {
value_out.emplace_back(line_range{0, 1}, &view_curses::VC_GRAPHIC,
@ -349,20 +348,22 @@ void filter_sub_source::text_attrs_for_line(textview_curses &tc, int line,
vcolors.ansi_to_theme_color(COLOR_GREEN));
}
int fg = tf->get_type() == text_filter::INCLUDE ? COLOR_GREEN : COLOR_RED;
value_out.emplace_back(line_range{4, 7}, &view_curses::VC_FOREGROUND,
vcolors.ansi_to_theme_color(fg));
int fg_role = tf->get_type() == text_filter::INCLUDE ?
view_colors::VCR_OK : view_colors::VCR_ERROR;
value_out.emplace_back(line_range{4, 7},
&view_curses::VC_ROLE_FG,
fg_role);
value_out.emplace_back(line_range{4, 7}, &view_curses::VC_STYLE, A_BOLD);
value_out.emplace_back(line_range{8, 17}, &view_curses::VC_STYLE, A_BOLD);
value_out.emplace_back(line_range{23, 24}, &view_curses::VC_GRAPHIC,
ACS_VLINE);
fg = selected ? COLOR_BLACK : COLOR_WHITE;
value_out.emplace_back(line_range{0, -1}, &view_curses::VC_FOREGROUND,
vcolors.ansi_to_theme_color(fg));
value_out.emplace_back(line_range{0, -1}, &view_curses::VC_BACKGROUND,
vcolors.ansi_to_theme_color(bg));
if (selected) {
value_out.emplace_back(line_range{0, -1},
&view_curses::VC_ROLE,
view_colors::VCR_FOCUSED);
}
}
size_t filter_sub_source::text_size_for_line(textview_curses &tc, int line,

View File

@ -1,6 +1,21 @@
noinst_HEADERS = \
fmt/chrono.h \
fmt/color.h \
fmt/compile.h \
fmt/core.h \
fmt/format-inl.h \
fmt/format.h \
fmt/locale.h \
fmt/os.h \
fmt/ostream.h \
fmt/posix.h \
fmt/printf.h \
fmt/ranges.h \
fmt/time.h
noinst_LIBRARIES = libcppfmt.a
libcppfmt_a_SOURCES = \
format.cc \
posix.cc
os.cc

File diff suppressed because it is too large Load Diff

View File

@ -12,184 +12,149 @@
FMT_BEGIN_NAMESPACE
#ifdef FMT_DEPRECATED_COLORS
// color and (v)print_colored are deprecated.
enum color { black, red, green, yellow, blue, magenta, cyan, white };
FMT_API void vprint_colored(color c, string_view format, format_args args);
FMT_API void vprint_colored(color c, wstring_view format, wformat_args args);
template <typename... Args>
inline void print_colored(color c, string_view format_str,
const Args & ... args) {
vprint_colored(c, format_str, make_format_args(args...));
}
template <typename... Args>
inline void print_colored(color c, wstring_view format_str,
const Args & ... args) {
vprint_colored(c, format_str, make_format_args<wformat_context>(args...));
}
inline void vprint_colored(color c, string_view format, format_args args) {
char escape[] = "\x1b[30m";
escape[3] = static_cast<char>('0' + c);
std::fputs(escape, stdout);
vprint(format, args);
std::fputs(internal::data::RESET_COLOR, stdout);
}
inline void vprint_colored(color c, wstring_view format, wformat_args args) {
wchar_t escape[] = L"\x1b[30m";
escape[3] = static_cast<wchar_t>('0' + c);
std::fputws(escape, stdout);
vprint(format, args);
std::fputws(internal::data::WRESET_COLOR, stdout);
}
#else
enum class color : uint32_t {
alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215)
aqua = 0x00FFFF, // rgb(0,255,255)
aquamarine = 0x7FFFD4, // rgb(127,255,212)
azure = 0xF0FFFF, // rgb(240,255,255)
beige = 0xF5F5DC, // rgb(245,245,220)
bisque = 0xFFE4C4, // rgb(255,228,196)
black = 0x000000, // rgb(0,0,0)
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
blue = 0x0000FF, // rgb(0,0,255)
blue_violet = 0x8A2BE2, // rgb(138,43,226)
brown = 0xA52A2A, // rgb(165,42,42)
burly_wood = 0xDEB887, // rgb(222,184,135)
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
chartreuse = 0x7FFF00, // rgb(127,255,0)
chocolate = 0xD2691E, // rgb(210,105,30)
coral = 0xFF7F50, // rgb(255,127,80)
cornflower_blue = 0x6495ED, // rgb(100,149,237)
cornsilk = 0xFFF8DC, // rgb(255,248,220)
crimson = 0xDC143C, // rgb(220,20,60)
cyan = 0x00FFFF, // rgb(0,255,255)
dark_blue = 0x00008B, // rgb(0,0,139)
dark_cyan = 0x008B8B, // rgb(0,139,139)
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
dark_gray = 0xA9A9A9, // rgb(169,169,169)
dark_green = 0x006400, // rgb(0,100,0)
dark_khaki = 0xBDB76B, // rgb(189,183,107)
dark_magenta = 0x8B008B, // rgb(139,0,139)
dark_olive_green = 0x556B2F, // rgb(85,107,47)
dark_orange = 0xFF8C00, // rgb(255,140,0)
dark_orchid = 0x9932CC, // rgb(153,50,204)
dark_red = 0x8B0000, // rgb(139,0,0)
dark_salmon = 0xE9967A, // rgb(233,150,122)
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
dark_turquoise = 0x00CED1, // rgb(0,206,209)
dark_violet = 0x9400D3, // rgb(148,0,211)
deep_pink = 0xFF1493, // rgb(255,20,147)
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
dim_gray = 0x696969, // rgb(105,105,105)
dodger_blue = 0x1E90FF, // rgb(30,144,255)
fire_brick = 0xB22222, // rgb(178,34,34)
floral_white = 0xFFFAF0, // rgb(255,250,240)
forest_green = 0x228B22, // rgb(34,139,34)
fuchsia = 0xFF00FF, // rgb(255,0,255)
gainsboro = 0xDCDCDC, // rgb(220,220,220)
ghost_white = 0xF8F8FF, // rgb(248,248,255)
gold = 0xFFD700, // rgb(255,215,0)
golden_rod = 0xDAA520, // rgb(218,165,32)
gray = 0x808080, // rgb(128,128,128)
green = 0x008000, // rgb(0,128,0)
green_yellow = 0xADFF2F, // rgb(173,255,47)
honey_dew = 0xF0FFF0, // rgb(240,255,240)
hot_pink = 0xFF69B4, // rgb(255,105,180)
indian_red = 0xCD5C5C, // rgb(205,92,92)
indigo = 0x4B0082, // rgb(75,0,130)
ivory = 0xFFFFF0, // rgb(255,255,240)
khaki = 0xF0E68C, // rgb(240,230,140)
lavender = 0xE6E6FA, // rgb(230,230,250)
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
lawn_green = 0x7CFC00, // rgb(124,252,0)
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
light_blue = 0xADD8E6, // rgb(173,216,230)
light_coral = 0xF08080, // rgb(240,128,128)
light_cyan = 0xE0FFFF, // rgb(224,255,255)
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
light_gray = 0xD3D3D3, // rgb(211,211,211)
light_green = 0x90EE90, // rgb(144,238,144)
light_pink = 0xFFB6C1, // rgb(255,182,193)
light_salmon = 0xFFA07A, // rgb(255,160,122)
light_sea_green = 0x20B2AA, // rgb(32,178,170)
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
light_slate_gray = 0x778899, // rgb(119,136,153)
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
light_yellow = 0xFFFFE0, // rgb(255,255,224)
lime = 0x00FF00, // rgb(0,255,0)
lime_green = 0x32CD32, // rgb(50,205,50)
linen = 0xFAF0E6, // rgb(250,240,230)
magenta = 0xFF00FF, // rgb(255,0,255)
maroon = 0x800000, // rgb(128,0,0)
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
medium_blue = 0x0000CD, // rgb(0,0,205)
medium_orchid = 0xBA55D3, // rgb(186,85,211)
medium_purple = 0x9370DB, // rgb(147,112,219)
medium_sea_green = 0x3CB371, // rgb(60,179,113)
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
medium_violet_red = 0xC71585, // rgb(199,21,133)
midnight_blue = 0x191970, // rgb(25,25,112)
mint_cream = 0xF5FFFA, // rgb(245,255,250)
misty_rose = 0xFFE4E1, // rgb(255,228,225)
moccasin = 0xFFE4B5, // rgb(255,228,181)
navajo_white = 0xFFDEAD, // rgb(255,222,173)
navy = 0x000080, // rgb(0,0,128)
old_lace = 0xFDF5E6, // rgb(253,245,230)
olive = 0x808000, // rgb(128,128,0)
olive_drab = 0x6B8E23, // rgb(107,142,35)
orange = 0xFFA500, // rgb(255,165,0)
orange_red = 0xFF4500, // rgb(255,69,0)
orchid = 0xDA70D6, // rgb(218,112,214)
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
pale_green = 0x98FB98, // rgb(152,251,152)
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
pale_violet_red = 0xDB7093, // rgb(219,112,147)
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
peach_puff = 0xFFDAB9, // rgb(255,218,185)
peru = 0xCD853F, // rgb(205,133,63)
pink = 0xFFC0CB, // rgb(255,192,203)
plum = 0xDDA0DD, // rgb(221,160,221)
powder_blue = 0xB0E0E6, // rgb(176,224,230)
purple = 0x800080, // rgb(128,0,128)
rebecca_purple = 0x663399, // rgb(102,51,153)
red = 0xFF0000, // rgb(255,0,0)
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
royal_blue = 0x4169E1, // rgb(65,105,225)
saddle_brown = 0x8B4513, // rgb(139,69,19)
salmon = 0xFA8072, // rgb(250,128,114)
sandy_brown = 0xF4A460, // rgb(244,164,96)
sea_green = 0x2E8B57, // rgb(46,139,87)
sea_shell = 0xFFF5EE, // rgb(255,245,238)
sienna = 0xA0522D, // rgb(160,82,45)
silver = 0xC0C0C0, // rgb(192,192,192)
sky_blue = 0x87CEEB, // rgb(135,206,235)
slate_blue = 0x6A5ACD, // rgb(106,90,205)
slate_gray = 0x708090, // rgb(112,128,144)
snow = 0xFFFAFA, // rgb(255,250,250)
spring_green = 0x00FF7F, // rgb(0,255,127)
steel_blue = 0x4682B4, // rgb(70,130,180)
tan = 0xD2B48C, // rgb(210,180,140)
teal = 0x008080, // rgb(0,128,128)
thistle = 0xD8BFD8, // rgb(216,191,216)
tomato = 0xFF6347, // rgb(255,99,71)
turquoise = 0x40E0D0, // rgb(64,224,208)
violet = 0xEE82EE, // rgb(238,130,238)
wheat = 0xF5DEB3, // rgb(245,222,179)
white = 0xFFFFFF, // rgb(255,255,255)
white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32 // rgb(154,205,50)
}; // enum class color
alice_blue = 0xF0F8FF, // rgb(240,248,255)
antique_white = 0xFAEBD7, // rgb(250,235,215)
aqua = 0x00FFFF, // rgb(0,255,255)
aquamarine = 0x7FFFD4, // rgb(127,255,212)
azure = 0xF0FFFF, // rgb(240,255,255)
beige = 0xF5F5DC, // rgb(245,245,220)
bisque = 0xFFE4C4, // rgb(255,228,196)
black = 0x000000, // rgb(0,0,0)
blanched_almond = 0xFFEBCD, // rgb(255,235,205)
blue = 0x0000FF, // rgb(0,0,255)
blue_violet = 0x8A2BE2, // rgb(138,43,226)
brown = 0xA52A2A, // rgb(165,42,42)
burly_wood = 0xDEB887, // rgb(222,184,135)
cadet_blue = 0x5F9EA0, // rgb(95,158,160)
chartreuse = 0x7FFF00, // rgb(127,255,0)
chocolate = 0xD2691E, // rgb(210,105,30)
coral = 0xFF7F50, // rgb(255,127,80)
cornflower_blue = 0x6495ED, // rgb(100,149,237)
cornsilk = 0xFFF8DC, // rgb(255,248,220)
crimson = 0xDC143C, // rgb(220,20,60)
cyan = 0x00FFFF, // rgb(0,255,255)
dark_blue = 0x00008B, // rgb(0,0,139)
dark_cyan = 0x008B8B, // rgb(0,139,139)
dark_golden_rod = 0xB8860B, // rgb(184,134,11)
dark_gray = 0xA9A9A9, // rgb(169,169,169)
dark_green = 0x006400, // rgb(0,100,0)
dark_khaki = 0xBDB76B, // rgb(189,183,107)
dark_magenta = 0x8B008B, // rgb(139,0,139)
dark_olive_green = 0x556B2F, // rgb(85,107,47)
dark_orange = 0xFF8C00, // rgb(255,140,0)
dark_orchid = 0x9932CC, // rgb(153,50,204)
dark_red = 0x8B0000, // rgb(139,0,0)
dark_salmon = 0xE9967A, // rgb(233,150,122)
dark_sea_green = 0x8FBC8F, // rgb(143,188,143)
dark_slate_blue = 0x483D8B, // rgb(72,61,139)
dark_slate_gray = 0x2F4F4F, // rgb(47,79,79)
dark_turquoise = 0x00CED1, // rgb(0,206,209)
dark_violet = 0x9400D3, // rgb(148,0,211)
deep_pink = 0xFF1493, // rgb(255,20,147)
deep_sky_blue = 0x00BFFF, // rgb(0,191,255)
dim_gray = 0x696969, // rgb(105,105,105)
dodger_blue = 0x1E90FF, // rgb(30,144,255)
fire_brick = 0xB22222, // rgb(178,34,34)
floral_white = 0xFFFAF0, // rgb(255,250,240)
forest_green = 0x228B22, // rgb(34,139,34)
fuchsia = 0xFF00FF, // rgb(255,0,255)
gainsboro = 0xDCDCDC, // rgb(220,220,220)
ghost_white = 0xF8F8FF, // rgb(248,248,255)
gold = 0xFFD700, // rgb(255,215,0)
golden_rod = 0xDAA520, // rgb(218,165,32)
gray = 0x808080, // rgb(128,128,128)
green = 0x008000, // rgb(0,128,0)
green_yellow = 0xADFF2F, // rgb(173,255,47)
honey_dew = 0xF0FFF0, // rgb(240,255,240)
hot_pink = 0xFF69B4, // rgb(255,105,180)
indian_red = 0xCD5C5C, // rgb(205,92,92)
indigo = 0x4B0082, // rgb(75,0,130)
ivory = 0xFFFFF0, // rgb(255,255,240)
khaki = 0xF0E68C, // rgb(240,230,140)
lavender = 0xE6E6FA, // rgb(230,230,250)
lavender_blush = 0xFFF0F5, // rgb(255,240,245)
lawn_green = 0x7CFC00, // rgb(124,252,0)
lemon_chiffon = 0xFFFACD, // rgb(255,250,205)
light_blue = 0xADD8E6, // rgb(173,216,230)
light_coral = 0xF08080, // rgb(240,128,128)
light_cyan = 0xE0FFFF, // rgb(224,255,255)
light_golden_rod_yellow = 0xFAFAD2, // rgb(250,250,210)
light_gray = 0xD3D3D3, // rgb(211,211,211)
light_green = 0x90EE90, // rgb(144,238,144)
light_pink = 0xFFB6C1, // rgb(255,182,193)
light_salmon = 0xFFA07A, // rgb(255,160,122)
light_sea_green = 0x20B2AA, // rgb(32,178,170)
light_sky_blue = 0x87CEFA, // rgb(135,206,250)
light_slate_gray = 0x778899, // rgb(119,136,153)
light_steel_blue = 0xB0C4DE, // rgb(176,196,222)
light_yellow = 0xFFFFE0, // rgb(255,255,224)
lime = 0x00FF00, // rgb(0,255,0)
lime_green = 0x32CD32, // rgb(50,205,50)
linen = 0xFAF0E6, // rgb(250,240,230)
magenta = 0xFF00FF, // rgb(255,0,255)
maroon = 0x800000, // rgb(128,0,0)
medium_aquamarine = 0x66CDAA, // rgb(102,205,170)
medium_blue = 0x0000CD, // rgb(0,0,205)
medium_orchid = 0xBA55D3, // rgb(186,85,211)
medium_purple = 0x9370DB, // rgb(147,112,219)
medium_sea_green = 0x3CB371, // rgb(60,179,113)
medium_slate_blue = 0x7B68EE, // rgb(123,104,238)
medium_spring_green = 0x00FA9A, // rgb(0,250,154)
medium_turquoise = 0x48D1CC, // rgb(72,209,204)
medium_violet_red = 0xC71585, // rgb(199,21,133)
midnight_blue = 0x191970, // rgb(25,25,112)
mint_cream = 0xF5FFFA, // rgb(245,255,250)
misty_rose = 0xFFE4E1, // rgb(255,228,225)
moccasin = 0xFFE4B5, // rgb(255,228,181)
navajo_white = 0xFFDEAD, // rgb(255,222,173)
navy = 0x000080, // rgb(0,0,128)
old_lace = 0xFDF5E6, // rgb(253,245,230)
olive = 0x808000, // rgb(128,128,0)
olive_drab = 0x6B8E23, // rgb(107,142,35)
orange = 0xFFA500, // rgb(255,165,0)
orange_red = 0xFF4500, // rgb(255,69,0)
orchid = 0xDA70D6, // rgb(218,112,214)
pale_golden_rod = 0xEEE8AA, // rgb(238,232,170)
pale_green = 0x98FB98, // rgb(152,251,152)
pale_turquoise = 0xAFEEEE, // rgb(175,238,238)
pale_violet_red = 0xDB7093, // rgb(219,112,147)
papaya_whip = 0xFFEFD5, // rgb(255,239,213)
peach_puff = 0xFFDAB9, // rgb(255,218,185)
peru = 0xCD853F, // rgb(205,133,63)
pink = 0xFFC0CB, // rgb(255,192,203)
plum = 0xDDA0DD, // rgb(221,160,221)
powder_blue = 0xB0E0E6, // rgb(176,224,230)
purple = 0x800080, // rgb(128,0,128)
rebecca_purple = 0x663399, // rgb(102,51,153)
red = 0xFF0000, // rgb(255,0,0)
rosy_brown = 0xBC8F8F, // rgb(188,143,143)
royal_blue = 0x4169E1, // rgb(65,105,225)
saddle_brown = 0x8B4513, // rgb(139,69,19)
salmon = 0xFA8072, // rgb(250,128,114)
sandy_brown = 0xF4A460, // rgb(244,164,96)
sea_green = 0x2E8B57, // rgb(46,139,87)
sea_shell = 0xFFF5EE, // rgb(255,245,238)
sienna = 0xA0522D, // rgb(160,82,45)
silver = 0xC0C0C0, // rgb(192,192,192)
sky_blue = 0x87CEEB, // rgb(135,206,235)
slate_blue = 0x6A5ACD, // rgb(106,90,205)
slate_gray = 0x708090, // rgb(112,128,144)
snow = 0xFFFAFA, // rgb(255,250,250)
spring_green = 0x00FF7F, // rgb(0,255,127)
steel_blue = 0x4682B4, // rgb(70,130,180)
tan = 0xD2B48C, // rgb(210,180,140)
teal = 0x008080, // rgb(0,128,128)
thistle = 0xD8BFD8, // rgb(216,191,216)
tomato = 0xFF6347, // rgb(255,99,71)
turquoise = 0x40E0D0, // rgb(64,224,208)
violet = 0xEE82EE, // rgb(238,130,238)
wheat = 0xF5DEB3, // rgb(245,222,179)
white = 0xFFFFFF, // rgb(255,255,255)
white_smoke = 0xF5F5F5, // rgb(245,245,245)
yellow = 0xFFFF00, // rgb(255,255,0)
yellow_green = 0x9ACD32 // rgb(154,205,50)
}; // enum class color
enum class terminal_color : uint8_t {
black = 30,
@ -208,49 +173,46 @@ enum class terminal_color : uint8_t {
bright_magenta,
bright_cyan,
bright_white
}; // enum class terminal_color
};
enum class emphasis : uint8_t {
bold = 1,
italic = 1 << 1,
underline = 1 << 2,
strikethrough = 1 << 3
}; // enum class emphasis
};
// rgb is a struct for red, green and blue colors.
// We use rgb as name because some editors will show it as color direct in the
// editor.
// Using the name "rgb" makes some editors show the color in a tooltip.
struct rgb {
FMT_CONSTEXPR_DECL rgb() : r(0), g(0), b(0) {}
FMT_CONSTEXPR_DECL rgb(uint8_t r_, uint8_t g_, uint8_t b_)
: r(r_), g(g_), b(b_) {}
FMT_CONSTEXPR_DECL rgb(uint32_t hex)
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b((hex) & 0xFF) {}
FMT_CONSTEXPR_DECL rgb(color hex)
: r((uint32_t(hex) >> 16) & 0xFF), g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {}
FMT_CONSTEXPR rgb() : r(0), g(0), b(0) {}
FMT_CONSTEXPR rgb(uint8_t r_, uint8_t g_, uint8_t b_) : r(r_), g(g_), b(b_) {}
FMT_CONSTEXPR rgb(uint32_t hex)
: r((hex >> 16) & 0xFF), g((hex >> 8) & 0xFF), b(hex & 0xFF) {}
FMT_CONSTEXPR rgb(color hex)
: r((uint32_t(hex) >> 16) & 0xFF),
g((uint32_t(hex) >> 8) & 0xFF),
b(uint32_t(hex) & 0xFF) {}
uint8_t r;
uint8_t g;
uint8_t b;
};
namespace internal {
namespace detail {
// color is a struct of either a rgb color or a terminal color.
struct color_type {
FMT_CONSTEXPR color_type() FMT_NOEXCEPT
: is_rgb(), value{} {}
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT
: is_rgb(true), value{} {
FMT_CONSTEXPR color_type() FMT_NOEXCEPT : is_rgb(), value{} {}
FMT_CONSTEXPR color_type(color rgb_color) FMT_NOEXCEPT : is_rgb(true),
value{} {
value.rgb_color = static_cast<uint32_t>(rgb_color);
}
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT
: is_rgb(true), value{} {
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16)
| (static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
FMT_CONSTEXPR color_type(rgb rgb_color) FMT_NOEXCEPT : is_rgb(true), value{} {
value.rgb_color = (static_cast<uint32_t>(rgb_color.r) << 16) |
(static_cast<uint32_t>(rgb_color.g) << 8) | rgb_color.b;
}
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT
: is_rgb(), value{} {
FMT_CONSTEXPR color_type(terminal_color term_color) FMT_NOEXCEPT : is_rgb(),
value{} {
value.term_color = static_cast<uint8_t>(term_color);
}
bool is_rgb;
@ -259,21 +221,23 @@ struct color_type {
uint32_t rgb_color;
} value;
};
} // namespace internal
} // namespace detail
// Experimental text formatting support.
class text_style {
public:
FMT_CONSTEXPR text_style(emphasis em = emphasis()) FMT_NOEXCEPT
: set_foreground_color(), set_background_color(), ems(em) {}
: set_foreground_color(),
set_background_color(),
ems(em) {}
FMT_CONSTEXPR text_style &operator|=(const text_style &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)
throw format_error("can't OR a terminal color");
FMT_THROW(format_error("can't OR a terminal color"));
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
}
@ -282,7 +246,7 @@ class text_style {
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
throw format_error("can't OR a terminal color");
FMT_THROW(format_error("can't OR a terminal color"));
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
}
@ -291,18 +255,18 @@ class text_style {
return *this;
}
friend FMT_CONSTEXPR
text_style operator|(text_style lhs, const text_style &rhs) {
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
const text_style& rhs) {
return lhs |= rhs;
}
FMT_CONSTEXPR text_style &operator&=(const text_style &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)
throw format_error("can't AND a terminal color");
FMT_THROW(format_error("can't AND a terminal color"));
foreground_color.value.rgb_color &= rhs.foreground_color.value.rgb_color;
}
@ -311,7 +275,7 @@ class text_style {
background_color = rhs.background_color;
} else if (rhs.set_background_color) {
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
throw format_error("can't AND a terminal color");
FMT_THROW(format_error("can't AND a terminal color"));
background_color.value.rgb_color &= rhs.background_color.value.rgb_color;
}
@ -320,8 +284,8 @@ class text_style {
return *this;
}
friend FMT_CONSTEXPR
text_style operator&(text_style lhs, const text_style &rhs) {
friend FMT_CONSTEXPR text_style operator&(text_style lhs,
const text_style& rhs) {
return lhs &= rhs;
}
@ -334,51 +298,51 @@ class text_style {
FMT_CONSTEXPR bool has_emphasis() const FMT_NOEXCEPT {
return static_cast<uint8_t>(ems) != 0;
}
FMT_CONSTEXPR internal::color_type get_foreground() const FMT_NOEXCEPT {
assert(has_foreground() && "no foreground specified for this style");
FMT_CONSTEXPR detail::color_type get_foreground() const FMT_NOEXCEPT {
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
return foreground_color;
}
FMT_CONSTEXPR internal::color_type get_background() const FMT_NOEXCEPT {
assert(has_background() && "no background specified for this style");
FMT_CONSTEXPR detail::color_type get_background() const FMT_NOEXCEPT {
FMT_ASSERT(has_background(), "no background specified for this style");
return background_color;
}
FMT_CONSTEXPR emphasis get_emphasis() const FMT_NOEXCEPT {
assert(has_emphasis() && "no emphasis specified for this style");
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
return ems;
}
private:
FMT_CONSTEXPR text_style(bool is_foreground,
internal::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems() {
if (is_foreground) {
foreground_color = text_color;
set_foreground_color = true;
} else {
background_color = text_color;
set_background_color = true;
}
}
private:
FMT_CONSTEXPR text_style(bool is_foreground,
detail::color_type text_color) FMT_NOEXCEPT
: set_foreground_color(),
set_background_color(),
ems() {
if (is_foreground) {
foreground_color = text_color;
set_foreground_color = true;
} else {
background_color = text_color;
set_background_color = true;
}
}
friend FMT_CONSTEXPR_DECL text_style fg(internal::color_type foreground)
friend FMT_CONSTEXPR_DECL text_style fg(detail::color_type foreground)
FMT_NOEXCEPT;
friend FMT_CONSTEXPR_DECL text_style bg(internal::color_type background)
friend FMT_CONSTEXPR_DECL text_style bg(detail::color_type background)
FMT_NOEXCEPT;
internal::color_type foreground_color;
internal::color_type background_color;
detail::color_type foreground_color;
detail::color_type background_color;
bool set_foreground_color;
bool set_background_color;
emphasis ems;
};
FMT_CONSTEXPR text_style fg(internal::color_type foreground) FMT_NOEXCEPT {
FMT_CONSTEXPR text_style fg(detail::color_type foreground) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/true, foreground);
}
FMT_CONSTEXPR text_style bg(internal::color_type background) FMT_NOEXCEPT {
FMT_CONSTEXPR text_style bg(detail::color_type background) FMT_NOEXCEPT {
return text_style(/*is_foreground=*/false, background);
}
@ -386,23 +350,21 @@ FMT_CONSTEXPR text_style operator|(emphasis lhs, emphasis rhs) FMT_NOEXCEPT {
return text_style(lhs) | rhs;
}
namespace internal {
namespace detail {
template <typename Char>
struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(internal::color_type text_color,
const char * esc) FMT_NOEXCEPT {
template <typename Char> struct ansi_color_escape {
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
const char* esc) FMT_NOEXCEPT {
// If we have a terminal color, we need to output another escape code
// sequence.
if (!text_color.is_rgb) {
bool is_background = esc == internal::data::BACKGROUND_COLOR;
bool is_background = esc == detail::data::background_color;
uint32_t value = text_color.value.term_color;
// Background ASCII codes are the same as the foreground ones but with
// 10 more.
if (is_background)
value += 10u;
if (is_background) value += 10u;
std::size_t index = 0;
size_t index = 0;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
@ -422,7 +384,7 @@ struct ansi_color_escape {
buffer[i] = static_cast<Char>(esc[i]);
}
rgb color(text_color.value.rgb_color);
to_esc(color.r, buffer + 7, ';');
to_esc(color.r, buffer + 7, ';');
to_esc(color.g, buffer + 11, ';');
to_esc(color.b, buffer + 15, 'm');
buffer[19] = static_cast<Char>(0);
@ -430,19 +392,15 @@ struct ansi_color_escape {
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::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;
std::size_t index = 0;
size_t index = 0;
for (int i = 0; i < 4; ++i) {
if (!em_codes[i])
continue;
if (!em_codes[i]) continue;
buffer[index++] = static_cast<Char>('\x1b');
buffer[index++] = static_cast<Char>('[');
buffer[index++] = static_cast<Char>('0' + em_codes[i]);
@ -450,12 +408,17 @@ struct ansi_color_escape {
}
buffer[index++] = static_cast<Char>(0);
}
FMT_CONSTEXPR operator const Char *() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR operator const Char*() const FMT_NOEXCEPT { return buffer; }
private:
FMT_CONSTEXPR const Char* begin() const FMT_NOEXCEPT { return buffer; }
FMT_CONSTEXPR const Char* end() const FMT_NOEXCEPT {
return buffer + std::char_traits<Char>::length(buffer);
}
private:
Char buffer[7u + 3u * 4u + 1u];
static FMT_CONSTEXPR void to_esc(uint8_t c, Char *out,
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
char delimiter) FMT_NOEXCEPT {
out[0] = static_cast<Char>('0' + c / 100);
out[1] = static_cast<Char>('0' + c / 10 % 10);
@ -465,95 +428,98 @@ private:
};
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char>
make_foreground_color(internal::color_type foreground) FMT_NOEXCEPT {
return ansi_color_escape<Char>(foreground, internal::data::FOREGROUND_COLOR);
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);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char>
make_background_color(internal::color_type background) FMT_NOEXCEPT {
return ansi_color_escape<Char>(background, internal::data::BACKGROUND_COLOR);
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);
}
template <typename Char>
FMT_CONSTEXPR ansi_color_escape<Char>
make_emphasis(emphasis em) FMT_NOEXCEPT {
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) FMT_NOEXCEPT {
return ansi_color_escape<Char>(em);
}
template <typename Char>
inline void fputs(const Char *chars, FILE *stream) FMT_NOEXCEPT {
inline void fputs(const Char* chars, FILE* stream) FMT_NOEXCEPT {
std::fputs(chars, stream);
}
template <>
inline void fputs<wchar_t>(const wchar_t *chars, FILE *stream) FMT_NOEXCEPT {
inline void fputs<wchar_t>(const wchar_t* chars, FILE* stream) FMT_NOEXCEPT {
std::fputws(chars, stream);
}
template <typename Char> inline void reset_color(FILE* stream) FMT_NOEXCEPT {
fputs(detail::data::reset_color, stream);
}
template <> inline void reset_color<wchar_t>(FILE* stream) FMT_NOEXCEPT {
fputs(detail::data::wreset_color, stream);
}
template <typename Char>
inline void reset_color(FILE *stream) FMT_NOEXCEPT {
fputs(internal::data::RESET_COLOR, stream);
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);
}
template <>
inline void reset_color<wchar_t>(FILE *stream) FMT_NOEXCEPT {
fputs(internal::data::WRESET_COLOR, stream);
}
// The following specialiazation disables using std::FILE as a character type,
// which is needed because or else
// fmt::print(stderr, fmt::emphasis::bold, "");
// would take stderr (a std::FILE *) as the format string.
template <>
struct is_string<std::FILE *> : std::false_type {};
template <>
struct is_string<const std::FILE *> : std::false_type {};
} // namespace internal
template <
typename S, typename Char = typename internal::char_t<S>::type>
void vprint(std::FILE *f, const text_style &ts, const S &format,
basic_format_args<typename buffer_context<Char>::type> args) {
template <typename Char>
void vformat_to(buffer<Char>& buf, const text_style& ts,
basic_string_view<Char> format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
bool has_style = false;
if (ts.has_emphasis()) {
has_style = true;
internal::fputs<Char>(
internal::make_emphasis<Char>(ts.get_emphasis()), f);
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
buf.append(emphasis.begin(), emphasis.end());
}
if (ts.has_foreground()) {
has_style = true;
internal::fputs<Char>(
internal::make_foreground_color<Char>(ts.get_foreground()), f);
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
buf.append(foreground.begin(), foreground.end());
}
if (ts.has_background()) {
has_style = true;
internal::fputs<Char>(
internal::make_background_color<Char>(ts.get_background()), f);
}
vprint(f, format, args);
if (has_style) {
internal::reset_color<Char>(f);
auto background = detail::make_background_color<Char>(ts.get_background());
buf.append(background.begin(), background.end());
}
detail::vformat_to(buf, format_str, args);
if (has_style) detail::reset_color<Char>(buf);
}
} // namespace detail
template <typename S, typename Char = char_t<S>>
void vprint(std::FILE* f, const text_style& ts, const S& format,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format), args);
buf.push_back(Char(0));
detail::fputs(buf.data(), f);
}
/**
\rst
Formats a string and prints it to the specified file stream 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 String, typename... Args>
typename std::enable_if<internal::is_string<String>::value>::type print(
std::FILE *f, const text_style &ts, const String &format_str,
const Args &... args) {
internal::check_format_string<Args...>(format_str);
typedef typename internal::char_t<String>::type char_t;
typedef typename buffer_context<char_t>::type context_t;
format_arg_store<context_t, Args...> as{args...};
vprint(f, ts, format_str, basic_format_args<context_t>(as));
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(std::FILE* f, const text_style& ts, const S& format_str,
const Args&... args) {
vprint(f, ts, format_str,
fmt::make_args_checked<Args...>(format_str, args...));
}
/**
@ -563,14 +529,74 @@ typename std::enable_if<internal::is_string<String>::value>::type print(
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
"Elapsed time: {0:.2f} seconds", 1.23);
*/
template <typename String, typename... Args>
typename std::enable_if<internal::is_string<String>::value>::type print(
const text_style &ts, const String &format_str,
const Args &... args) {
template <typename S, typename... Args,
FMT_ENABLE_IF(detail::is_string<S>::value)>
void print(const text_style& ts, const S& format_str, const Args&... args) {
return print(stdout, ts, format_str, args...);
}
#endif
template <typename S, typename Char = char_t<S>>
inline std::basic_string<Char> vformat(
const text_style& ts, const S& format_str,
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
basic_memory_buffer<Char> buf;
detail::vformat_to(buf, ts, to_string_view(format_str), args);
return fmt::to_string(buf);
}
/**
\rst
Formats arguments and returns the result as a string using ANSI
escape sequences to specify text formatting.
**Example**::
#include <fmt/color.h>
std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red),
"The answer is {}", 42);
\endrst
*/
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...));
}
/**
Formats a string with the given text_style and writes the output to ``out``.
*/
template <typename OutputIt, typename Char,
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value)>
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));
detail::vformat_to(buf, ts, format_str, args);
return detail::get_iterator(buf);
}
/**
\rst
Formats arguments with the given text_style, writes the result to the output
iterator ``out`` and returns the iterator past the end of the output range.
**Example**::
std::vector<char> out;
fmt::format_to(std::back_inserter(out),
fmt::emphasis::bold | fg(fmt::color::red), "{}", 42);
\endrst
*/
template <typename OutputIt, typename S, typename... Args,
bool enable = detail::is_output_iterator<OutputIt, char_t<S>>::value&&
detail::is_string<S>::value>
inline auto format_to(OutputIt out, const text_style& ts, const S& format_str,
Args&&... args) ->
typename std::enable_if<enable, OutputIt>::type {
return vformat_to(out, ts, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

701
src/fmtlib/fmt/compile.h Normal file
View File

@ -0,0 +1,701 @@
// Formatting library for C++ - experimental format string compilation
//
// Copyright (c) 2012 - present, Victor Zverovich and fmt contributors
// All rights reserved.
//
// For the license information refer to format.h.
#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 };
struct replacement {
arg_ref<Char> arg_id;
dynamic_format_specs<Char> specs;
};
kind part_kind;
union value {
int arg_index;
basic_string_view<Char> str;
replacement repl;
FMT_CONSTEXPR value(int index = 0) : arg_index(index) {}
FMT_CONSTEXPR value(basic_string_view<Char> s) : str(s) {}
FMT_CONSTEXPR value(replacement r) : repl(r) {}
} val;
// Position past the end of the argument id.
const Char* arg_id_end = nullptr;
FMT_CONSTEXPR format_part(kind k = kind::arg_index, value v = {})
: part_kind(k), val(v) {}
static FMT_CONSTEXPR format_part make_arg_index(int index) {
return format_part(kind::arg_index, index);
}
static FMT_CONSTEXPR format_part make_arg_name(basic_string_view<Char> name) {
return format_part(kind::arg_name, name);
}
static FMT_CONSTEXPR format_part make_text(basic_string_view<Char> text) {
return format_part(kind::text, text);
}
static FMT_CONSTEXPR format_part make_replacement(replacement repl) {
return format_part(kind::replacement, repl);
}
};
template <typename Char> struct part_counter {
unsigned num_parts = 0;
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end) ++num_parts;
}
FMT_CONSTEXPR int on_arg_id() { return ++num_parts, 0; }
FMT_CONSTEXPR int on_arg_id(int) { return ++num_parts, 0; }
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char>) {
return ++num_parts, 0;
}
FMT_CONSTEXPR void on_replacement_field(int, const Char*) {}
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) {
// Find the matching brace.
unsigned brace_counter = 0;
for (; begin != end; ++begin) {
if (*begin == '{') {
++brace_counter;
} else if (*begin == '}') {
if (brace_counter == 0u) break;
--brace_counter;
}
}
return begin;
}
FMT_CONSTEXPR void on_error(const char*) {}
};
// Counts the number of parts in a format string.
template <typename Char>
FMT_CONSTEXPR unsigned count_parts(basic_string_view<Char> format_str) {
part_counter<Char> counter;
parse_format_string<true>(format_str, counter);
return counter.num_parts;
}
template <typename Char, typename PartHandler>
class format_string_compiler : public error_handler {
private:
using part = format_part<Char>;
PartHandler handler_;
part part_;
basic_string_view<Char> format_str_;
basic_format_parse_context<Char> parse_context_;
public:
FMT_CONSTEXPR format_string_compiler(basic_string_view<Char> format_str,
PartHandler handler)
: handler_(handler),
format_str_(format_str),
parse_context_(format_str) {}
FMT_CONSTEXPR void on_text(const Char* begin, const Char* end) {
if (begin != end)
handler_(part::make_text({begin, to_unsigned(end - begin)}));
}
FMT_CONSTEXPR int on_arg_id() {
part_ = part::make_arg_index(parse_context_.next_arg_id());
return 0;
}
FMT_CONSTEXPR int on_arg_id(int id) {
parse_context_.check_arg_id(id);
part_ = part::make_arg_index(id);
return 0;
}
FMT_CONSTEXPR int on_arg_id(basic_string_view<Char> id) {
part_ = part::make_arg_name(id);
return 0;
}
FMT_CONSTEXPR void on_replacement_field(int, const Char* ptr) {
part_.arg_id_end = ptr;
handler_(part_);
}
FMT_CONSTEXPR const Char* on_format_specs(int, const Char* begin,
const Char* end) {
auto repl = typename part::replacement();
dynamic_specs_handler<basic_format_parse_context<Char>> handler(
repl.specs, parse_context_);
auto it = parse_format_specs(begin, end, handler);
if (*it != '}') on_error("missing '}' in format string");
repl.arg_id = part_.part_kind == part::kind::arg_index
? arg_ref<Char>(part_.val.arg_index)
: arg_ref<Char>(part_.val.str);
auto part = part::make_replacement(repl);
part.arg_id_end = begin;
handler_(part);
return it;
}
};
// Compiles a format string and invokes handler(part) for each parsed part.
template <bool IS_CONSTEXPR, typename Char, typename PartHandler>
FMT_CONSTEXPR void compile_format_string(basic_string_view<Char> format_str,
PartHandler handler) {
parse_format_string<IS_CONSTEXPR>(
format_str,
format_string_compiler<Char, PartHandler>(format_str, handler));
}
template <typename OutputIt, typename Context, typename Id>
void format_arg(
basic_format_parse_context<typename Context::char_type>& parse_ctx,
Context& ctx, Id arg_id) {
ctx.advance_to(visit_format_arg(
arg_formatter<OutputIt, typename Context::char_type>(ctx, &parse_ctx),
ctx.arg(arg_id)));
}
// vformat_to is defined in a subnamespace to prevent ADL.
namespace cf {
template <typename Context, typename OutputIt, typename CompiledFormat>
auto vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) -> typename Context::iterator {
using char_type = typename Context::char_type;
basic_format_parse_context<char_type> parse_ctx(
to_string_view(cf.format_str_));
Context ctx(out, args);
const auto& parts = cf.parts();
for (auto part_it = std::begin(parts); part_it != std::end(parts);
++part_it) {
const auto& part = *part_it;
const auto& value = part.val;
using format_part_t = format_part<char_type>;
switch (part.part_kind) {
case format_part_t::kind::text: {
const auto text = value.str;
auto output = ctx.out();
auto&& it = reserve(output, text.size());
it = std::copy_n(text.begin(), text.size(), it);
ctx.advance_to(output);
break;
}
case format_part_t::kind::arg_index:
advance_to(parse_ctx, part.arg_id_end);
detail::format_arg<OutputIt>(parse_ctx, ctx, value.arg_index);
break;
case format_part_t::kind::arg_name:
advance_to(parse_ctx, part.arg_id_end);
detail::format_arg<OutputIt>(parse_ctx, ctx, value.str);
break;
case format_part_t::kind::replacement: {
const auto& arg_id_value = value.repl.arg_id.val;
const auto arg = value.repl.arg_id.kind == arg_id_kind::index
? ctx.arg(arg_id_value.index)
: ctx.arg(arg_id_value.name);
auto specs = value.repl.specs;
handle_dynamic_spec<width_checker>(specs.width, specs.width_ref, ctx);
handle_dynamic_spec<precision_checker>(specs.precision,
specs.precision_ref, ctx);
error_handler h;
numeric_specs_checker<error_handler> checker(h, arg.type());
if (specs.align == align::numeric) checker.require_numeric_argument();
if (specs.sign != sign::none) checker.check_sign();
if (specs.alt) checker.require_numeric_argument();
if (specs.precision >= 0) checker.check_precision();
advance_to(parse_ctx, part.arg_id_end);
ctx.advance_to(
visit_format_arg(arg_formatter<OutputIt, typename Context::char_type>(
ctx, nullptr, &specs),
arg));
break;
}
}
}
return ctx.out();
}
} // namespace cf
struct basic_compiled_format {};
template <typename S, typename = void>
struct compiled_format_base : basic_compiled_format {
using char_type = char_t<S>;
using parts_container = std::vector<detail::format_part<char_type>>;
parts_container compiled_parts;
explicit compiled_format_base(basic_string_view<char_type> format_str) {
compile_format_string<false>(format_str,
[this](const format_part<char_type>& part) {
compiled_parts.push_back(part);
});
}
const parts_container& parts() const { return compiled_parts; }
};
template <typename Char, unsigned N> struct format_part_array {
format_part<Char> data[N] = {};
FMT_CONSTEXPR format_part_array() = default;
};
template <typename Char, unsigned N>
FMT_CONSTEXPR format_part_array<Char, N> compile_to_parts(
basic_string_view<Char> format_str) {
format_part_array<Char, N> parts;
unsigned counter = 0;
// This is not a lambda for compatibility with older compilers.
struct {
format_part<Char>* parts;
unsigned* counter;
FMT_CONSTEXPR void operator()(const format_part<Char>& part) {
parts[(*counter)++] = part;
}
} collector{parts.data, &counter};
compile_format_string<true>(format_str, collector);
if (counter < N) {
parts.data[counter] =
format_part<Char>::make_text(basic_string_view<Char>());
}
return parts;
}
template <typename T> constexpr const T& constexpr_max(const T& a, const T& b) {
return (a < b) ? b : a;
}
template <typename S>
struct compiled_format_base<S, enable_if_t<is_compile_string<S>::value>>
: basic_compiled_format {
using char_type = char_t<S>;
FMT_CONSTEXPR explicit compiled_format_base(basic_string_view<char_type>) {}
// Workaround for old compilers. Format string compilation will not be
// performed there anyway.
#if FMT_USE_CONSTEXPR
static FMT_CONSTEXPR_DECL const unsigned num_format_parts =
constexpr_max(count_parts(to_string_view(S())), 1u);
#else
static const unsigned num_format_parts = 1;
#endif
using parts_container = format_part<char_type>[num_format_parts];
const parts_container& parts() const {
static FMT_CONSTEXPR_DECL const auto compiled_parts =
compile_to_parts<char_type, num_format_parts>(
detail::to_string_view(S()));
return compiled_parts.data;
}
};
template <typename S, typename... Args>
class compiled_format : private compiled_format_base<S> {
public:
using typename compiled_format_base<S>::char_type;
private:
basic_string_view<char_type> format_str_;
template <typename Context, typename OutputIt, typename CompiledFormat>
friend auto cf::vformat_to(OutputIt out, CompiledFormat& cf,
basic_format_args<Context> args) ->
typename Context::iterator;
public:
compiled_format() = delete;
explicit constexpr compiled_format(basic_string_view<char_type> format_str)
: compiled_format_base<S>(format_str), format_str_(format_str) {}
};
#ifdef __cpp_if_constexpr
template <typename... Args> struct type_list {};
// Returns a reference to the argument at index N from [first, rest...].
template <int N, typename T, typename... Args>
constexpr const auto& get([[maybe_unused]] const T& first,
[[maybe_unused]] const Args&... rest) {
static_assert(N < 1 + sizeof...(Args), "index is out of bounds");
if constexpr (N == 0)
return first;
else
return get<N - 1>(rest...);
}
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>()...))>;
};
template <int N, typename T>
using get_type = typename get_type_impl<N, T>::type;
template <typename T> struct is_compiled_format : std::false_type {};
template <typename Char> struct text {
basic_string_view<Char> data;
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, data);
}
};
template <typename Char>
struct is_compiled_format<text<Char>> : std::true_type {};
template <typename Char>
constexpr text<Char> make_text(basic_string_view<Char> s, size_t pos,
size_t size) {
return {{&s[pos], size}};
}
template <typename Char> struct code_unit {
Char value;
using char_type = Char;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&...) const {
return write<Char>(out, value);
}
};
template <typename Char>
struct is_compiled_format<code_unit<Char>> : std::true_type {};
// A replacement field that refers to argument N.
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);
}
};
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 N and has format specifiers.
template <typename Char, typename T, int N> struct spec_field {
using char_type = Char;
mutable 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...);
const auto& vargs =
make_format_args<basic_format_context<OutputIt, Char>>(args...);
basic_format_context<OutputIt, Char> ctx(out, vargs);
return fmt.format(arg, ctx);
}
};
template <typename Char, typename T, int N>
struct is_compiled_format<spec_field<Char, T, N>> : std::true_type {};
template <typename L, typename R> struct concat {
L lhs;
R rhs;
using char_type = typename L::char_type;
template <typename OutputIt, typename... Args>
OutputIt format(OutputIt out, const Args&... args) const {
out = lhs.format(out, args...);
return rhs.format(out, args...);
}
};
template <typename L, typename R>
struct is_compiled_format<concat<L, R>> : std::true_type {};
template <typename L, typename R>
constexpr concat<L, R> make_concat(L lhs, R rhs) {
return {lhs, rhs};
}
struct unknown_format {};
template <typename Char>
constexpr size_t parse_text(basic_string_view<Char> str, size_t pos) {
for (size_t size = str.size(); pos != size; ++pos) {
if (str[pos] == '{' || str[pos] == '}') break;
}
return pos;
}
template <typename Args, size_t POS, int ID, typename S>
constexpr auto compile_format_string(S format_str);
template <typename Args, size_t POS, int ID, typename T, typename S>
constexpr auto parse_tail(T head, S format_str) {
if constexpr (POS !=
basic_string_view<typename S::char_type>(format_str).size()) {
constexpr auto tail = compile_format_string<Args, POS, ID>(format_str);
if constexpr (std::is_same<remove_cvref_t<decltype(tail)>,
unknown_format>())
return tail;
else
return make_concat(head, tail);
} else {
return head;
}
}
template <typename T, typename Char> struct parse_specs_result {
formatter<T, Char> fmt;
size_t end;
int next_arg_id;
};
template <typename T, typename Char>
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
size_t pos, int arg_id) {
str.remove_prefix(pos);
auto ctx = basic_format_parse_context<Char>(str, {}, arg_id + 1);
auto f = formatter<T, Char>();
auto end = f.parse(ctx);
return {f, pos + (end - str.data()) + 1, ctx.next_arg_id()};
}
// Compiles a non-empty format string and returns the compiled representation
// or unknown_format() on unrecognized input.
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;
if constexpr (str[POS] == '{') {
if (POS + 1 == str.size())
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 {
return unknown_format();
}
} else if constexpr (str[POS] == '}') {
if (POS + 1 == str.size())
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);
if constexpr (end - POS > 1) {
return parse_tail<Args, end, ID>(make_text(str, POS, end - POS),
format_str);
} else {
return parse_tail<Args, end, ID>(code_unit<char_type>{str[POS]},
format_str);
}
}
}
template <typename... Args, typename S,
FMT_ENABLE_IF(is_compile_string<S>::value ||
detail::is_compiled_string<S>::value)>
constexpr auto compile(S format_str) {
constexpr basic_string_view<typename S::char_type> str = 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;
}
}
}
#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));
}
} // 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...);
}
#if FMT_USE_CONSTEXPR
# ifdef __cpp_if_constexpr
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);
}
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) {
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...));
}
#endif
constexpr auto compiled = detail::compile<Args...>(S());
return format(compiled, std::forward<Args>(args)...);
}
template <typename OutputIt, typename CompiledFormat, typename... Args,
FMT_ENABLE_IF(std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value)>
OutputIt format_to(OutputIt out, const CompiledFormat& cf,
const Args&... args) {
using char_type = typename CompiledFormat::char_type;
using context = format_context_t<OutputIt, char_type>;
return detail::cf::vformat_to<context>(out, cf,
make_format_args<context>(args...));
}
template <typename OutputIt, typename S, typename... Args,
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
OutputIt format_to(OutputIt out, const S&, const Args&... args) {
constexpr auto compiled = detail::compile<Args...>(S());
return format_to(out, compiled, args...);
}
template <typename OutputIt, typename CompiledFormat, typename... Args>
auto format_to_n(OutputIt out, size_t n, const CompiledFormat& cf,
const Args&... args) ->
typename std::enable_if<
detail::is_output_iterator<OutputIt,
typename CompiledFormat::char_type>::value &&
std::is_base_of<detail::basic_compiled_format,
CompiledFormat>::value,
format_to_n_result<OutputIt>>::type {
auto it =
format_to(detail::truncating_iterator<OutputIt>(out, n), cf, args...);
return {it.base(), it.count()};
}
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&,
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()};
}
template <typename CompiledFormat, typename... Args>
size_t formatted_size(const CompiledFormat& cf, const Args&... args) {
return format_to(detail::counting_iterator(), cf, args...).count();
}
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

View File

@ -8,68 +8,55 @@
#ifndef FMT_LOCALE_H_
#define FMT_LOCALE_H_
#include "format.h"
#include <locale>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
template <typename Char>
typename buffer_context<Char>::type::iterator vformat_to(
const std::locale &loc, basic_buffer<Char> &buf,
basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) {
typedef back_insert_range<basic_buffer<Char> > range;
return vformat_to<arg_formatter<range>>(
buf, to_string_view(format_str), args, internal::locale_ref(loc));
}
namespace detail {
template <typename Char>
std::basic_string<Char> vformat(
const std::locale &loc, basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) {
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;
internal::vformat_to(loc, buffer, format_str, args);
detail::vformat_to(buffer, format_str, args, detail::locale_ref(loc));
return fmt::to_string(buffer);
}
}
} // namespace detail
template <typename S, typename Char = FMT_CHAR(S)>
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<typename buffer_context<Char>::type> args) {
return internal::vformat(loc, to_string_view(format_str), args);
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>
inline std::basic_string<FMT_CHAR(S)> format(
const std::locale &loc, const S &format_str, const Args &... args) {
return internal::vformat(
loc, to_string_view(format_str),
*internal::checked_args<S, Args...>(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 String, typename OutputIt, typename... Args>
inline typename std::enable_if<internal::is_output_iterator<OutputIt>::value,
OutputIt>::type
vformat_to(OutputIt out, const std::locale &loc, const String &format_str,
typename format_args_t<OutputIt, FMT_CHAR(String)>::type args) {
typedef output_range<OutputIt, FMT_CHAR(String)> range;
return vformat_to<arg_formatter<range>>(
range(out), to_string_view(format_str), args, internal::locale_ref(loc));
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>
inline typename std::enable_if<
internal::is_string<S>::value &&
internal::is_output_iterator<OutputIt>::value, OutputIt>::type
format_to(OutputIt out, const std::locale &loc, const S &format_str,
const Args &... args) {
internal::check_format_string<Args...>(format_str);
typedef typename format_context_t<OutputIt, FMT_CHAR(S)>::type context;
format_arg_store<context, Args...> as{args...};
return vformat_to(out, loc, to_string_view(format_str),
basic_format_args<context>(as));
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

480
src/fmtlib/fmt/os.h Normal file
View File

@ -0,0 +1,480 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#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 <cstddef>
#include <cstdio>
#include <cstdlib> // for strtod_l
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
// 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
#endif
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) ::call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
(result) = (expression); \
} while ((result) == (error_result) && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following type aliases for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/
template <typename Char> class basic_cstring_view {
private:
const Char* data_;
public:
/** Constructs a string reference object from a C string. */
basic_cstring_view(const Char* s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char>& s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char* c_str() const { return data_; }
};
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) {}
int get() const FMT_NOEXCEPT { return value_; }
};
#ifdef _WIN32
namespace detail {
// 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 {
private:
memory_buffer buffer_;
public:
utf16_to_utf8() {}
FMT_API explicit utf16_to_utf8(wstring_view 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]; }
std::string str() const { return std::string(&buffer_[0], size()); }
// 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 void format_windows_error(buffer<char>& out, int error_code,
string_view message) FMT_NOEXCEPT;
} // namespace detail
/** A Windows error. */
class windows_error : public system_error {
private:
FMT_API void init(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...));
}
};
// 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;
#endif // _WIN32
// A buffered file.
class buffered_file {
private:
FILE* file_;
friend class file;
explicit buffered_file(FILE* f) : file_(f) {}
public:
buffered_file(const buffered_file&) = delete;
void operator=(const buffered_file&) = delete;
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(nullptr) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
public:
buffered_file(buffered_file&& other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = nullptr;
}
buffered_file& operator=(buffered_file&& other) {
close();
file_ = other.file_;
other.file_ = nullptr;
return *this;
}
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE* get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
FMT_API int(fileno)() const;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args&... args) {
vprint(format_str, make_format_args(args...));
}
};
#if FMT_USE_FCNTL
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class file {
private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
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.
};
// Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
public:
file(const file&) = delete;
void operator=(const file&) = delete;
file(file&& other) FMT_NOEXCEPT : fd_(other.fd_) { other.fd_ = -1; }
file& operator=(file&& other) FMT_NOEXCEPT {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
// Closes the file.
FMT_API void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API size_t read(void* buffer, size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API size_t write(const void* buffer, size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// 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;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
FMT_API static void pipe(file& read_end, file& write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
FMT_API buffered_file fdopen(const char* mode);
};
// Returns the memory page size.
long getpagesize();
namespace detail {
struct buffer_size {
size_t value = 0;
buffer_size operator=(size_t val) const {
auto bs = buffer_size();
bs.value = val;
return bs;
}
};
struct ostream_params {
int oflag = file::WRONLY | file::CREATE;
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;
}
template <typename... T>
ostream_params(T... params, detail::buffer_size bs)
: ostream_params(params...) {
this->buffer_size = bs.value;
}
};
} // namespace detail
static constexpr detail::buffer_size buffer_size;
// A fast output stream which is not thread-safe.
class 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;
ostream(cstring_view path, const detail::ostream_params& params)
: file_(path, params.oflag) {
set(new char[params.buffer_size], params.buffer_size);
}
public:
ostream(ostream&& other)
: detail::buffer<char>(other.data(), other.size(), other.capacity()),
file_(std::move(other.file_)) {
other.set(nullptr, 0);
}
~ostream() {
flush();
delete[] data();
}
template <typename... T>
friend ostream output_file(cstring_view path, T... params);
void close() {
flush();
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...);
}
};
/**
Opens a file for writing. Supported parameters passed in `params`:
* ``<integer>``: Output flags (``file::WRONLY | file::CREATE`` by default)
* ``buffer_size=<integer>``: Output buffer size
*/
template <typename... T>
inline ostream output_file(cstring_view path, T... params) {
return {path, detail::ostream_params(params...)};
}
#endif // FMT_USE_FCNTL
#ifdef FMT_LOCALE
// A "C" numeric locale.
class locale {
private:
# ifdef _WIN32
using locale_t = _locale_t;
static void freelocale(locale_t loc) { _free_locale(loc); }
static double strtod_l(const char* nptr, char** endptr, _locale_t loc) {
return _strtod_l(nptr, endptr, loc);
}
# endif
locale_t locale_;
public:
using type = locale_t;
locale(const locale&) = delete;
void operator=(const locale&) = delete;
locale() {
# ifndef _WIN32
locale_ = FMT_SYSTEM(newlocale(LC_NUMERIC_MASK, "C", nullptr));
# else
locale_ = _create_locale(LC_NUMERIC, "C");
# endif
if (!locale_) FMT_THROW(system_error(errno, "cannot create locale"));
}
~locale() { freelocale(locale_); }
type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char*& str) const {
char* end = nullptr;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
using Locale FMT_DEPRECATED_ALIAS = locale;
#endif // FMT_LOCALE
FMT_END_NAMESPACE
#endif // FMT_OS_H_

View File

@ -8,22 +8,26 @@
#ifndef FMT_OSTREAM_H_
#define FMT_OSTREAM_H_
#include "format.h"
#include <ostream>
#include "format.h"
FMT_BEGIN_NAMESPACE
namespace internal {
template <class Char>
class formatbuf : public std::basic_streambuf<Char> {
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:
typedef typename std::basic_streambuf<Char>::int_type int_type;
typedef typename std::basic_streambuf<Char>::traits_type traits_type;
using int_type = typename std::basic_streambuf<Char>::int_type;
using traits_type = typename std::basic_streambuf<Char>::traits_type;
basic_buffer<Char> &buffer_;
buffer<Char>& buffer_;
public:
formatbuf(basic_buffer<Char> &buffer) : buffer_(buffer) {}
formatbuf(buffer<Char>& buf) : buffer_(buf) {}
protected:
// The put-area is actually always empty. This makes the implementation
@ -39,33 +43,46 @@ class formatbuf : public std::basic_streambuf<Char> {
return ch;
}
std::streamsize xsputn(const Char *s, std::streamsize count) FMT_OVERRIDE {
std::streamsize xsputn(const Char* s, std::streamsize count) FMT_OVERRIDE {
buffer_.append(s, s + count);
return count;
}
};
template <typename Char>
struct test_stream : std::basic_ostream<Char> {
private:
struct null;
// Hide all operator<< from std::basic_ostream<Char>.
void operator<<(null);
struct converter {
template <typename T, FMT_ENABLE_IF(is_integral<T>::value)> converter(T);
};
// Checks if T has a user-defined operator<< (e.g. not a member of std::ostream).
template <typename T, typename Char>
class is_streamable {
template <typename Char> struct test_stream : std::basic_ostream<Char> {
private:
void_t<> operator<<(converter);
};
// Hide insertion operators for built-in types.
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, Char);
template <typename Char, typename Traits>
void_t<> operator<<(std::basic_ostream<Char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, signed char);
template <typename Traits>
void_t<> operator<<(std::basic_ostream<char, Traits>&, unsigned char);
// Checks if T has a user-defined operator<< (e.g. not a member of
// std::ostream).
template <typename T, typename Char> class is_streamable {
private:
template <typename U>
static decltype(
internal::declval<test_stream<Char>&>()
<< internal::declval<U>(), std::true_type()) test(int);
static bool_constant<!std::is_same<decltype(std::declval<test_stream<Char>&>()
<< std::declval<U>()),
void_t<>>::value>
test(int);
template <typename>
static std::false_type test(...);
template <typename> static std::false_type test(...);
typedef decltype(test<T>(0)) result;
using result = decltype(test<T>(0));
public:
static const bool value = result::value;
@ -73,65 +90,73 @@ class is_streamable {
// Write the content of buf to os.
template <typename Char>
void write(std::basic_ostream<Char> &os, basic_buffer<Char> &buf) {
const Char *data = buf.data();
typedef std::make_unsigned<std::streamsize>::type UnsignedStreamSize;
UnsignedStreamSize size = buf.size();
UnsignedStreamSize max_size =
internal::to_unsigned((std::numeric_limits<std::streamsize>::max)());
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
const Char* buf_data = buf.data();
using unsigned_streamsize = std::make_unsigned<std::streamsize>::type;
unsigned_streamsize size = buf.size();
unsigned_streamsize max_size = to_unsigned(max_value<std::streamsize>());
do {
UnsignedStreamSize n = size <= max_size ? size : max_size;
os.write(data, static_cast<std::streamsize>(n));
data += n;
unsigned_streamsize n = size <= max_size ? size : max_size;
os.write(buf_data, static_cast<std::streamsize>(n));
buf_data += n;
size -= n;
} while (size != 0);
}
template <typename Char, typename T>
void format_value(basic_buffer<Char> &buffer, const T &value) {
internal::formatbuf<Char> format_buf(buffer);
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);
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
if (loc) output.imbue(loc.get<std::locale>());
#endif
output << value;
buffer.resize(buffer.size());
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
buf.try_resize(buf.size());
}
} // namespace internal
// Disable conversion to int if T has an overloaded operator<< which is a free
// function (not a member of std::ostream).
template <typename T, typename Char>
struct convert_to_int<T, Char, void> {
static const bool value =
convert_to_int<T, Char, int>::value &&
!internal::is_streamable<T, Char>::value;
};
// Formats an object of type T that has an overloaded ostream operator<<.
template <typename T, typename Char>
struct formatter<T, Char,
typename std::enable_if<
internal::is_streamable<T, Char>::value &&
!internal::format_type<
typename buffer_context<Char>::type, T>::value>::type>
: formatter<basic_string_view<Char>, 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();
}
template <typename Context>
auto format(const T &value, Context &ctx) -> decltype(ctx.out()) {
template <typename OutputIt>
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
internal::format_value(buffer, value);
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);
}
template <typename OutputIt>
auto format(const T& value, basic_printf_context<OutputIt, Char>& ctx)
-> OutputIt {
basic_memory_buffer<Char> buffer;
format_value(buffer, value, ctx.locale());
return std::copy(buffer.begin(), buffer.end(), ctx.out());
}
};
} // namespace detail
template <typename Char>
inline void vprint(std::basic_ostream<Char> &os,
basic_string_view<Char> format_str,
basic_format_args<typename buffer_context<Char>::type> args) {
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;
internal::vformat_to(buffer, format_str, args);
internal::write(os, buffer);
detail::vformat_to(buffer, format_str, args);
detail::write_buffer(os, buffer);
}
/**
\rst
Prints formatted data to the stream *os*.
@ -141,12 +166,11 @@ inline void vprint(std::basic_ostream<Char> &os,
fmt::print(cerr, "Don't {}!", "panic");
\endrst
*/
template <typename S, typename... Args>
inline typename std::enable_if<internal::is_string<S>::value>::type
print(std::basic_ostream<FMT_CHAR(S)> &os, const S &format_str,
const Args & ... args) {
internal::checked_args<S, Args...> ca(format_str, args...);
vprint(os, to_string_view(format_str), *ca);
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) {
vprint(os, to_string_view(format_str),
fmt::make_args_checked<Args...>(format_str, args...));
}
FMT_END_NAMESPACE

View File

@ -1,324 +1,2 @@
// A C++ interface to POSIX functions.
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#ifndef FMT_POSIX_H_
#define FMT_POSIX_H_
#if defined(__MINGW32__) || defined(__CYGWIN__)
// Workaround MinGW bug https://sourceforge.net/p/mingw/bugs/2024/.
# undef __STRICT_ANSI__
#endif
#include <errno.h>
#include <fcntl.h> // for O_RDONLY
#include <locale.h> // for locale_t
#include <stdio.h>
#include <stdlib.h> // for strtod_l
#include <cstddef>
#if defined __APPLE__ || defined(__FreeBSD__)
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
#endif
#include "format.h"
#ifndef FMT_POSIX
# if defined(_WIN32) && !defined(__MINGW32__)
// Fix warnings about deprecated symbols.
# define FMT_POSIX(call) _##call
# else
# define FMT_POSIX(call) call
# endif
#endif
// Calls to system functions are wrapped in FMT_SYSTEM for testability.
#ifdef FMT_SYSTEM
# define FMT_POSIX_CALL(call) FMT_SYSTEM(call)
#else
# define FMT_SYSTEM(call) call
# ifdef _WIN32
// Fix warnings about deprecated symbols.
# define FMT_POSIX_CALL(call) ::_##call
# else
# define FMT_POSIX_CALL(call) ::call
# endif
#endif
// Retries the expression while it evaluates to error_result and errno
// equals to EINTR.
#ifndef _WIN32
# define FMT_RETRY_VAL(result, expression, error_result) \
do { \
result = (expression); \
} while (result == error_result && errno == EINTR)
#else
# define FMT_RETRY_VAL(result, expression, error_result) result = (expression)
#endif
#define FMT_RETRY(result, expression) FMT_RETRY_VAL(result, expression, -1)
FMT_BEGIN_NAMESPACE
/**
\rst
A reference to a null-terminated string. It can be constructed from a C
string or ``std::string``.
You can use one of the following typedefs for common character types:
+---------------+-----------------------------+
| Type | Definition |
+===============+=============================+
| cstring_view | basic_cstring_view<char> |
+---------------+-----------------------------+
| wcstring_view | basic_cstring_view<wchar_t> |
+---------------+-----------------------------+
This class is most useful as a parameter type to allow passing
different types of strings to a function, for example::
template <typename... Args>
std::string format(cstring_view format_str, const Args & ... args);
format("{}", 42);
format(std::string("{}"), 42);
\endrst
*/
template <typename Char>
class basic_cstring_view {
private:
const Char *data_;
public:
/** Constructs a string reference object from a C string. */
basic_cstring_view(const Char *s) : data_(s) {}
/**
\rst
Constructs a string reference from an ``std::string`` object.
\endrst
*/
basic_cstring_view(const std::basic_string<Char> &s) : data_(s.c_str()) {}
/** Returns the pointer to a C string. */
const Char *c_str() const { return data_; }
};
typedef basic_cstring_view<char> cstring_view;
typedef basic_cstring_view<wchar_t> wcstring_view;
// An error code.
class error_code {
private:
int value_;
public:
explicit error_code(int value = 0) FMT_NOEXCEPT : value_(value) {}
int get() const FMT_NOEXCEPT { return value_; }
};
// A buffered file.
class buffered_file {
private:
FILE *file_;
friend class file;
explicit buffered_file(FILE *f) : file_(f) {}
public:
// Constructs a buffered_file object which doesn't represent any file.
buffered_file() FMT_NOEXCEPT : file_(FMT_NULL) {}
// Destroys the object closing the file it represents if any.
FMT_API ~buffered_file() FMT_NOEXCEPT;
private:
buffered_file(const buffered_file &) = delete;
void operator=(const buffered_file &) = delete;
public:
buffered_file(buffered_file &&other) FMT_NOEXCEPT : file_(other.file_) {
other.file_ = FMT_NULL;
}
buffered_file& operator=(buffered_file &&other) {
close();
file_ = other.file_;
other.file_ = FMT_NULL;
return *this;
}
// Opens a file.
FMT_API buffered_file(cstring_view filename, cstring_view mode);
// Closes the file.
FMT_API void close();
// Returns the pointer to a FILE object representing this file.
FILE *get() const FMT_NOEXCEPT { return file_; }
// We place parentheses around fileno to workaround a bug in some versions
// of MinGW that define fileno as a macro.
FMT_API int (fileno)() const;
void vprint(string_view format_str, format_args args) {
fmt::vprint(file_, format_str, args);
}
template <typename... Args>
inline void print(string_view format_str, const Args & ... args) {
vprint(format_str, make_format_args(args...));
}
};
// A file. Closed file is represented by a file object with descriptor -1.
// Methods that are not declared with FMT_NOEXCEPT may throw
// fmt::system_error in case of failure. Note that some errors such as
// closing the file multiple times will cause a crash on Windows rather
// than an exception. You can get standard behavior by overriding the
// invalid parameter handler with _set_invalid_parameter_handler.
class file {
private:
int fd_; // File descriptor.
// Constructs a file object with a given descriptor.
explicit file(int fd) : fd_(fd) {}
public:
// Possible values for the oflag argument to the constructor.
enum {
RDONLY = FMT_POSIX(O_RDONLY), // Open for reading only.
WRONLY = FMT_POSIX(O_WRONLY), // Open for writing only.
RDWR = FMT_POSIX(O_RDWR) // Open for reading and writing.
};
// Constructs a file object which doesn't represent any file.
file() FMT_NOEXCEPT : fd_(-1) {}
// Opens a file and constructs a file object representing this file.
FMT_API file(cstring_view path, int oflag);
private:
file(const file &) = delete;
void operator=(const file &) = delete;
public:
file(file &&other) FMT_NOEXCEPT : fd_(other.fd_) {
other.fd_ = -1;
}
file& operator=(file &&other) {
close();
fd_ = other.fd_;
other.fd_ = -1;
return *this;
}
// Destroys the object closing the file it represents if any.
FMT_API ~file() FMT_NOEXCEPT;
// Returns the file descriptor.
int descriptor() const FMT_NOEXCEPT { return fd_; }
// Closes the file.
FMT_API void close();
// Returns the file size. The size has signed type for consistency with
// stat::st_size.
FMT_API long long size() const;
// Attempts to read count bytes from the file into the specified buffer.
FMT_API std::size_t read(void *buffer, std::size_t count);
// Attempts to write count bytes from the specified buffer to the file.
FMT_API std::size_t write(const void *buffer, std::size_t count);
// Duplicates a file descriptor with the dup function and returns
// the duplicate as a file object.
FMT_API static file dup(int fd);
// Makes fd be the copy of this file descriptor, closing fd first if
// necessary.
FMT_API void dup2(int fd);
// 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;
// Creates a pipe setting up read_end and write_end file objects for reading
// and writing respectively.
FMT_API static void pipe(file &read_end, file &write_end);
// Creates a buffered_file object associated with this file and detaches
// this file object from the file.
FMT_API buffered_file fdopen(const char *mode);
};
// Returns the memory page size.
long getpagesize();
#if (defined(LC_NUMERIC_MASK) || defined(_MSC_VER)) && \
!defined(__ANDROID__) && !defined(__CYGWIN__) && !defined(__OpenBSD__) && \
!defined(__NEWLIB_H__)
# define FMT_LOCALE
#endif
#ifdef FMT_LOCALE
// A "C" numeric locale.
class Locale {
private:
# ifdef _MSC_VER
typedef _locale_t locale_t;
enum { LC_NUMERIC_MASK = LC_NUMERIC };
static locale_t newlocale(int category_mask, const char *locale, locale_t) {
return _create_locale(category_mask, locale);
}
static void freelocale(locale_t locale) {
_free_locale(locale);
}
static double strtod_l(const char *nptr, char **endptr, _locale_t locale) {
return _strtod_l(nptr, endptr, locale);
}
# endif
locale_t locale_;
Locale(const Locale &) = delete;
void operator=(const Locale &) = delete;
public:
typedef locale_t Type;
Locale() : locale_(newlocale(LC_NUMERIC_MASK, "C", FMT_NULL)) {
if (!locale_)
FMT_THROW(system_error(errno, "cannot create locale"));
}
~Locale() { freelocale(locale_); }
Type get() const { return locale_; }
// Converts string to floating-point number and advances str past the end
// of the parsed input.
double strtod(const char *&str) const {
char *end = FMT_NULL;
double result = strtod_l(str, &end, locale_);
str = end;
return result;
}
};
#endif // FMT_LOCALE
FMT_END_NAMESPACE
#endif // FMT_POSIX_H_
#include "os.h"
#warning "fmt/posix.h is deprecated; use fmt/os.h instead"

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
// Formatting library for C++ - the core API
// Formatting library for C++ - experimental range support
//
// Copyright (c) 2012 - present, Victor Zverovich
// All rights reserved.
@ -12,28 +12,30 @@
#ifndef FMT_RANGES_H_
#define FMT_RANGES_H_
#include "format.h"
#include <initializer_list>
#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
# define FMT_RANGE_OUTPUT_LENGTH_LIMIT 256
#endif
FMT_BEGIN_NAMESPACE
template <typename Char>
struct formatting_base {
template <typename Char> struct formatting_base {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
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 std::size_t range_length_limit =
FMT_RANGE_OUTPUT_LENGTH_LIMIT; // output only up to N items from the range.
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;
@ -52,106 +54,92 @@ struct formatting_tuple : formatting_base<Char> {
static FMT_CONSTEXPR_DECL const bool add_prepostfix_space = false;
};
namespace internal {
namespace detail {
template <typename RangeT, typename OutputIterator>
void copy(const RangeT &range, OutputIterator out) {
OutputIterator copy(const RangeT& range, OutputIterator out) {
for (auto it = range.begin(), end = range.end(); it != end; ++it)
*out++ = *it;
return out;
}
template <typename OutputIterator>
void copy(const char *str, OutputIterator out) {
const char *p_curr = str;
while (*p_curr) {
*out++ = *p_curr++;
}
OutputIterator copy(const char* str, OutputIterator out) {
while (*str) *out++ = *str++;
return out;
}
template <typename OutputIterator>
void copy(char ch, OutputIterator out) {
OutputIterator copy(char ch, OutputIterator out) {
*out++ = ch;
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 T> class is_like_std_string {
template <typename U>
static auto check(U *p) ->
decltype(p->find('a'), p->length(), p->data(), int());
template <typename>
static void check(...);
static auto check(U* p)
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
template <typename> static void check(...);
public:
static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(FMT_NULL))>::value;
is_string<T>::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 {};
template <typename... Ts>
struct conditional_helper {};
template <typename... Ts> struct conditional_helper {};
template <typename T, typename _ = void>
struct is_range_ : std::false_type {};
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
#if !FMT_MSC_VER || FMT_MSC_VER > 1800
template <typename T>
struct is_range_<T, typename std::conditional<
false,
conditional_helper<decltype(internal::declval<T>().begin()),
decltype(internal::declval<T>().end())>,
void>::type> : std::true_type {};
struct is_range_<
T, conditional_t<false,
conditional_helper<decltype(std::declval<T>().begin()),
decltype(std::declval<T>().end())>,
void>> : std::true_type {};
#endif
/// tuple_size and tuple_element check.
template <typename T>
class is_tuple_like_ {
template <typename T> class is_tuple_like_ {
template <typename U>
static auto check(U *p) ->
decltype(std::tuple_size<U>::value,
internal::declval<typename std::tuple_element<0, U>::type>(), int());
template <typename>
static void check(...);
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
template <typename> static void check(...);
public:
static FMT_CONSTEXPR_DECL const bool value =
!std::is_void<decltype(check<T>(FMT_NULL))>::value;
!std::is_void<decltype(check<T>(nullptr))>::value;
};
// Check for integer_sequence
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VER >= 1900
template <typename T, T... N>
using integer_sequence = std::integer_sequence<T, N...>;
template <std::size_t... N>
using index_sequence = std::index_sequence<N...>;
template <std::size_t N>
using make_index_sequence = std::make_index_sequence<N>;
template <size_t... N> using index_sequence = std::index_sequence<N...>;
template <size_t N> using make_index_sequence = std::make_index_sequence<N>;
#else
template <typename T, T... N>
struct integer_sequence {
typedef T value_type;
template <typename T, T... N> struct integer_sequence {
using value_type = T;
static FMT_CONSTEXPR std::size_t size() {
return sizeof...(N);
}
static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
};
template <std::size_t... N>
using index_sequence = integer_sequence<std::size_t, N...>;
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
template <typename T, std::size_t N, T... Ns>
template <typename T, size_t N, T... Ns>
struct make_integer_sequence : make_integer_sequence<T, N - 1, N - 1, Ns...> {};
template <typename T, T... Ns>
struct make_integer_sequence<T, 0, Ns...> : integer_sequence<T, Ns...> {};
template <std::size_t N>
using make_index_sequence = make_integer_sequence<std::size_t, N>;
template <size_t N>
using make_index_sequence = make_integer_sequence<size_t, N>;
#endif
template <class Tuple, class F, size_t... Is>
void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT {
void for_each(index_sequence<Is...>, Tuple&& tup, F&& f) FMT_NOEXCEPT {
using std::get;
// using free function get<I>(T) now.
const int _[] = {0, ((void)f(get<Is>(tup)), 0)...};
@ -159,26 +147,28 @@ void for_each(index_sequence<Is...>, Tuple &&tup, F &&f) FMT_NOEXCEPT {
}
template <class T>
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value>
get_indexes(T const &) { return {}; }
FMT_CONSTEXPR make_index_sequence<std::tuple_size<T>::value> get_indexes(
T const&) {
return {};
}
template <class Tuple, class F>
void for_each(Tuple &&tup, F &&f) {
template <class Tuple, class F> void for_each(Tuple&& tup, F&& f) {
const auto indexes = get_indexes(tup);
for_each(indexes, std::forward<Tuple>(tup), std::forward<F>(f));
}
template<typename Arg>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&,
typename std::enable_if<
!is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
template <typename Range>
using value_type = remove_cvref_t<decltype(*std::declval<Range>().begin())>;
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 Arg>
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const Arg&,
typename std::enable_if<
is_like_std_string<typename std::decay<Arg>::type>::value>::type* = nullptr) {
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 ? " \"{}\"" : "\"{}\"";
}
@ -186,123 +176,221 @@ FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char*) {
return add_space ? " \"{}\"" : "\"{}\"";
}
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t*) {
return add_space ? L" \"{}\"" : L"\"{}\"";
return add_space ? L" \"{}\"" : L"\"{}\"";
}
FMT_CONSTEXPR const char* format_str_quoted(bool add_space, const char) {
return add_space ? " '{}'" : "'{}'";
return add_space ? " '{}'" : "'{}'";
}
FMT_CONSTEXPR const wchar_t* format_str_quoted(bool add_space, const wchar_t) {
return add_space ? L" '{}'" : L"'{}'";
return add_space ? L" '{}'" : L"'{}'";
}
} // namespace detail
} // namespace internal
template <typename T>
struct is_tuple_like {
template <typename T> struct is_tuple_like {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_tuple_like_<T>::value && !internal::is_range_<T>::value;
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
};
template <typename TupleT, typename Char>
struct formatter<TupleT, Char,
typename std::enable_if<fmt::is_tuple_like<TupleT>::value>::type> {
private:
struct formatter<TupleT, Char, enable_if_t<fmt::is_tuple_like<TupleT>::value>> {
private:
// C++11 generic lambda for format()
template <typename FormatContext>
struct format_each {
template <typename T>
void operator()(const T& v) {
template <typename FormatContext> struct format_each {
template <typename T> void operator()(const T& v) {
if (i > 0) {
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
internal::copy(formatting.delimiter, out);
out = detail::copy(formatting.delimiter, out);
}
format_to(out,
internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v),
v);
out = format_to(out,
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), v),
v);
++i;
}
formatting_tuple<Char>& formatting;
std::size_t& i;
typename std::add_lvalue_reference<decltype(std::declval<FormatContext>().out())>::type out;
size_t& i;
typename std::add_lvalue_reference<decltype(
std::declval<FormatContext>().out())>::type out;
};
public:
public:
formatting_tuple<Char> formatting;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx);
}
template <typename FormatContext = format_context>
auto format(const TupleT &values, FormatContext &ctx) -> decltype(ctx.out()) {
auto format(const TupleT& values, FormatContext& ctx) -> decltype(ctx.out()) {
auto out = ctx.out();
std::size_t i = 0;
internal::copy(formatting.prefix, out);
size_t i = 0;
detail::copy(formatting.prefix, out);
internal::for_each(values, format_each<FormatContext>{formatting, i, out});
detail::for_each(values, format_each<FormatContext>{formatting, i, out});
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
internal::copy(formatting.postfix, out);
detail::copy(formatting.postfix, out);
return ctx.out();
}
};
template <typename T>
struct is_range {
template <typename T, typename Char> struct is_range {
static FMT_CONSTEXPR_DECL const bool value =
internal::is_range_<T>::value && !internal::is_like_std_string<T>::value;
detail::is_range_<T>::value && !detail::is_like_std_string<T>::value &&
!std::is_convertible<T, std::basic_string<Char>>::value &&
!std::is_constructible<detail::std_string_view<Char>, T>::value;
};
template <typename RangeT, typename Char>
struct formatter<RangeT, Char,
typename std::enable_if<fmt::is_range<RangeT>::value>::type> {
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)
#endif
>> {
formatting_range<Char> formatting;
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext &ctx) -> decltype(ctx.begin()) {
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return formatting.parse(ctx);
}
template <typename FormatContext>
typename FormatContext::iterator format(
const RangeT &values, FormatContext &ctx) {
auto out = ctx.out();
internal::copy(formatting.prefix, out);
std::size_t i = 0;
for (auto it = values.begin(), end = values.end(); it != end; ++it) {
typename FormatContext::iterator format(const T& values, FormatContext& ctx) {
auto out = detail::copy(formatting.prefix, ctx.out());
size_t i = 0;
auto it = values.begin();
auto end = values.end();
for (; it != end; ++it) {
if (i > 0) {
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
internal::copy(formatting.delimiter, out);
if (formatting.add_prepostfix_space) *out++ = ' ';
out = detail::copy(formatting.delimiter, out);
}
format_to(out,
internal::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it),
*it);
out = format_to(out,
detail::format_str_quoted(
(formatting.add_delimiter_spaces && i > 0), *it),
*it);
if (++i > formatting.range_length_limit) {
format_to(out, " ... <other elements>");
out = format_to(out, " ... <other elements>");
break;
}
}
if (formatting.add_prepostfix_space) {
*out++ = ' ';
}
internal::copy(formatting.postfix, out);
return ctx.out();
if (formatting.add_prepostfix_space) *out++ = ' ';
return detail::copy(formatting.postfix, out);
}
};
template <typename Char, typename... T> struct tuple_arg_join : detail::view {
const std::tuple<T...>& tuple;
basic_string_view<Char> sep;
tuple_arg_join(const std::tuple<T...>& t, basic_string_view<Char> s)
: tuple{t}, sep{s} {}
};
template <typename Char, typename... T>
struct formatter<tuple_arg_join<Char, T...>, Char> {
template <typename ParseContext>
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
return ctx.begin();
}
template <typename FormatContext>
typename FormatContext::iterator format(
const tuple_arg_join<Char, T...>& value, FormatContext& ctx) {
return format(value, ctx, detail::make_index_sequence<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)...);
}
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.
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) {
out = std::copy(value.sep.begin(), value.sep.end(), out);
ctx.advance_to(out);
return format_args(value, ctx, args...);
}
return out;
}
};
/**
\rst
Returns an object that formats `tuple` with elements separated by `sep`.
**Example**::
std::tuple<int, char> t = {1, 'a'};
fmt::print("{}", fmt::join(t, ", "));
// Output: "1, a"
\endrst
*/
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<char, T...> join(const std::tuple<T...>& tuple,
string_view sep) {
return {tuple, sep};
}
template <typename... T>
FMT_CONSTEXPR tuple_arg_join<wchar_t, T...> join(const std::tuple<T...>& tuple,
wstring_view sep) {
return {tuple, sep};
}
/**
\rst
Returns an object that formats `initializer_list` with elements separated by
`sep`.
**Example**::
fmt::print("{}", fmt::join({1, 2, 3}, ", "));
// Output: "1, 2, 3"
\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) {
return join(std::begin(list), std::end(list), sep);
}
FMT_END_NAMESPACE
#endif // FMT_RANGES_H_
#endif // FMT_RANGES_H_

View File

@ -8,52 +8,92 @@
#include "fmt/format-inl.h"
FMT_BEGIN_NAMESPACE
template struct internal::basic_data<void>;
template FMT_API internal::locale_ref::locale_ref(const std::locale &loc);
template FMT_API std::locale internal::locale_ref::get<std::locale>() const;
namespace detail {
template <typename T>
int format_float(char* buf, std::size_t size, const char* format, int precision,
T value) {
#ifdef FMT_FUZZ
if (precision > 100000)
throw std::runtime_error(
"fuzz mode - avoid large allocation inside snprintf");
#endif
// Suppress the warning about nonliteral format string.
int (*snprintf_ptr)(char*, size_t, const char*, ...) = FMT_SNPRINTF;
return precision < 0 ? snprintf_ptr(buf, size, format, value)
: snprintf_ptr(buf, size, format, precision, value);
}
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;
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
template FMT_API detail::locale_ref::locale_ref(const std::locale& loc);
template FMT_API std::locale detail::locale_ref::get<std::locale>() const;
#endif
// Explicit instantiations for char.
template FMT_API char internal::thousands_sep_impl(locale_ref);
template FMT_API std::string detail::grouping_impl<char>(locale_ref);
template FMT_API char detail::thousands_sep_impl(locale_ref);
template FMT_API char detail::decimal_point_impl(locale_ref);
template FMT_API void internal::basic_buffer<char>::append(const char *, const char *);
template FMT_API void detail::buffer<char>::append(const char*, const char*);
template FMT_API void internal::arg_map<format_context>::init(
const basic_format_args<format_context> &args);
template FMT_API void detail::vformat_to(
detail::buffer<char>&, string_view,
basic_format_args<FMT_BUFFER_CONTEXT(char)>, detail::locale_ref);
template FMT_API int internal::char_traits<char>::format_float(
char *, std::size_t, const char *, int, double);
template FMT_API int internal::char_traits<char>::format_float(
char *, std::size_t, const char *, int, long double);
template FMT_API std::string internal::vformat<char>(
string_view, basic_format_args<format_context>);
template FMT_API format_context::iterator internal::vformat_to(
internal::buffer &, string_view, basic_format_args<format_context>);
template FMT_API void internal::sprintf_format(
double, internal::buffer &, core_format_specs);
template FMT_API void internal::sprintf_format(
long double, internal::buffer &, core_format_specs);
template FMT_API int detail::snprintf_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::snprintf_float(long double, int,
detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(double, int, detail::float_specs,
detail::buffer<char>&);
template FMT_API int detail::format_float(long double, int, detail::float_specs,
detail::buffer<char>&);
// Explicit instantiations for wchar_t.
template FMT_API wchar_t internal::thousands_sep_impl(locale_ref);
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 wchar_t detail::decimal_point_impl(locale_ref);
template FMT_API void internal::basic_buffer<wchar_t>::append(
const wchar_t *, const wchar_t *);
template FMT_API void internal::arg_map<wformat_context>::init(
const basic_format_args<wformat_context> &);
template FMT_API int internal::char_traits<wchar_t>::format_float(
wchar_t *, std::size_t, const wchar_t *, int, double);
template FMT_API int internal::char_traits<wchar_t>::format_float(
wchar_t *, std::size_t, const wchar_t *, int, long double);
template FMT_API std::wstring internal::vformat<wchar_t>(
wstring_view, basic_format_args<wformat_context>);
template FMT_API void detail::buffer<wchar_t>::append(const wchar_t*,
const wchar_t*);
FMT_END_NAMESPACE

322
src/fmtlib/os.cc Normal file
View File

@ -0,0 +1,322 @@
// Formatting library for C++ - optional OS-specific functionality
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
// Disable bogus MSVC warnings.
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "fmt/os.h"
#include <climits>
#if FMT_USE_FCNTL
# include <sys/stat.h>
# include <sys/types.h>
# ifndef _WIN32
# include <unistd.h>
# else
# ifndef WIN32_LEAN_AND_MEAN
# 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
# endif
# endif // _WIN32
#endif // FMT_USE_FCNTL
#ifdef _WIN32
# include <windows.h>
#endif
#ifdef fileno
# undef fileno
#endif
namespace {
#ifdef _WIN32
// Return type of read and write functions.
using RWResult = int;
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
inline unsigned convert_rwcount(std::size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#elif FMT_USE_FCNTL
// Return type of read and write functions.
using RWResult = ssize_t;
inline std::size_t convert_rwcount(std::size_t count) { return count; }
#endif
} // namespace
FMT_BEGIN_NAMESPACE
#ifdef _WIN32
detail::utf16_to_utf8::utf16_to_utf8(wstring_view 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) {
if (s.size() > INT_MAX) return ERROR_INVALID_PARAMETER;
int s_size = static_cast<int>(s.size());
if (s_size == 0) {
// WideCharToMultiByte does not support zero length, handle separately.
buffer_.resize(1);
buffer_[0] = 0;
return 0;
}
int length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, nullptr, 0,
nullptr, nullptr);
if (length == 0) return GetLastError();
buffer_.resize(length + 1);
length = WideCharToMultiByte(CP_UTF8, 0, s.data(), s_size, &buffer_[0],
length, nullptr, nullptr);
if (length == 0) return GetLastError();
buffer_[length] = 0;
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));
}
void detail::format_windows_error(detail::buffer<char>& out, int error_code,
string_view 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;
}
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 {
report_error(detail::format_windows_error, error_code, message);
}
#endif // _WIN32
buffered_file::~buffered_file() FMT_NOEXCEPT {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
report_system_error(errno, "cannot close file");
}
buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
FMT_RETRY_VAL(file_, FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())),
nullptr);
if (!file_)
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
}
void buffered_file::close() {
if (!file_) return;
int result = FMT_SYSTEM(fclose(file_));
file_ = nullptr;
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
}
// A macro used to prevent expansion of fileno on broken versions of MinGW.
#define FMT_ARGS
int buffered_file::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1) FMT_THROW(system_error(errno, "cannot get file descriptor"));
return fd;
}
#if FMT_USE_FCNTL
file::file(cstring_view path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
# if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
# else
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
# endif
if (fd_ == -1)
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
}
file::~file() FMT_NOEXCEPT {
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
report_system_error(errno, "cannot close file");
}
void file::close() {
if (fd_ == -1) return;
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1;
if (result != 0) FMT_THROW(system_error(errno, "cannot close file"));
}
long long file::size() const {
# ifdef _WIN32
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
// is less than 0x0500 as is the case with some default MinGW builds.
// Both functions support large file sizes.
DWORD size_upper = 0;
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
if (size_lower == INVALID_FILE_SIZE) {
DWORD error = GetLastError();
if (error != NO_ERROR)
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
}
unsigned long long long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
# else
using Stat = struct stat;
Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(system_error(errno, "cannot get file attributes"));
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
"return type of file::size is not large enough");
return file_stat.st_size;
# endif
}
std::size_t file::read(void* buffer, std::size_t count) {
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;
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);
}
file file::dup(int fd) {
// Don't retry as dup doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd));
if (new_fd == -1)
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
return file(new_fd);
}
void file::dup2(int fd) {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) {
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {} to {}",
fd_, fd));
}
}
void file::dup2(int fd, error_code& ec) FMT_NOEXCEPT {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) ec = error_code(errno);
}
void file::pipe(file& read_end, file& write_end) {
// Close the descriptors first to make sure that assignments don't throw
// and there are no leaks.
read_end.close();
write_end.close();
int fds[2] = {};
# ifdef _WIN32
// Make the default pipe capacity same as on Linux 2.6.11+.
enum { DEFAULT_CAPACITY = 65536 };
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
# else
// Don't retry as the pipe function doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
# endif
if (result != 0) FMT_THROW(system_error(errno, "cannot create pipe"));
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = file(fds[0]);
write_end = file(fds[1]);
}
buffered_file file::fdopen(const char* mode) {
// Don't retry as fdopen doesn't return EINTR.
# if defined(__MINGW32__) && defined(_POSIX_)
FILE* f = ::fdopen(fd_, mode);
# else
FILE* f = FMT_POSIX_CALL(fdopen(fd_, mode));
# endif
if (!f)
FMT_THROW(
system_error(errno, "cannot associate stream with file descriptor"));
buffered_file bf(f);
fd_ = -1;
return bf;
}
long getpagesize() {
# ifdef _WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwPageSize;
# else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0) FMT_THROW(system_error(errno, "cannot get memory page size"));
return size;
# endif
}
FMT_API void ostream::grow(size_t) {
if (this->size() == this->capacity()) flush();
}
#endif // FMT_USE_FCNTL
FMT_END_NAMESPACE

View File

@ -1,244 +0,0 @@
// A C++ interface to POSIX functions.
//
// Copyright (c) 2012 - 2016, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
// Disable bogus MSVC warnings.
#if !defined(_CRT_SECURE_NO_WARNINGS) && defined(_MSC_VER)
# define _CRT_SECURE_NO_WARNINGS
#endif
#include "fmt/posix.h"
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#ifndef _WIN32
# include <unistd.h>
#else
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN
# endif
# include <windows.h>
# include <io.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
# endif
#endif // _WIN32
#ifdef fileno
# undef fileno
#endif
namespace {
#ifdef _WIN32
// Return type of read and write functions.
typedef int RWResult;
// On Windows the count argument to read and write is unsigned, so convert
// it from size_t preventing integer overflow.
inline unsigned convert_rwcount(std::size_t count) {
return count <= UINT_MAX ? static_cast<unsigned>(count) : UINT_MAX;
}
#else
// Return type of read and write functions.
typedef ssize_t RWResult;
inline std::size_t convert_rwcount(std::size_t count) { return count; }
#endif
}
FMT_BEGIN_NAMESPACE
buffered_file::~buffered_file() FMT_NOEXCEPT {
if (file_ && FMT_SYSTEM(fclose(file_)) != 0)
report_system_error(errno, "cannot close file");
}
buffered_file::buffered_file(cstring_view filename, cstring_view mode) {
FMT_RETRY_VAL(file_,
FMT_SYSTEM(fopen(filename.c_str(), mode.c_str())), FMT_NULL);
if (!file_)
FMT_THROW(system_error(errno, "cannot open file {}", filename.c_str()));
}
void buffered_file::close() {
if (!file_)
return;
int result = FMT_SYSTEM(fclose(file_));
file_ = FMT_NULL;
if (result != 0)
FMT_THROW(system_error(errno, "cannot close file"));
}
// A macro used to prevent expansion of fileno on broken versions of MinGW.
#define FMT_ARGS
int buffered_file::fileno() const {
int fd = FMT_POSIX_CALL(fileno FMT_ARGS(file_));
if (fd == -1)
FMT_THROW(system_error(errno, "cannot get file descriptor"));
return fd;
}
file::file(cstring_view path, int oflag) {
int mode = S_IRUSR | S_IWUSR;
#if defined(_WIN32) && !defined(__MINGW32__)
fd_ = -1;
FMT_POSIX_CALL(sopen_s(&fd_, path.c_str(), oflag, _SH_DENYNO, mode));
#else
FMT_RETRY(fd_, FMT_POSIX_CALL(open(path.c_str(), oflag, mode)));
#endif
if (fd_ == -1)
FMT_THROW(system_error(errno, "cannot open file {}", path.c_str()));
}
file::~file() FMT_NOEXCEPT {
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
if (fd_ != -1 && FMT_POSIX_CALL(close(fd_)) != 0)
report_system_error(errno, "cannot close file");
}
void file::close() {
if (fd_ == -1)
return;
// Don't retry close in case of EINTR!
// See http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html
int result = FMT_POSIX_CALL(close(fd_));
fd_ = -1;
if (result != 0)
FMT_THROW(system_error(errno, "cannot close file"));
}
long long file::size() const {
#ifdef _WIN32
// Use GetFileSize instead of GetFileSizeEx for the case when _WIN32_WINNT
// is less than 0x0500 as is the case with some default MinGW builds.
// Both functions support large file sizes.
DWORD size_upper = 0;
HANDLE handle = reinterpret_cast<HANDLE>(_get_osfhandle(fd_));
DWORD size_lower = FMT_SYSTEM(GetFileSize(handle, &size_upper));
if (size_lower == INVALID_FILE_SIZE) {
DWORD error = GetLastError();
if (error != NO_ERROR)
FMT_THROW(windows_error(GetLastError(), "cannot get file size"));
}
unsigned long long long_size = size_upper;
return (long_size << sizeof(DWORD) * CHAR_BIT) | size_lower;
#else
typedef struct stat Stat;
Stat file_stat = Stat();
if (FMT_POSIX_CALL(fstat(fd_, &file_stat)) == -1)
FMT_THROW(system_error(errno, "cannot get file attributes"));
static_assert(sizeof(long long) >= sizeof(file_stat.st_size),
"return type of file::size is not large enough");
return file_stat.st_size;
#endif
}
std::size_t file::read(void *buffer, std::size_t count) {
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 internal::to_unsigned(result);
}
std::size_t file::write(const void *buffer, std::size_t count) {
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 internal::to_unsigned(result);
}
file file::dup(int fd) {
// Don't retry as dup doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dup.html
int new_fd = FMT_POSIX_CALL(dup(fd));
if (new_fd == -1)
FMT_THROW(system_error(errno, "cannot duplicate file descriptor {}", fd));
return file(new_fd);
}
void file::dup2(int fd) {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1) {
FMT_THROW(system_error(errno,
"cannot duplicate file descriptor {} to {}", fd_, fd));
}
}
void file::dup2(int fd, error_code &ec) FMT_NOEXCEPT {
int result = 0;
FMT_RETRY(result, FMT_POSIX_CALL(dup2(fd_, fd)));
if (result == -1)
ec = error_code(errno);
}
void file::pipe(file &read_end, file &write_end) {
// Close the descriptors first to make sure that assignments don't throw
// and there are no leaks.
read_end.close();
write_end.close();
int fds[2] = {};
#ifdef _WIN32
// Make the default pipe capacity same as on Linux 2.6.11+.
enum { DEFAULT_CAPACITY = 65536 };
int result = FMT_POSIX_CALL(pipe(fds, DEFAULT_CAPACITY, _O_BINARY));
#else
// Don't retry as the pipe function doesn't return EINTR.
// http://pubs.opengroup.org/onlinepubs/009696799/functions/pipe.html
int result = FMT_POSIX_CALL(pipe(fds));
#endif
if (result != 0)
FMT_THROW(system_error(errno, "cannot create pipe"));
// The following assignments don't throw because read_fd and write_fd
// are closed.
read_end = file(fds[0]);
write_end = file(fds[1]);
}
buffered_file file::fdopen(const char *mode) {
// Don't retry as fdopen doesn't return EINTR.
FILE *f = FMT_POSIX_CALL(fdopen(fd_, mode));
if (!f)
FMT_THROW(system_error(errno,
"cannot associate stream with file descriptor"));
buffered_file bf(f);
fd_ = -1;
return bf;
}
long getpagesize() {
#ifdef _WIN32
SYSTEM_INFO si;
GetSystemInfo(&si);
return si.dwPageSize;
#else
long size = FMT_POSIX_CALL(sysconf(_SC_PAGESIZE));
if (size < 0)
FMT_THROW(system_error(errno, "cannot get memory page size"));
return size;
#endif
}
FMT_END_NAMESPACE

View File

@ -9,7 +9,7 @@
"pattern": "(?s)^(?<timestamp>\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2},\\d{3}(?:\\+\\d{4})?) (?<level>\\w+)\\s+\\((?<thread>.+?)\\) \\[(?<logger>.+?)\\]\\s+?(?<body>(?:.|[\\r\\n])*?)\\s+?\\((?<src_file>\\w+?):(?<src_line>\\d+)\\)(\\n(?<traceback>Traceback.*?)(?=\\n(?P=timestamp)|$))?"
},
"v4": {
"pattern": "^(?<tid>.+)::(?<level>.+)::(?<timestamp>.+)::(?<module>.+)::(?<src_line>.+)::(?<logger>.+)::\\((?<func>[^\\)]+)\\)(?<body>(?:.|\\n)*)"
"pattern": "^(?<tid>[^:]+)::(?<level>[^:]+)::(?<timestamp>\\d{4}-\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}(?:,\\d{3})?)::(?<module>[^:]+)::(?<src_line>[^:]+)::(?<logger>[^:]+)::\\((?<func>[^\\)]+)\\)(?<body>(?:.|\\n)*)"
}
},
"level-field": "level",

View File

@ -238,7 +238,7 @@ bool handle_paging_key(int ch)
case KEY_F(2):
if (xterm_mouse::is_available()) {
auto mouse_i = injector::get<xterm_mouse&>();
auto& mouse_i = injector::get<xterm_mouse&>();
mouse_i.set_enabled(!mouse_i.is_enabled());
lnav_data.ld_rl_view->set_value(
ok_prefix("info: mouse mode -- ") +
@ -897,7 +897,7 @@ bool handle_paging_key(int ch)
case 'r':
case 'R':
if (lss) {
auto &last_time =
auto& last_time =
injector::get<const relative_time&, last_relative_time_tag>();
if (last_time.empty()) {

View File

@ -17,6 +17,12 @@
"title": "/tuning/archive-manager",
"type": "object",
"properties": {
"min-free-space": {
"title": "/tuning/archive-manager/min-free-space",
"description": "The minimum free space to maintain when unpacking",
"type": "integer",
"minimum": 0
},
"cache-ttl": {
"title": "/tuning/archive-manager/cache-ttl",
"description": "The time-to-live for unpacked archives",
@ -148,6 +154,16 @@
"title": "/ui/theme-defs/<theme_name>/styles/popup",
"$ref": "#/definitions/style"
},
"focused": {
"description": "Styling for a focused row in a list view",
"title": "/ui/theme-defs/<theme_name>/styles/focused",
"$ref": "#/definitions/style"
},
"disabled-focused": {
"description": "Styling for a disabled focused row in a list view",
"title": "/ui/theme-defs/<theme_name>/styles/disabled-focused",
"$ref": "#/definitions/style"
},
"scrollbar": {
"description": "Styling for scrollbars",
"title": "/ui/theme-defs/<theme_name>/styles/scrollbar",

View File

@ -366,7 +366,7 @@ public:
vis_line_t last_line(this->get_inner_height() - 1);
retval = last_line - vis_line_t(this->rows_available(last_line, RD_UP) - 1);
if ((retval + 1) < this->get_inner_height()) {
if ((retval + this->lv_tail_space) < this->get_inner_height()) {
retval += this->lv_tail_space;
}
}

View File

@ -81,6 +81,7 @@
#include "logfile.hh"
#include "base/func_util.hh"
#include "base/injector.bind.hh"
#include "base/isc.hh"
#include "base/string_util.hh"
#include "base/lnav_log.hh"
#include "bound_tags.hh"
@ -122,11 +123,14 @@
#include "xpath_vtab.hh"
#include "textfile_highlighters.hh"
#include "base/future_util.hh"
#include "service_tags.hh"
#ifdef HAVE_LIBCURL
#include <curl/curl.h>
#endif
#include "curl_looper.hh"
#if HAVE_ARCHIVE_H
#include <archive.h>
#endif
@ -244,6 +248,10 @@ static auto bound_xterm_mouse =
static auto bound_scripts =
injector::bind<available_scripts>::to_singleton();
static auto bound_curl =
injector::bind_multiple<isc::service>()
.add_singleton<curl_looper, services::curl_streamer_t>();
bool setup_logline_table(exec_context &ec)
{
// Hidden columns don't show up in the table_info pragma.
@ -412,7 +420,7 @@ private:
for (auto &sc : lnav_data.ld_status) {
sc.do_update();
}
if (lnav_data.ld_mode == LNM_FILES) {
if (lnav_data.ld_mode == LNM_FILES && !lnav_data.ld_session_loaded) {
auto &fc = lnav_data.ld_active_files;
auto iter = std::find(fc.fc_files.begin(),
fc.fc_files.end(), lf);
@ -830,7 +838,9 @@ bool update_active_files(const file_collection& new_files)
}
}
lnav_data.ld_active_files.merge(new_files);
if (!new_files.fc_files.empty()) {
if (!new_files.fc_files.empty() ||
!new_files.fc_other_files.empty() ||
!new_files.fc_name_to_errors.empty()) {
lnav_data.ld_active_files.regenerate_unique_file_names();
}
@ -1293,6 +1303,8 @@ static void looper()
lnav_data.ld_files_view.set_selectable(true);
lnav_data.ld_files_view.set_window(lnav_data.ld_window);
lnav_data.ld_files_view.set_show_scrollbar(true);
lnav_data.ld_files_view.get_disabled_highlights()
.insert(highlight_source_t::THEME);
lnav_data.ld_files_view.set_overlay_source(&lnav_data.ld_files_overlay);
lnav_data.ld_status[LNS_TOP].set_top(0);
@ -1386,6 +1398,8 @@ static void looper()
bool initial_rescan_completed = false;
int session_stage = 0;
rlc.do_update();
while (lnav_data.ld_looping) {
static bool initial_build = false;
@ -1638,7 +1652,11 @@ static void looper()
.set_top(vis_line_t(vs.vs_top));
}
}
lnav_data.ld_mode = LNM_PAGING;
if (lnav_data.ld_active_files.fc_name_to_errors.empty()) {
lnav_data.ld_mode = LNM_PAGING;
} else {
lnav_data.ld_files_view.set_selection(0_vl);
}
session_stage += 1;
load_time_bookmarks();
}
@ -2343,7 +2361,10 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
lnav_data.ld_active_files.fc_file_names[argv[lpc]]
.with_fd(ul->copy_fd());
lnav_data.ld_curl_looper.add_request(ul);
isc::to<curl_looper&, services::curl_streamer_t>()
.send([ul](auto& clooper) {
clooper.add_request(ul);
});
}
#endif
else if (is_glob(argv[lpc])) {
@ -2526,6 +2547,8 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
usage();
}
else {
isc::service_guard serv_guard(injector::get<isc::service_list>());
try {
log_info("startup: %s", VCS_PACKAGE_STRING);
log_host_info();
@ -2600,7 +2623,10 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
execute_init_commands(lnav_data.ld_exec_context, cmd_results);
archive_manager::cleanup_cache();
wait_for_pipers();
lnav_data.ld_curl_looper.process_all();
isc::to<curl_looper&, services::curl_streamer_t>()
.send_and_wait([](auto& clooper) {
clooper.process_all();
});
rebuild_indexes();
for (auto &pair : cmd_results) {
@ -2685,9 +2711,6 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
}
}
else {
lnav_data.ld_curl_looper.start();
init_session();
guard_termios gt(STDIN_FILENO);
@ -2744,7 +2767,5 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
}
}
lnav_data.ld_curl_looper.stop();
return retval;
}

View File

@ -59,7 +59,6 @@
#include "log_vtab_impl.hh"
#include "readline_curses.hh"
#include "piper_proc.hh"
#include "curl_looper.hh"
#include "relative_time.hh"
#include "log_format_loader.hh"
#include "spectro_source.hh"
@ -266,8 +265,6 @@ struct lnav_data_t {
db_overlay_source ld_db_overlay;
std::vector<std::string> ld_db_key_names;
std::string ld_previous_search;
vis_line_t ld_last_pretty_print_top;
std::unique_ptr<log_vtab_manager> ld_vtab_manager;
@ -281,8 +278,6 @@ struct lnav_data_t {
input_state_tracker ld_input_state;
input_dispatcher ld_input_dispatcher;
curl_looper ld_curl_looper;
exec_context ld_exec_context;
int ld_fifo_counter;

View File

@ -69,6 +69,7 @@
#include "papertrail_proc.hh"
#include "yajlpp/json_op.hh"
#include "yajlpp/yajlpp.hh"
#include "service_tags.hh"
#include "sqlite-extension-func.hh"
using namespace std;
@ -107,7 +108,10 @@ static string refresh_pt_search()
}
}
lnav_data.ld_curl_looper.close_request("papertrailapp.com");
isc::to<curl_looper&, services::curl_streamer_t>()
.send([](auto& clooper) {
clooper.close_request("papertrailapp.com");
});
if (lnav_data.ld_pt_search.empty()) {
return "info: no papertrail query is active";
@ -118,7 +122,10 @@ static string refresh_pt_search()
lnav_data.ld_pt_max_time);
lnav_data.ld_active_files.fc_file_names[lnav_data.ld_pt_search]
.with_fd(pt->copy_fd());
lnav_data.ld_curl_looper.add_request(pt);
isc::to<curl_looper&, services::curl_streamer_t>()
.send([pt](auto& clooper) {
clooper.add_request(pt);
});
ensure_view(&lnav_data.ld_views[LNV_LOG]);
@ -1685,7 +1692,7 @@ static Result<string, string> com_create_search_table(exec_context &ec, string c
regex = remaining_args(cmdline, args, 2);
}
else {
regex = lnav_data.ld_views[LNV_LOG].get_last_search();
regex = lnav_data.ld_views[LNV_LOG].get_current_search();
}
if ((code = pcre_compile(regex.c_str(),
@ -1937,7 +1944,10 @@ static Result<string, string> com_open(exec_context &ec, string cmdline, vector<
lnav_data.ld_active_files.fc_file_names[fn]
.with_fd(ul->copy_fd());
lnav_data.ld_curl_looper.add_request(ul);
isc::to<curl_looper&, services::curl_streamer_t>()
.send([ul](auto& clooper) {
clooper.add_request(ul);
});
lnav_data.ld_files_to_front.emplace_back(fn, top);
retval = "info: opened URL";
} else {
@ -2151,7 +2161,10 @@ static Result<string, string> com_close(exec_context &ec, string cmdline, vector
}
else {
if (is_url(fn.c_str())) {
lnav_data.ld_curl_looper.close_request(fn);
isc::to<curl_looper&, services::curl_streamer_t>()
.send([fn](auto& clooper) {
clooper.close_request(fn);
});
}
lnav_data.ld_active_files.fc_file_names.erase(fn);
lnav_data.ld_active_files.fc_closed_files.insert(fn);
@ -2249,7 +2262,7 @@ static Result<string, string> com_file_visibility(exec_context &ec, string cmdli
if (!ec.ec_dry_run && text_file_count > 0) {
lnav_data.ld_views[LNV_TEXT].get_sub_source()->text_filters_changed();
}
retval = fmt::format("{} {} log files and {} text files",
retval = fmt::format(FMT_STRING("{} {:L} log files and {:L} text files"),
make_visible ? "showing" : "hiding",
log_file_count,
text_file_count);
@ -3361,7 +3374,10 @@ static Result<string, string> com_poll_now(exec_context &ec, string cmdline, vec
}
else if (!ec.ec_dry_run) {
lnav_data.ld_curl_looper.process_all();
isc::to<curl_looper&, services::curl_streamer_t>()
.send_and_wait([](auto& clooper) {
clooper.process_all();
});
}
return Ok(string());
@ -3529,11 +3545,15 @@ static Result<string, string> com_config(exec_context &ec, string cmdline, vecto
}
else if (args.size() > 1) {
yajlpp_parse_context ypc("input", &lnav_config_handlers);
vector<string> errors;
string option = args[1];
lnav_config = rollback_lnav_config;
ypc.set_path(option)
.with_obj(lnav_config);
.with_obj(lnav_config)
.with_error_reporter([&errors](const auto& ypc, auto level, auto* msg) {
errors.push_back(msg);
});
ypc.ypc_active_paths.insert(option);
ypc.update_callbacks();
@ -3558,8 +3578,6 @@ static Result<string, string> com_config(exec_context &ec, string cmdline, vecto
string old_value = gen.to_string_fragment().to_string();
if (args.size() == 2 || ypc.ypc_current_handler == nullptr) {
vector<string> errors;
lnav_config = rollback_lnav_config;
reload_config(errors);
@ -3588,7 +3606,6 @@ static Result<string, string> com_config(exec_context &ec, string cmdline, vecto
}
else {
string value = remaining_args(cmdline, args, 2);
vector<string> errors;
bool changed = false;
if (ec.ec_dry_run) {
@ -3610,6 +3627,17 @@ static Result<string, string> com_config(exec_context &ec, string cmdline, vecto
value.size());
changed = true;
}
else if (ypc.ypc_current_handler->jph_callbacks.yajl_integer) {
long long val = 0;
auto consumed = strtonum(val, value.c_str(), value.length());
log_debug("got val %d", (int) val);
if (consumed != value.length()) {
return ec.make_error("expecting an integer, found: {}", value);
}
ypc.ypc_callbacks.yajl_integer(&ypc, val);
changed = true;
}
else if (ypc.ypc_current_handler->jph_callbacks.yajl_boolean) {
bool bvalue = false;
@ -3623,6 +3651,10 @@ static Result<string, string> com_config(exec_context &ec, string cmdline, vecto
return ec.make_error("unhandled type");
}
if (!errors.empty()) {
return ec.make_error(errors[0]);
}
if (changed) {
intern_string_t path = intern_string::lookup(option);
@ -3635,7 +3667,7 @@ static Result<string, string> com_config(exec_context &ec, string cmdline, vecto
if (!errors.empty()) {
lnav_config = rollback_lnav_config;
reload_config(errors);
return Err("error: " +errors[0]);
return Err("error: " + errors[0]);
} else if (!ec.ec_dry_run) {
retval = "info: changed config option -- " + option;
rollback_lnav_config = lnav_config;
@ -4170,7 +4202,7 @@ static void command_prompt(vector<string> &args)
rollback_lnav_config = lnav_config;
lnav_data.ld_doc_status_source.set_title("Command Help");
add_view_text_possibilities(lnav_data.ld_rl_view, LNM_COMMAND, "filter", tc);
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND, "filter", tc->get_last_search());
lnav_data.ld_rl_view->add_possibility(LNM_COMMAND, "filter", tc->get_current_search());
add_filter_possibilities(tc);
add_mark_possibilities();
add_config_possibilities();
@ -4217,7 +4249,6 @@ static void search_prompt(vector<string> &args)
textview_curses *tc = *lnav_data.ld_view_stack.top();
lnav_data.ld_mode = LNM_SEARCH;
lnav_data.ld_previous_search = tc->get_last_search();
lnav_data.ld_search_start_line = tc->get_top();
add_view_text_possibilities(lnav_data.ld_rl_view, LNM_SEARCH, "*", tc);
lnav_data.ld_rl_view->focus(LNM_SEARCH,

View File

@ -567,6 +567,18 @@ static struct json_path_container theme_styles_handlers = {
return &root->lt_style_popup;
})
.with_children(style_config_handlers),
yajlpp::property_handler("focused")
.with_description("Styling for a focused row in a list view")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_focused;
})
.with_children(style_config_handlers),
yajlpp::property_handler("disabled-focused")
.with_description("Styling for a disabled focused row in a list view")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
return &root->lt_style_disabled_focused;
})
.with_children(style_config_handlers),
yajlpp::property_handler("scrollbar")
.with_description("Styling for scrollbars")
.with_obj_provider<style_config, lnav_theme>([](const yajlpp_provider_context &ypc, lnav_theme *root) {
@ -857,6 +869,11 @@ static struct json_path_container ui_handlers = {
};
static struct json_path_container archive_handlers = {
yajlpp::property_handler("min-free-space")
.with_description("The minimum free space to maintain when unpacking")
.with_min_value(0)
.for_field(&_lnav_config::lc_archive_manager,
&archive_manager::config::amc_min_free_space),
yajlpp::property_handler("cache-ttl")
.with_description("The time-to-live for unpacked archives")
.for_field(&_lnav_config::lc_archive_manager,

View File

@ -177,6 +177,7 @@ size_t strtonum(T &num_out, const char *string, size_t len)
num_out *= 10;
num_out += string[retval] - '0';
}
num_out *= sign;
return retval;
}

View File

@ -399,7 +399,6 @@ logfile::rebuild_result_t logfile::rebuild_index()
log_info("file is not utf, hiding: %s",
this->lf_filename.c_str());
this->lf_indexing = false;
this->lf_options.loo_non_utf_is_visible = true;
break;
}
@ -462,7 +461,6 @@ logfile::rebuild_result_t logfile::rebuild_index()
log_info("file has unknown format and is too large: %s",
this->lf_filename.c_str());
this->lf_indexing = false;
this->lf_options.loo_visible_size_limit = -1;
}
if (this->lf_logline_observer != nullptr) {

View File

@ -165,7 +165,8 @@ bool pcrepp::match(pcre_context &pc, pcre_input &pi, int options) const
return true;
default:
log_error("pcre err %d", rc);
log_error("pcre_exec error(%d) with pattern -- %s",
rc, this->p_pattern.c_str());
break;
}
}

View File

@ -237,7 +237,6 @@ void rl_change(readline_curses *rc)
lnav_data.ld_preview_source.clear();
lnav_data.ld_preview_status_source.get_description().clear();
log_debug("change");
switch (lnav_data.ld_mode) {
case LNM_COMMAND: {
static string last_command;
@ -333,7 +332,7 @@ void rl_change(readline_curses *rc)
string line = rc->get_line_buffer();
size_t name_end = line.find(' ');
string script_name = line.substr(0, name_end);
auto scripts = injector::get<available_scripts&>();
auto& scripts = injector::get<available_scripts&>();
auto iter = scripts.as_scripts.find(script_name);
if (iter == scripts.as_scripts.end() ||
@ -488,7 +487,7 @@ void lnav_rl_abort(readline_curses *rc)
switch (lnav_data.ld_mode) {
case LNM_SEARCH:
tc->set_top(lnav_data.ld_search_start_line);
tc->execute_search(lnav_data.ld_previous_search);
tc->revert_search();
break;
case LNM_SQL:
tc->reload_data();

View File

@ -9,6 +9,7 @@
},
"tuning": {
"archive-manager": {
"min-free-space": 33554432,
"cache-ttl": "2d"
}
}

42
src/service_tags.hh Normal file
View File

@ -0,0 +1,42 @@
/**
* Copyright (c) 2021, Timothy Stack
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* * Neither the name of Timothy Stack nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* @file service_tags.hh
*/
#ifndef lnav_service_tags_hh
#define lnav_service_tags_hh
namespace services {
struct ui_t {};
struct curl_streamer_t {};
}
#endif

View File

@ -718,7 +718,7 @@ static int read_files(yajlpp_parse_context *ypc, const unsigned char *str, size_
return 1;
}
static int read_last_search(yajlpp_parse_context *ypc, const unsigned char *str, size_t len)
static int read_current_search(yajlpp_parse_context *ypc, const unsigned char *str, size_t len)
{
string regex = std::string((const char *)str, len);
const char **view_name;
@ -818,7 +818,7 @@ static int read_commands(yajlpp_parse_context *ypc, const unsigned char *str, si
static struct json_path_container view_def_handlers = {
json_path_handler("top_line", read_top_line),
json_path_handler("search", read_last_search),
json_path_handler("search", read_current_search),
json_path_handler("word_wrap", read_word_wrap),
json_path_handler("filtering", read_filtering),
json_path_handler("commands#", read_commands)
@ -1363,7 +1363,7 @@ static void save_session_with_id(const std::string session_id)
}
view_map.gen("search");
view_map.gen(lnav_data.ld_views[lpc].get_last_search());
view_map.gen(lnav_data.ld_views[lpc].get_current_search());
view_map.gen("word_wrap");
view_map.gen(tc.get_word_wrap());
@ -1525,7 +1525,7 @@ void reset_session()
lnav_data.ld_filter_view.reload_data();
lnav_data.ld_files_view.reload_data();
for (auto format : log_format::get_root_formats()) {
for (const auto& format : log_format::get_root_formats()) {
auto *elf = dynamic_cast<external_log_format *>(format.get());
if (elf == nullptr) {

View File

@ -137,6 +137,8 @@ struct lnav_theme {
style_config lt_style_error;
style_config lt_style_warning;
style_config lt_style_popup;
style_config lt_style_focused;
style_config lt_style_disabled_focused;
style_config lt_style_scrollbar;
style_config lt_style_hidden;
style_config lt_style_adjusted_time;

View File

@ -430,6 +430,10 @@ void textview_curses::textview_value_for_row(vis_line_t row,
continue;
}
if (this->tc_disabled_highlights.count(tc_highlight.first.first)) {
continue;
}
// Internal highlights should only apply to the log message body so
// that we don't start highlighting other fields. User-provided
// highlights should apply only to the line itself and not any of the
@ -506,10 +510,12 @@ void textview_curses::execute_search(const std::string &regex_orig)
std::string regex = regex_orig;
pcre *code = nullptr;
if ((this->tc_search_child == nullptr) || (regex != this->tc_last_search)) {
if ((this->tc_search_child == nullptr) ||
(regex != this->tc_current_search)) {
const char *errptr;
int eoff;
this->tc_previous_search = this->tc_current_search;
this->match_reset();
this->tc_search_child.reset();
@ -578,7 +584,7 @@ void textview_curses::execute_search(const std::string &regex_orig)
}
}
this->tc_last_search = regex;
this->tc_current_search = regex;
if (this->tc_state_event_handler) {
this->tc_state_event_handler(*this);
}

View File

@ -792,7 +792,7 @@ public:
void match_reset()
{
this->tc_bookmarks[&BM_SEARCH].clear();
if (this->tc_sub_source != NULL) {
if (this->tc_sub_source != nullptr) {
this->tc_sub_source->text_clear_marks(&BM_SEARCH);
}
};
@ -801,13 +801,17 @@ public:
const highlight_map_t &get_highlights() const { return this->tc_highlights; };
std::set<highlight_source_t> &get_disabled_highlights() {
return this->tc_disabled_highlights;
}
bool handle_mouse(mouse_event &me);
void reload_data();
void do_update() {
this->listview_curses::do_update();
if (this->tc_delegate != NULL) {
if (this->tc_delegate != nullptr) {
this->tc_delegate->text_overlay(*this);
}
};
@ -880,8 +884,12 @@ public:
}
}
std::string get_last_search() const {
return this->tc_last_search;
std::string get_current_search() const {
return this->tc_current_search;
}
void revert_search() {
this->execute_search(this->tc_previous_search);
}
void invoke_scroll() {
@ -938,6 +946,7 @@ protected:
action tc_search_action;
highlight_map_t tc_highlights;
std::set<highlight_source_t> tc_disabled_highlights;
vis_line_t tc_selection_start{-1_vl};
vis_line_t tc_selection_last{-1_vl};
@ -945,7 +954,8 @@ protected:
bool tc_hide_fields{true};
bool tc_paused{false};
std::string tc_last_search;
std::string tc_current_search;
std::string tc_previous_search;
std::unique_ptr<grep_highlighter> tc_search_child;
std::shared_ptr<grep_proc<vis_line_t>> tc_source_search_child;
};

View File

@ -46,6 +46,14 @@
"popup": {
"color": "Silver",
"background-color": "Teal"
},
"focused": {
"color": "Black",
"background-color": "Silver"
},
"disabled-focused": {
"color": "Black",
"background-color": "#888"
}
},
"syntax-styles": {

View File

@ -54,6 +54,14 @@
"invalid-msg": {
"color": "$yellow"
},
"focused": {
"color": "$black",
"background-color": "$white"
},
"disabled-focused": {
"color": "$white",
"background-color": "#333"
},
"popup": {
"color": "$white",
"background-color": "$cyan"

View File

@ -10,7 +10,8 @@
"yellow": "#cdcd00",
"blue": "#5394ec",
"magenta": "#ff70ff",
"cyan": "#33cccc"
"cyan": "#33cccc",
"white": "#d6deeb"
},
"styles": {
"identifier": {
@ -53,6 +54,14 @@
"invalid-msg": {
"color": "$yellow"
},
"focused": {
"color": "$black",
"background-color": "#666"
},
"disabled-focused": {
"color": "$white",
"background-color": "#333"
},
"popup": {
"color": "$base00",
"background-color": "$base3"

View File

@ -66,6 +66,14 @@
"popup": {
"color": "$base00",
"background-color": "$base3"
},
"focused": {
"color": "$base03",
"background-color": "$base01"
},
"disabled-focused": {
"color": "$base0",
"background-color": "$base02"
}
},
"syntax-styles": {

View File

@ -66,6 +66,14 @@
"popup": {
"color": "$base00",
"background-color": "$base3"
},
"focused": {
"color": "$base03",
"background-color": "$base01"
},
"disabled-focused": {
"color": "$base0",
"background-color": "$base02"
}
},
"syntax-styles": {

View File

@ -34,6 +34,8 @@
#include <paths.h>
#include <curl/curl.h>
#include "curl_looper.hh"
class url_loader : public curl_request {
public:
url_loader(const std::string &url) : curl_request(url), ul_resume_offset(0) {

View File

@ -45,6 +45,7 @@
using namespace std;
string_attr_type view_curses::VC_ROLE("role");
string_attr_type view_curses::VC_ROLE_FG("role-fg");
string_attr_type view_curses::VC_STYLE("style");
string_attr_type view_curses::VC_GRAPHIC("graphic");
string_attr_type view_curses::VC_SELECTED("selected");
@ -218,6 +219,7 @@ void view_curses::mvwattrline(WINDOW *window,
require(attr_range.lr_end >= -1);
if (!(iter->sa_type == &VC_ROLE ||
iter->sa_type == &VC_ROLE_FG ||
iter->sa_type == &VC_STYLE ||
iter->sa_type == &VC_GRAPHIC ||
iter->sa_type == &VC_FOREGROUND ||
@ -254,15 +256,6 @@ void view_curses::mvwattrline(WINDOW *window,
attr_range.lr_end = min(line_width_chars, attr_range.lr_end - lr_chars.lr_start);
if (iter->sa_type == &VC_GRAPHIC) {
for (int index = attr_range.lr_start;
index < attr_range.lr_end;
index++) {
mvwaddch(window, y, x + index, iter->sa_value.sav_int | text_attrs);
}
continue;
}
if (iter->sa_type == &VC_FOREGROUND) {
if (!has_fg) {
memset(fg_color, -1, line_width_chars * sizeof(short));
@ -283,18 +276,33 @@ void view_curses::mvwattrline(WINDOW *window,
if (attr_range.lr_end > attr_range.lr_start) {
int awidth = attr_range.length();
int color_pair = 0;
nonstd::optional<char> graphic;
short color_pair = 0;
if (iter->sa_type == &VC_STYLE) {
if (iter->sa_type == &VC_GRAPHIC) {
graphic = iter->sa_value.sav_int;
} else if (iter->sa_type == &VC_STYLE) {
attrs = iter->sa_value.sav_int & ~A_COLOR;
color_pair = PAIR_NUMBER(iter->sa_value.sav_int);
} else if (iter->sa_type == &VC_ROLE) {
attrs = vc.attrs_for_role((view_colors::role_t) iter->sa_value.sav_int);
color_pair = PAIR_NUMBER(attrs);
attrs = attrs & ~A_COLOR;
} else if (iter->sa_type == &VC_ROLE_FG) {
short role_fg, role_bg;
attrs = vc.attrs_for_role((view_colors::role_t) iter->sa_value.sav_int);
color_pair = PAIR_NUMBER(attrs);
pair_content(color_pair, &role_fg, &role_bg);
attrs = attrs & ~A_COLOR;
if (!has_fg) {
memset(fg_color, -1, line_width_chars * sizeof(short));
}
fill(&fg_color[attr_range.lr_start], &fg_color[attr_range.lr_end], (short) role_fg);
has_fg = true;
color_pair = 0;
}
if (attrs || color_pair > 0) {
if (graphic || attrs || color_pair > 0) {
int x_pos = x + attr_range.lr_start;
int ch_width = min(awidth, (line_width_chars - attr_range.lr_start));
cchar_t row_ch[ch_width + 1];
@ -303,6 +311,10 @@ void view_curses::mvwattrline(WINDOW *window,
for (int lpc = 0; lpc < ch_width; lpc++) {
bool clear_rev = false;
if (graphic) {
row_ch[lpc].chars[0] = graphic.value();
row_ch[lpc].attr |= A_ALTCHARSET;
}
if (row_ch[lpc].attr & A_REVERSE && attrs & A_REVERSE) {
clear_rev = true;
}
@ -344,6 +356,16 @@ void view_curses::mvwattrline(WINDOW *window,
continue;
}
auto cur_pair = PAIR_NUMBER(row_ch[lpc].attr);
short cur_fg, cur_bg;
pair_content(cur_pair, &cur_fg, &cur_bg);
if (fg_color[lpc] == -1) {
fg_color[lpc] = cur_fg;
}
if (bg_color[lpc] == -1) {
bg_color[lpc] = cur_bg;
}
int color_pair = vc.ensure_color_pair(fg_color[lpc], bg_color[lpc]);
row_ch[lpc].attr = row_ch[lpc].attr & ~A_COLOR;
@ -768,6 +790,10 @@ void view_colors::init_roles(const lnav_theme &lt,
this->vc_role_colors[VCR_POPUP] = this->to_attrs(
color_pair_base, lt, lt.lt_style_popup, lt.lt_style_text, reporter);
this->vc_role_colors[VCR_FOCUSED] = this->to_attrs(
color_pair_base, lt, lt.lt_style_focused, lt.lt_style_focused, reporter);
this->vc_role_colors[VCR_DISABLED_FOCUSED] = this->to_attrs(
color_pair_base, lt, lt.lt_style_disabled_focused, lt.lt_style_disabled_focused, reporter);
this->vc_role_colors[VCR_COLOR_HINT] = make_pair(
COLOR_PAIR(color_pair_base), COLOR_PAIR(color_pair_base + 1));
color_pair_base += 2;

View File

@ -219,6 +219,8 @@ public:
VCR_SCROLLBAR,
VCR_SCROLLBAR_ERROR,
VCR_SCROLLBAR_WARNING,
VCR_FOCUSED,
VCR_DISABLED_FOCUSED,
VCR_POPUP,
VCR_COLOR_HINT,
@ -436,6 +438,7 @@ public:
}
static string_attr_type VC_ROLE;
static string_attr_type VC_ROLE_FG;
static string_attr_type VC_STYLE;
static string_attr_type VC_GRAPHIC;
static string_attr_type VC_SELECTED;

View File

@ -176,7 +176,7 @@ CREATE TABLE lnav_views (
sqlite3_result_int(ctx, tc.is_paused());
break;
case 7: {
const string &str = tc.get_last_search();
const string &str = tc.get_current_search();
sqlite3_result_text(ctx, str.c_str(), str.length(), SQLITE_TRANSIENT);
break;

View File

@ -224,6 +224,7 @@ struct json_path_handler_base {
std::vector<std::string> jph_examples;
std::function<int(yajlpp_parse_context *, int)> jph_bool_cb;
std::function<int(yajlpp_parse_context *, long long)> jph_integer_cb;
std::function<int(yajlpp_parse_context *, const unsigned char *str, size_t len)> jph_str_cb;
};
@ -231,16 +232,17 @@ struct json_path_handler;
class yajlpp_parse_context {
public:
typedef void (*error_reporter_t)(const yajlpp_parse_context &ypc,
lnav_log_level_t level,
const char *msg);
using error_reporter_t = std::function<
void(const yajlpp_parse_context &ypc,
lnav_log_level_t level,
const char *msg)>;
yajlpp_parse_context(std::string source,
const struct json_path_container *handlers = nullptr);
const char *get_path_fragment(int offset, char *frag_in, size_t &len_out) const;
const intern_string_t get_path_fragment_i(int offset) const {
intern_string_t get_path_fragment_i(int offset) const {
char fragbuf[this->ypc_path.size()];
const char *frag;
size_t len;

View File

@ -259,6 +259,10 @@ struct json_path_handler : public json_path_handler_base {
return ypc->ypc_current_handler->jph_str_cb(ypc, str, len);
};
static int int_field_cb(yajlpp_parse_context *ypc, long long val) {
return ypc->ypc_current_handler->jph_integer_cb(ypc, val);
};
template<typename T, typename NUM_T, NUM_T T::*NUM>
static int num_field_cb(yajlpp_parse_context *ypc, long long num)
{
@ -478,6 +482,17 @@ struct json_path_handler : public json_path_handler_base {
static constexpr bool value = std::is_enum<U>::value;
};
template<typename T, typename... Args>
struct LastIsNumber {
static constexpr bool value = LastIsNumber<Args...>::value;
};
template<typename T, typename U>
struct LastIsNumber<U T::*> {
static constexpr bool value = std::is_integral<U>::value &&
!std::is_same<U, bool>::value;
};
template<
typename... Args,
std::enable_if_t<LastIs<bool, Args...>::value, bool> = true
@ -491,6 +506,73 @@ struct json_path_handler : public json_path_handler_base {
return 1;
};
this->jph_gen_callback = [args...](yajlpp_gen_context &ygc,
const json_path_handler_base &jph,
yajl_gen handle) {
const auto& field = json_path_handler::get_field(ygc.ygc_obj_stack.top(), args...);
if (!ygc.ygc_default_stack.empty()) {
const auto& field_def = json_path_handler::get_field(ygc.ygc_default_stack.top(), args...);
if (field == field_def) {
return yajl_gen_status_ok;
}
}
if (ygc.ygc_depth) {
yajl_gen_string(handle, jph.jph_property);
}
yajlpp_generator gen(handle);
return gen(field);
};
return *this;
}
template<
typename... Args,
std::enable_if_t<LastIsNumber<Args...>::value, bool> = true
>
json_path_handler &for_field(Args... args) {
this->add_cb(int_field_cb);
this->jph_integer_cb = [args...](yajlpp_parse_context *ypc, long long val) {
auto obj = ypc->ypc_obj_stack.top();
if (val < ypc->ypc_current_handler->jph_min_value) {
ypc->report_error(lnav_log_level_t::ERROR,
"value must be greater than or equal to %lld, found %lld",
ypc->ypc_current_handler->jph_min_value,
val);
return 1;
}
json_path_handler::get_field(obj, args...) = val;
return 1;
};
this->jph_gen_callback = [args...](yajlpp_gen_context &ygc,
const json_path_handler_base &jph,
yajl_gen handle) {
const auto& field = json_path_handler::get_field(ygc.ygc_obj_stack.top(), args...);
if (!ygc.ygc_default_stack.empty()) {
const auto& field_def = json_path_handler::get_field(ygc.ygc_default_stack.top(), args...);
if (field == field_def) {
return yajl_gen_status_ok;
}
}
if (ygc.ygc_depth) {
yajl_gen_string(handle, jph.jph_property);
}
yajlpp_generator gen(handle);
return gen(field);
};
return *this;
}
@ -639,12 +721,12 @@ struct json_path_container {
: jpc_children(children) {
}
json_path_container &with_definition_id(const std::string id) {
json_path_container &with_definition_id(const std::string& id) {
this->jpc_definition_id = id;
return *this;
}
json_path_container &with_schema_id(const std::string id) {
json_path_container &with_schema_id(const std::string& id) {
this->jpc_schema_id = id;
return *this;
}

View File

@ -38,11 +38,41 @@ EOF
fi
if test x"${LIBARCHIVE_LIBS}" != x""; then
run_test env TMPDIR=tmp ${lnav_test} -n \
-c ':config /tuning/archive-manager/min-free-space -1' \
${srcdir}/logfile_syslog.0
check_error_output "invalid min-free-space allowed?" <<EOF
command-option:1: error: value must be greater than or equal to 0, found -1
EOF
rm -rf tmp/lnav-*
if test x"${XZ_CMD}" != x""; then
${XZ_CMD} -z -c ${srcdir}/logfile_syslog.1 > logfile_syslog.1.xz
run_test env TMPDIR=tmp ${lnav_test} -n \
-c ':config /tuning/archive-manager/min-free-space 1125899906842624' \
-c ':config /tuning/archive-manager/cache-ttl 1d' \
${srcdir}/logfile_syslog.0
run_test env TMPDIR=tmp ${lnav_test} -d /tmp/lnav.err -n \
logfile_syslog.1.xz
sed -e "s|lnav-[0-9]*-archives|lnav-NNN-archives|g" \
-e "s|arc-[0-9a-z]*-logfile|arc-NNN-logfile|g" \
-e "s|space on disk \(.*\) is|space on disk (NNN) is|g" \
-e "s|${builddir}||g" \
`test_err_filename` > test_logfile.big.out
mv test_logfile.big.out `test_err_filename`
check_error_output "decompression worked?" <<EOF
error: unable to open file: /logfile_syslog.1.xz -- available space on disk (NNN) is below the minimum-free threshold (1.0PB). Unable to unpack 'logfile_syslog.1.xz' to 'tmp/lnav-NNN-archives/arc-NNN-logfile_syslog.1.xz'
EOF
run_test env TMPDIR=tmp ${lnav_test} -n \
-c ':config /tuning/archive-manager/min-free-space 33554432' \
${srcdir}/logfile_syslog.0
run_test env TMPDIR=tmp ${lnav_test} -n \
logfile_syslog.1.xz
check_output "decompression not working" <<EOF