mirror of https://github.com/tstack/lnav.git
[services] initial pass at injecting services and a bunch of other stuff
This commit is contained in:
parent
8633afdff3
commit
cac1175973
|
@ -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
|
||||
|
||||
|
|
|
@ -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 = \
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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)};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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:
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
@ -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
|
||||
|
|
|
@ -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_
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
@ -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_
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
@ -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",
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
41
src/lnav.cc
41
src/lnav.cc
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
},
|
||||
"tuning": {
|
||||
"archive-manager": {
|
||||
"min-free-space": 33554432,
|
||||
"cache-ttl": "2d"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 ®ex_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 ®ex_orig)
|
|||
}
|
||||
}
|
||||
|
||||
this->tc_last_search = regex;
|
||||
this->tc_current_search = regex;
|
||||
if (this->tc_state_event_handler) {
|
||||
this->tc_state_event_handler(*this);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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 <,
|
|||
|
||||
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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue