mirror of
https://github.com/aristocratos/btop.git
synced 2024-05-14 17:33:11 +12:00
Squashed commit of the following:
commitc296ac13cd
Merge:9a1e760
091c30a
Author: Jakob P. Liljenberg <admin@qvantnet.com> Date: Sat Aug 26 19:29:57 2023 +0200 Merge pull request #590 from nobounce/dangling-reference-config Convert parameters and config keys to std::string_view commit9a1e760a66
Merge:9c8af4d
22e64ca
Author: Jakob P. Liljenberg <admin@qvantnet.com> Date: Sat Aug 26 19:20:18 2023 +0200 Merge pull request #602 from jfouquart/main Fix getting zfs pool name with '.' char in freebsd commit9c8af4df43
Merge:8a49d8c
2217cbe
Author: Jakob P. Liljenberg <admin@qvantnet.com> Date: Sat Aug 26 19:18:55 2023 +0200 Merge pull request #601 from joske/cleanup [macos] don't check /sys on macos commit8a49d8cf45
Merge:1556388
008fcd8
Author: Jakob P. Liljenberg <admin@qvantnet.com> Date: Sat Aug 26 19:18:07 2023 +0200 Merge pull request #600 from joske/makefile [macos/freebsd] support gcc13 commit1556388c83
Merge:1b126f5
d17e1a2
Author: Jakob P. Liljenberg <admin@qvantnet.com> Date: Sat Aug 26 19:14:00 2023 +0200 Merge pull request #599 from joske/main [macos] fix temp sensor on system with many cores commitd17e1a2dac
Author: Jos Dehaes <jos.dehaes@gmail.com> Date: Fri Aug 25 16:18:39 2023 +0200 fix some warnings commit4d8aa6b118
Author: Jos Dehaes <jos.dehaes@gmail.com> Date: Fri Aug 25 15:52:58 2023 +0200 fix core check commit22e64caaff
Author: Jonathan Fouquart <jfouquart@hotmail.fr> Date: Fri Aug 25 09:37:49 2023 +0200 Fix getting zfs pool name with '.' char in freebsd commit2217cbe143
Author: Jos Dehaes <jos.dehaes@gmail.com> Date: Wed Aug 23 16:01:04 2023 +0200 [macos] don't check /sys on macos commit008fcd889e
Author: Jos Dehaes <jos.dehaes@gmail.com> Date: Wed Aug 23 16:05:00 2023 +0200 also add g++13 commit0fdca5eb03
Author: Jos Dehaes <jos.dehaes@gmail.com> Date: Wed Aug 23 15:54:07 2023 +0200 support gcc13 commitdcbdb7360d
Author: Jos Dehaes <jos.dehaes@gmail.com> Date: Wed Aug 23 15:46:47 2023 +0200 [macos] fix temp sensor on system with many cores commit1b126f55e3
Author: aristocratos <gnmjpl@gmail.com> Date: Fri Aug 4 01:08:27 2023 +0200 Update Makefile for partial static compilation on freebsd commitc8ec6bbb00
Author: aristocratos <gnmjpl@gmail.com> Date: Thu Aug 3 23:08:33 2023 +0200 Fix freebsd nullptr changes and makefile for gcc12 and newer commit8a33aab588
Merge:94e5c02
e4abcef
Author: Jakob P. Liljenberg <admin@qvantnet.com> Date: Sun Jul 30 13:21:48 2023 +0200 Merge pull request #539 from nobounce/replace-NULL-nullptr Modernize using nullptr. commit94e5c02d11
Author: aristocratos <gnmjpl@gmail.com> Date: Thu Jul 27 20:51:21 2023 +0200 Better text editing commit091c30ab2b
Author: nobounce <steffen.winter@proton.me> Date: Thu Jul 27 14:17:54 2023 +0200 Convert parameters and config keys to std::string_view Using std::string_view instead of std::string& silences a new warning from GCC 13, -Wdangling-reference Also switch return type of `getI` from int& to int, trivial types are cheaper to copy by value commite4abcefbf9
Author: nobounce <steffen.winter@proton.me> Date: Wed Jul 26 16:19:17 2023 +0200 Use nullptr instead of NULL. See https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2431.pdf TLDR: NULL is of type int and relies on proper implicit pointer conversion which may lead to issues when using overloaded functions It is also considered a 'best practise' for modern C++ and conveys the programmers intention more precisly. commitd53307f14c
Author: nobounce <steffen.winter@proton.me> Date: Sun Jul 23 19:53:36 2023 +0200 Fix path to Linux CI file in itself The CI file has a list of dependent files including itself. The path was not updated when the CI was split into different files commit594f42b9eb
Merge:aca2e4b
53d6eba
Author: Jakob P. Liljenberg <admin@qvantnet.com> Date: Wed Jul 26 15:38:01 2023 +0200 Merge pull request #584 from nobounce/nb/fix-ci-path Fix path to Linux CI file in itself commitaca2e4be75
Author: aristocratos <gnmjpl@gmail.com> Date: Wed Jul 26 14:38:48 2023 +0200 Fix whitespace indent -> tab indent commit33faa01910
Author: aristocratos <gnmjpl@gmail.com> Date: Wed Jul 26 14:34:15 2023 +0200 Revert fmt submodule to static fmt folder in include commit53d6ebabc0
Author: nobounce <steffen.winter@proton.me> Date: Sun Jul 23 19:53:36 2023 +0200 Fix path to Linux CI file in itself The CI file has a list of dependent files including itself. The path was not updated when the CI was split into different files
This commit is contained in:
parent
346c9e479b
commit
bd5d697830
4
.github/workflows/continuous-build-linux.yml
vendored
4
.github/workflows/continuous-build-linux.yml
vendored
|
@ -13,7 +13,7 @@ on:
|
|||
- '!src/freebsd/**'
|
||||
- 'include/**'
|
||||
- 'Makefile'
|
||||
- '.github/workflows/continuous-build.yml'
|
||||
- '.github/workflows/continuous-build-linux.yml'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
|
@ -23,7 +23,7 @@ on:
|
|||
- '!src/freebsd/**'
|
||||
- 'include/**'
|
||||
- 'Makefile'
|
||||
- '.github/workflows/continuous-build.yml'
|
||||
- '.github/workflows/continuous-build-linux.yml'
|
||||
|
||||
jobs:
|
||||
static-build:
|
||||
|
|
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -1,3 +0,0 @@
|
|||
[submodule "lib/fmt"]
|
||||
path = lib/fmt
|
||||
url = https://github.com/fmtlib/fmt
|
17
Makefile
17
Makefile
|
@ -54,7 +54,11 @@ ifeq ($(CXX_IS_CLANG),true)
|
|||
endif
|
||||
ifeq ($(CLANG_WORKS),false)
|
||||
#? Try to find a newer GCC version
|
||||
ifeq ($(shell command -v g++-12 >/dev/null; echo $$?),0)
|
||||
ifeq ($(shell command -v g++-13 >/dev/null; echo $$?),0)
|
||||
CXX := g++-13
|
||||
else ifeq ($(shell command -v g++13 >/dev/null; echo $$?),0)
|
||||
CXX := g++13
|
||||
else ifeq ($(shell command -v g++-12 >/dev/null; echo $$?),0)
|
||||
CXX := g++-12
|
||||
else ifeq ($(shell command -v g++12 >/dev/null; echo $$?),0)
|
||||
CXX := g++12
|
||||
|
@ -89,15 +93,17 @@ ifneq ($(PLATFORM) $(ARCH),macos arm64)
|
|||
endif
|
||||
|
||||
ifeq ($(STATIC),true)
|
||||
ifeq ($(CXX_IS_CLANG),true)
|
||||
ifeq ($(CXX_IS_CLANG) $(CLANG_WORKS),true true)
|
||||
ifeq ($(shell $(CXX) -print-target-triple | grep gnu >/dev/null; echo $$?),0)
|
||||
$(error $(shell printf "\033[1;91mERROR: \033[97m$(CXX) can't statically link glibc\033[0m"))
|
||||
endif
|
||||
else
|
||||
override ADDFLAGS += -static-libgcc -static-libstdc++
|
||||
endif
|
||||
ifneq ($(PLATFORM),macos)
|
||||
ifeq ($(PLATFORM_LC),linux)
|
||||
override ADDFLAGS += -DSTATIC_BUILD -static -Wl,--fatal-warnings
|
||||
else ifeq ($(PLATFORM_LC),freebsd)
|
||||
override ADDFLAGS += -DSTATIC_BUILD
|
||||
endif
|
||||
endif
|
||||
|
||||
|
@ -120,7 +126,10 @@ else ifeq ($(PLATFORM_LC),freebsd)
|
|||
PLATFORM_DIR := freebsd
|
||||
THREADS := $(shell getconf NPROCESSORS_ONLN 2>/dev/null || echo 1)
|
||||
SU_GROUP := wheel
|
||||
override ADDFLAGS += -lstdc++ -lm -lkvm -ldevstat -Wl,-rpath=/usr/local/lib/gcc11
|
||||
override ADDFLAGS += -lm -lkvm -ldevstat -Wl,-rpath=/usr/local/lib/gcc$(CXX_VERSION_MAJOR)
|
||||
ifneq ($(STATIC),true)
|
||||
override ADDFLAGS += -lstdc++
|
||||
endif
|
||||
export MAKE = gmake
|
||||
else ifeq ($(PLATFORM_LC),macos)
|
||||
PLATFORM_DIR := osx
|
||||
|
|
18
README.md
18
README.md
|
@ -75,7 +75,7 @@ If you want to help out, test for bugs/fix bugs or just try out the branches:
|
|||
```bash
|
||||
# Install and use Homebrew or MacPorts package managers for easy dependency installation
|
||||
brew install coreutils make gcc@11
|
||||
git clone --recursive https://github.com/aristocratos/btop.git
|
||||
git clone https://github.com/aristocratos/btop.git
|
||||
cd btop
|
||||
git checkout OSX
|
||||
gmake
|
||||
|
@ -84,7 +84,7 @@ gmake
|
|||
**FreeBSD**
|
||||
```bash
|
||||
sudo pkg install gmake gcc11 coreutils git
|
||||
git clone --recursive https://github.com/aristocratos/btop.git
|
||||
git clone https://github.com/aristocratos/btop.git
|
||||
cd btop
|
||||
git checkout freebsd
|
||||
gmake
|
||||
|
@ -323,7 +323,7 @@ Also needs a UTF8 locale and a font that covers:
|
|||
2. **Clone repository**
|
||||
|
||||
```bash
|
||||
git clone --recursive https://github.com/aristocratos/btop.git
|
||||
git clone https://github.com/aristocratos/btop.git
|
||||
cd btop
|
||||
```
|
||||
|
||||
|
@ -404,7 +404,7 @@ Also needs a UTF8 locale and a font that covers:
|
|||
## Compilation macOS OSX
|
||||
|
||||
Needs GCC 10 or higher, (GCC 11 or above strongly recommended for better CPU efficiency in the compiled binary).
|
||||
|
||||
|
||||
GCC 12 needed for macOS Ventura. If you get linker errors on Ventura you'll need to upgrade your command line tools (Version 14.0) is bugged.
|
||||
|
||||
The makefile also needs GNU coreutils and `sed`.
|
||||
|
@ -420,14 +420,14 @@ Also needs a UTF8 locale and a font that covers:
|
|||
2. **Clone repository**
|
||||
|
||||
```bash
|
||||
git clone --recursive https://github.com/aristocratos/btop.git
|
||||
git clone https://github.com/aristocratos/btop.git
|
||||
cd btop
|
||||
```
|
||||
|
||||
3. **Compile**
|
||||
|
||||
Append `VERBOSE=true` to display full compiler/linker commands.
|
||||
|
||||
|
||||
Append `STATIC=true` for static compilation (only libgcc and libstdc++ will be static!).
|
||||
|
||||
Append `QUIET=true` for less verbose output.
|
||||
|
@ -506,7 +506,7 @@ Also needs a UTF8 locale and a font that covers:
|
|||
2. **Clone repository**
|
||||
|
||||
```bash
|
||||
git clone --recursive https://github.com/aristocratos/btop.git
|
||||
git clone https://github.com/aristocratos/btop.git
|
||||
cd btop
|
||||
```
|
||||
|
||||
|
@ -514,6 +514,8 @@ Also needs a UTF8 locale and a font that covers:
|
|||
|
||||
Append `VERBOSE=true` to display full compiler/linker commands.
|
||||
|
||||
Append `STATIC=true` for static compilation (only libgcc and libstdc++ will be static!).
|
||||
|
||||
Append `QUIET=true` for less verbose output.
|
||||
|
||||
Append `STRIP=true` to force stripping of debug symbols (adds `-s` linker flag).
|
||||
|
@ -620,7 +622,7 @@ Also needs a UTF8 locale and a font that covers:
|
|||
|
||||
```bash
|
||||
sudo snap connect btop:removable-media
|
||||
or
|
||||
or
|
||||
sudo snap connect btop-desktop:removable-media
|
||||
```
|
||||
|
||||
|
|
27
include/fmt/LICENSE.rst
Normal file
27
include/fmt/LICENSE.rst
Normal file
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
--- Optional exception to the license ---
|
||||
|
||||
As an exception, if, as a result of your compiling your source code, portions
|
||||
of this Software are embedded into a machine-executable object form of such
|
||||
source code, you may redistribute such embedded portions in such object form
|
||||
without including the above copyright and permission notices.
|
234
include/fmt/args.h
Normal file
234
include/fmt/args.h
Normal file
|
@ -0,0 +1,234 @@
|
|||
// Formatting library for C++ - dynamic format arguments
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_ARGS_H_
|
||||
#define FMT_ARGS_H_
|
||||
|
||||
#include <functional> // std::reference_wrapper
|
||||
#include <memory> // std::unique_ptr
|
||||
#include <vector>
|
||||
|
||||
#include "core.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T> struct is_reference_wrapper : std::false_type {};
|
||||
template <typename T>
|
||||
struct is_reference_wrapper<std::reference_wrapper<T>> : std::true_type {};
|
||||
|
||||
template <typename T> const T& unwrap(const T& v) { return v; }
|
||||
template <typename T> const T& unwrap(const std::reference_wrapper<T>& v) {
|
||||
return static_cast<const T&>(v);
|
||||
}
|
||||
|
||||
class dynamic_arg_list {
|
||||
// Workaround for clang's -Wweak-vtables. Unlike for regular classes, for
|
||||
// templates it doesn't complain about inability to deduce single translation
|
||||
// unit for placing vtable. So storage_node_base is made a fake template.
|
||||
template <typename = void> struct node {
|
||||
virtual ~node() = default;
|
||||
std::unique_ptr<node<>> next;
|
||||
};
|
||||
|
||||
template <typename T> struct typed_node : node<> {
|
||||
T value;
|
||||
|
||||
template <typename Arg>
|
||||
FMT_CONSTEXPR typed_node(const Arg& arg) : value(arg) {}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR typed_node(const basic_string_view<Char>& arg)
|
||||
: value(arg.data(), arg.size()) {}
|
||||
};
|
||||
|
||||
std::unique_ptr<node<>> head_;
|
||||
|
||||
public:
|
||||
template <typename T, typename Arg> const T& push(const Arg& arg) {
|
||||
auto new_node = std::unique_ptr<typed_node<T>>(new typed_node<T>(arg));
|
||||
auto& value = new_node->value;
|
||||
new_node->next = std::move(head_);
|
||||
head_ = std::move(new_node);
|
||||
return value;
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
/**
|
||||
\rst
|
||||
A dynamic version of `fmt::format_arg_store`.
|
||||
It's equipped with a storage to potentially temporary objects which lifetimes
|
||||
could be shorter than the format arguments object.
|
||||
|
||||
It can be implicitly converted into `~fmt::basic_format_args` for passing
|
||||
into type-erased formatting functions such as `~fmt::vformat`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename Context>
|
||||
class dynamic_format_arg_store
|
||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||
// Workaround a GCC template argument substitution bug.
|
||||
: public basic_format_args<Context>
|
||||
#endif
|
||||
{
|
||||
private:
|
||||
using char_type = typename Context::char_type;
|
||||
|
||||
template <typename T> struct need_copy {
|
||||
static constexpr detail::type mapped_type =
|
||||
detail::mapped_type_constant<T, Context>::value;
|
||||
|
||||
enum {
|
||||
value = !(detail::is_reference_wrapper<T>::value ||
|
||||
std::is_same<T, basic_string_view<char_type>>::value ||
|
||||
std::is_same<T, detail::std_string_view<char_type>>::value ||
|
||||
(mapped_type != detail::type::cstring_type &&
|
||||
mapped_type != detail::type::string_type &&
|
||||
mapped_type != detail::type::custom_type))
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using stored_type = conditional_t<
|
||||
std::is_convertible<T, std::basic_string<char_type>>::value &&
|
||||
!detail::is_reference_wrapper<T>::value,
|
||||
std::basic_string<char_type>, T>;
|
||||
|
||||
// Storage of basic_format_arg must be contiguous.
|
||||
std::vector<basic_format_arg<Context>> data_;
|
||||
std::vector<detail::named_arg_info<char_type>> named_info_;
|
||||
|
||||
// Storage of arguments not fitting into basic_format_arg must grow
|
||||
// without relocation because items in data_ refer to it.
|
||||
detail::dynamic_arg_list dynamic_args_;
|
||||
|
||||
friend class basic_format_args<Context>;
|
||||
|
||||
unsigned long long get_types() const {
|
||||
return detail::is_unpacked_bit | data_.size() |
|
||||
(named_info_.empty()
|
||||
? 0ULL
|
||||
: static_cast<unsigned long long>(detail::has_named_args_bit));
|
||||
}
|
||||
|
||||
const basic_format_arg<Context>* data() const {
|
||||
return named_info_.empty() ? data_.data() : data_.data() + 1;
|
||||
}
|
||||
|
||||
template <typename T> void emplace_arg(const T& arg) {
|
||||
data_.emplace_back(detail::make_arg<Context>(arg));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void emplace_arg(const detail::named_arg<char_type, T>& arg) {
|
||||
if (named_info_.empty()) {
|
||||
constexpr const detail::named_arg_info<char_type>* zero_ptr{nullptr};
|
||||
data_.insert(data_.begin(), {zero_ptr, 0});
|
||||
}
|
||||
data_.emplace_back(detail::make_arg<Context>(detail::unwrap(arg.value)));
|
||||
auto pop_one = [](std::vector<basic_format_arg<Context>>* data) {
|
||||
data->pop_back();
|
||||
};
|
||||
std::unique_ptr<std::vector<basic_format_arg<Context>>, decltype(pop_one)>
|
||||
guard{&data_, pop_one};
|
||||
named_info_.push_back({arg.name, static_cast<int>(data_.size() - 2u)});
|
||||
data_[0].value_.named_args = {named_info_.data(), named_info_.size()};
|
||||
guard.release();
|
||||
}
|
||||
|
||||
public:
|
||||
constexpr dynamic_format_arg_store() = default;
|
||||
|
||||
/**
|
||||
\rst
|
||||
Adds an argument into the dynamic store for later passing to a formatting
|
||||
function.
|
||||
|
||||
Note that custom types and string types (but not string views) are copied
|
||||
into the store dynamically allocating memory if necessary.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
store.push_back(42);
|
||||
store.push_back("abc");
|
||||
store.push_back(1.5f);
|
||||
std::string result = fmt::vformat("{} and {} and {}", store);
|
||||
\endrst
|
||||
*/
|
||||
template <typename T> void push_back(const T& arg) {
|
||||
if (detail::const_check(need_copy<T>::value))
|
||||
emplace_arg(dynamic_args_.push<stored_type<T>>(arg));
|
||||
else
|
||||
emplace_arg(detail::unwrap(arg));
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Adds a reference to the argument into the dynamic store for later passing to
|
||||
a formatting function.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::dynamic_format_arg_store<fmt::format_context> store;
|
||||
char band[] = "Rolling Stones";
|
||||
store.push_back(std::cref(band));
|
||||
band[9] = 'c'; // Changing str affects the output.
|
||||
std::string result = fmt::vformat("{}", store);
|
||||
// result == "Rolling Scones"
|
||||
\endrst
|
||||
*/
|
||||
template <typename T> void push_back(std::reference_wrapper<T> arg) {
|
||||
static_assert(
|
||||
need_copy<T>::value,
|
||||
"objects of built-in types and string views are always copied");
|
||||
emplace_arg(arg.get());
|
||||
}
|
||||
|
||||
/**
|
||||
Adds named argument into the dynamic store for later passing to a formatting
|
||||
function. ``std::reference_wrapper`` is supported to avoid copying of the
|
||||
argument. The name is always copied into the store.
|
||||
*/
|
||||
template <typename T>
|
||||
void push_back(const detail::named_arg<char_type, T>& arg) {
|
||||
const char_type* arg_name =
|
||||
dynamic_args_.push<std::basic_string<char_type>>(arg.name).c_str();
|
||||
if (detail::const_check(need_copy<T>::value)) {
|
||||
emplace_arg(
|
||||
fmt::arg(arg_name, dynamic_args_.push<stored_type<T>>(arg.value)));
|
||||
} else {
|
||||
emplace_arg(fmt::arg(arg_name, arg.value));
|
||||
}
|
||||
}
|
||||
|
||||
/** Erase all elements from the store */
|
||||
void clear() {
|
||||
data_.clear();
|
||||
named_info_.clear();
|
||||
dynamic_args_ = detail::dynamic_arg_list();
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Reserves space to store at least *new_cap* arguments including
|
||||
*new_cap_named* named arguments.
|
||||
\endrst
|
||||
*/
|
||||
void reserve(size_t new_cap, size_t new_cap_named) {
|
||||
FMT_ASSERT(new_cap >= new_cap_named,
|
||||
"Set of arguments includes set of named arguments");
|
||||
data_.reserve(new_cap);
|
||||
named_info_.reserve(new_cap_named);
|
||||
}
|
||||
};
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_ARGS_H_
|
2268
include/fmt/chrono.h
Normal file
2268
include/fmt/chrono.h
Normal file
File diff suppressed because it is too large
Load diff
633
include/fmt/color.h
Normal file
633
include/fmt/color.h
Normal file
|
@ -0,0 +1,633 @@
|
|||
// Formatting library for C++ - color support
|
||||
//
|
||||
// Copyright (c) 2018 - present, Victor Zverovich and fmt contributors
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_COLOR_H_
|
||||
#define FMT_COLOR_H_
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_BEGIN_EXPORT
|
||||
|
||||
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
|
||||
|
||||
enum class terminal_color : uint8_t {
|
||||
black = 30,
|
||||
red,
|
||||
green,
|
||||
yellow,
|
||||
blue,
|
||||
magenta,
|
||||
cyan,
|
||||
white,
|
||||
bright_black = 90,
|
||||
bright_red,
|
||||
bright_green,
|
||||
bright_yellow,
|
||||
bright_blue,
|
||||
bright_magenta,
|
||||
bright_cyan,
|
||||
bright_white
|
||||
};
|
||||
|
||||
enum class emphasis : uint8_t {
|
||||
bold = 1,
|
||||
faint = 1 << 1,
|
||||
italic = 1 << 2,
|
||||
underline = 1 << 3,
|
||||
blink = 1 << 4,
|
||||
reverse = 1 << 5,
|
||||
conceal = 1 << 6,
|
||||
strikethrough = 1 << 7,
|
||||
};
|
||||
|
||||
// rgb is a struct for red, green and blue colors.
|
||||
// Using the name "rgb" makes some editors show the color in a tooltip.
|
||||
struct rgb {
|
||||
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;
|
||||
};
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
// color is a struct of either a rgb color or a terminal color.
|
||||
struct color_type {
|
||||
FMT_CONSTEXPR color_type() noexcept : is_rgb(), value{} {}
|
||||
FMT_CONSTEXPR color_type(color rgb_color) noexcept : is_rgb(true), value{} {
|
||||
value.rgb_color = static_cast<uint32_t>(rgb_color);
|
||||
}
|
||||
FMT_CONSTEXPR color_type(rgb rgb_color) 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) noexcept
|
||||
: is_rgb(), value{} {
|
||||
value.term_color = static_cast<uint8_t>(term_color);
|
||||
}
|
||||
bool is_rgb;
|
||||
union color_union {
|
||||
uint8_t term_color;
|
||||
uint32_t rgb_color;
|
||||
} value;
|
||||
};
|
||||
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
/** A text style consisting of foreground and background colors and emphasis. */
|
||||
class text_style {
|
||||
public:
|
||||
FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept
|
||||
: set_foreground_color(), set_background_color(), ems(em) {}
|
||||
|
||||
FMT_CONSTEXPR text_style& operator|=(const text_style& rhs) {
|
||||
if (!set_foreground_color) {
|
||||
set_foreground_color = rhs.set_foreground_color;
|
||||
foreground_color = rhs.foreground_color;
|
||||
} else if (rhs.set_foreground_color) {
|
||||
if (!foreground_color.is_rgb || !rhs.foreground_color.is_rgb)
|
||||
FMT_THROW(format_error("can't OR a terminal color"));
|
||||
foreground_color.value.rgb_color |= rhs.foreground_color.value.rgb_color;
|
||||
}
|
||||
|
||||
if (!set_background_color) {
|
||||
set_background_color = rhs.set_background_color;
|
||||
background_color = rhs.background_color;
|
||||
} else if (rhs.set_background_color) {
|
||||
if (!background_color.is_rgb || !rhs.background_color.is_rgb)
|
||||
FMT_THROW(format_error("can't OR a terminal color"));
|
||||
background_color.value.rgb_color |= rhs.background_color.value.rgb_color;
|
||||
}
|
||||
|
||||
ems = static_cast<emphasis>(static_cast<uint8_t>(ems) |
|
||||
static_cast<uint8_t>(rhs.ems));
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend FMT_CONSTEXPR text_style operator|(text_style lhs,
|
||||
const text_style& rhs) {
|
||||
return lhs |= rhs;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR bool has_foreground() const noexcept {
|
||||
return set_foreground_color;
|
||||
}
|
||||
FMT_CONSTEXPR bool has_background() const noexcept {
|
||||
return set_background_color;
|
||||
}
|
||||
FMT_CONSTEXPR bool has_emphasis() const noexcept {
|
||||
return static_cast<uint8_t>(ems) != 0;
|
||||
}
|
||||
FMT_CONSTEXPR detail::color_type get_foreground() const noexcept {
|
||||
FMT_ASSERT(has_foreground(), "no foreground specified for this style");
|
||||
return foreground_color;
|
||||
}
|
||||
FMT_CONSTEXPR detail::color_type get_background() const noexcept {
|
||||
FMT_ASSERT(has_background(), "no background specified for this style");
|
||||
return background_color;
|
||||
}
|
||||
FMT_CONSTEXPR emphasis get_emphasis() const noexcept {
|
||||
FMT_ASSERT(has_emphasis(), "no emphasis specified for this style");
|
||||
return ems;
|
||||
}
|
||||
|
||||
private:
|
||||
FMT_CONSTEXPR text_style(bool is_foreground,
|
||||
detail::color_type text_color) 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 text_style fg(detail::color_type foreground) noexcept;
|
||||
|
||||
friend FMT_CONSTEXPR text_style bg(detail::color_type background) noexcept;
|
||||
|
||||
detail::color_type foreground_color;
|
||||
detail::color_type background_color;
|
||||
bool set_foreground_color;
|
||||
bool set_background_color;
|
||||
emphasis ems;
|
||||
};
|
||||
|
||||
/** Creates a text style from the foreground (text) color. */
|
||||
FMT_CONSTEXPR inline text_style fg(detail::color_type foreground) noexcept {
|
||||
return text_style(true, foreground);
|
||||
}
|
||||
|
||||
/** Creates a text style from the background color. */
|
||||
FMT_CONSTEXPR inline text_style bg(detail::color_type background) noexcept {
|
||||
return text_style(false, background);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR inline text_style operator|(emphasis lhs, emphasis rhs) noexcept {
|
||||
return text_style(lhs) | rhs;
|
||||
}
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
template <typename Char> struct ansi_color_escape {
|
||||
FMT_CONSTEXPR ansi_color_escape(detail::color_type text_color,
|
||||
const char* esc) noexcept {
|
||||
// If we have a terminal color, we need to output another escape code
|
||||
// sequence.
|
||||
if (!text_color.is_rgb) {
|
||||
bool is_background = esc == string_view("\x1b[48;2;");
|
||||
uint32_t value = text_color.value.term_color;
|
||||
// Background ASCII codes are the same as the foreground ones but with
|
||||
// 10 more.
|
||||
if (is_background) value += 10u;
|
||||
|
||||
size_t index = 0;
|
||||
buffer[index++] = static_cast<Char>('\x1b');
|
||||
buffer[index++] = static_cast<Char>('[');
|
||||
|
||||
if (value >= 100u) {
|
||||
buffer[index++] = static_cast<Char>('1');
|
||||
value %= 100u;
|
||||
}
|
||||
buffer[index++] = static_cast<Char>('0' + value / 10u);
|
||||
buffer[index++] = static_cast<Char>('0' + value % 10u);
|
||||
|
||||
buffer[index++] = static_cast<Char>('m');
|
||||
buffer[index++] = static_cast<Char>('\0');
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 7; i++) {
|
||||
buffer[i] = static_cast<Char>(esc[i]);
|
||||
}
|
||||
rgb color(text_color.value.rgb_color);
|
||||
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);
|
||||
}
|
||||
FMT_CONSTEXPR ansi_color_escape(emphasis em) noexcept {
|
||||
uint8_t em_codes[num_emphases] = {};
|
||||
if (has_emphasis(em, emphasis::bold)) em_codes[0] = 1;
|
||||
if (has_emphasis(em, emphasis::faint)) em_codes[1] = 2;
|
||||
if (has_emphasis(em, emphasis::italic)) em_codes[2] = 3;
|
||||
if (has_emphasis(em, emphasis::underline)) em_codes[3] = 4;
|
||||
if (has_emphasis(em, emphasis::blink)) em_codes[4] = 5;
|
||||
if (has_emphasis(em, emphasis::reverse)) em_codes[5] = 7;
|
||||
if (has_emphasis(em, emphasis::conceal)) em_codes[6] = 8;
|
||||
if (has_emphasis(em, emphasis::strikethrough)) em_codes[7] = 9;
|
||||
|
||||
size_t index = 0;
|
||||
for (size_t i = 0; i < num_emphases; ++i) {
|
||||
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]);
|
||||
buffer[index++] = static_cast<Char>('m');
|
||||
}
|
||||
buffer[index++] = static_cast<Char>(0);
|
||||
}
|
||||
FMT_CONSTEXPR operator const Char*() const noexcept { return buffer; }
|
||||
|
||||
FMT_CONSTEXPR const Char* begin() const noexcept { return buffer; }
|
||||
FMT_CONSTEXPR_CHAR_TRAITS const Char* end() const noexcept {
|
||||
return buffer + std::char_traits<Char>::length(buffer);
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr size_t num_emphases = 8;
|
||||
Char buffer[7u + 3u * num_emphases + 1u];
|
||||
|
||||
static FMT_CONSTEXPR void to_esc(uint8_t c, Char* out,
|
||||
char delimiter) noexcept {
|
||||
out[0] = static_cast<Char>('0' + c / 100);
|
||||
out[1] = static_cast<Char>('0' + c / 10 % 10);
|
||||
out[2] = static_cast<Char>('0' + c % 10);
|
||||
out[3] = static_cast<Char>(delimiter);
|
||||
}
|
||||
static FMT_CONSTEXPR bool has_emphasis(emphasis em, emphasis mask) noexcept {
|
||||
return static_cast<uint8_t>(em) & static_cast<uint8_t>(mask);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_foreground_color(
|
||||
detail::color_type foreground) noexcept {
|
||||
return ansi_color_escape<Char>(foreground, "\x1b[38;2;");
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_background_color(
|
||||
detail::color_type background) noexcept {
|
||||
return ansi_color_escape<Char>(background, "\x1b[48;2;");
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_CONSTEXPR ansi_color_escape<Char> make_emphasis(emphasis em) noexcept {
|
||||
return ansi_color_escape<Char>(em);
|
||||
}
|
||||
|
||||
template <typename Char> inline void reset_color(buffer<Char>& buffer) {
|
||||
auto reset_color = string_view("\x1b[0m");
|
||||
buffer.append(reset_color.begin(), reset_color.end());
|
||||
}
|
||||
|
||||
template <typename T> struct styled_arg {
|
||||
const T& value;
|
||||
text_style style;
|
||||
};
|
||||
|
||||
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;
|
||||
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
|
||||
buf.append(emphasis.begin(), emphasis.end());
|
||||
}
|
||||
if (ts.has_foreground()) {
|
||||
has_style = true;
|
||||
auto foreground = detail::make_foreground_color<Char>(ts.get_foreground());
|
||||
buf.append(foreground.begin(), foreground.end());
|
||||
}
|
||||
if (ts.has_background()) {
|
||||
has_style = true;
|
||||
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);
|
||||
}
|
||||
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
inline void vprint(std::FILE* f, const text_style& ts, string_view fmt,
|
||||
format_args args) {
|
||||
// Legacy wide streams are not supported.
|
||||
auto buf = memory_buffer();
|
||||
detail::vformat_to(buf, ts, fmt, args);
|
||||
if (detail::is_utf8()) {
|
||||
detail::print(f, string_view(buf.begin(), buf.size()));
|
||||
return;
|
||||
}
|
||||
buf.push_back('\0');
|
||||
int result = std::fputs(buf.data(), f);
|
||||
if (result < 0)
|
||||
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats a string and prints it to the specified file stream using ANSI
|
||||
escape sequences to specify text formatting.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
void print(std::FILE* f, const text_style& ts, const S& format_str,
|
||||
const Args&... args) {
|
||||
vprint(f, ts, format_str,
|
||||
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats a string and prints it to stdout using ANSI escape sequences to
|
||||
specify text formatting.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print(fmt::emphasis::bold | fg(fmt::color::red),
|
||||
"Elapsed time: {0:.2f} seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_string<S>::value)>
|
||||
void print(const text_style& ts, const S& format_str, const Args&... args) {
|
||||
return print(stdout, ts, format_str, args...);
|
||||
}
|
||||
|
||||
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, detail::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 fmt::vformat(ts, detail::to_string_view(format_str),
|
||||
fmt::make_format_args<buffer_context<Char>>(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) {
|
||||
auto&& buf = detail::get_buffer<Char>(out);
|
||||
detail::vformat_to(buf, ts, format_str, args);
|
||||
return detail::get_iterator(buf, out);
|
||||
}
|
||||
|
||||
/**
|
||||
\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, detail::to_string_view(format_str),
|
||||
fmt::make_format_args<buffer_context<char_t<S>>>(args...));
|
||||
}
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<detail::styled_arg<T>, Char> : formatter<T, Char> {
|
||||
template <typename FormatContext>
|
||||
auto format(const detail::styled_arg<T>& arg, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
const auto& ts = arg.style;
|
||||
const auto& value = arg.value;
|
||||
auto out = ctx.out();
|
||||
|
||||
bool has_style = false;
|
||||
if (ts.has_emphasis()) {
|
||||
has_style = true;
|
||||
auto emphasis = detail::make_emphasis<Char>(ts.get_emphasis());
|
||||
out = std::copy(emphasis.begin(), emphasis.end(), out);
|
||||
}
|
||||
if (ts.has_foreground()) {
|
||||
has_style = true;
|
||||
auto foreground =
|
||||
detail::make_foreground_color<Char>(ts.get_foreground());
|
||||
out = std::copy(foreground.begin(), foreground.end(), out);
|
||||
}
|
||||
if (ts.has_background()) {
|
||||
has_style = true;
|
||||
auto background =
|
||||
detail::make_background_color<Char>(ts.get_background());
|
||||
out = std::copy(background.begin(), background.end(), out);
|
||||
}
|
||||
out = formatter<T, Char>::format(value, ctx);
|
||||
if (has_style) {
|
||||
auto reset_color = string_view("\x1b[0m");
|
||||
out = std::copy(reset_color.begin(), reset_color.end(), out);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns an argument that will be formatted using ANSI escape sequences,
|
||||
to be used in a formatting function.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print("Elapsed time: {0:.2f} seconds",
|
||||
fmt::styled(1.23, fmt::fg(fmt::color::green) |
|
||||
fmt::bg(fmt::color::blue)));
|
||||
\endrst
|
||||
*/
|
||||
template <typename T>
|
||||
FMT_CONSTEXPR auto styled(const T& value, text_style ts)
|
||||
-> detail::styled_arg<remove_cvref_t<T>> {
|
||||
return detail::styled_arg<remove_cvref_t<T>>{value, ts};
|
||||
}
|
||||
|
||||
FMT_END_EXPORT
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_COLOR_H_
|
605
include/fmt/compile.h
Normal file
605
include/fmt/compile.h
Normal file
|
@ -0,0 +1,605 @@
|
|||
// 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 "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
|
||||
template <typename Char, typename InputIt>
|
||||
FMT_CONSTEXPR inline counting_iterator copy_str(InputIt begin, InputIt end,
|
||||
counting_iterator it) {
|
||||
return it + (end - begin);
|
||||
}
|
||||
|
||||
template <typename OutputIt> class truncating_iterator_base {
|
||||
protected:
|
||||
OutputIt out_;
|
||||
size_t limit_;
|
||||
size_t count_ = 0;
|
||||
|
||||
truncating_iterator_base() : out_(), limit_(0) {}
|
||||
|
||||
truncating_iterator_base(OutputIt out, size_t limit)
|
||||
: out_(out), limit_(limit) {}
|
||||
|
||||
public:
|
||||
using iterator_category = std::output_iterator_tag;
|
||||
using value_type = typename std::iterator_traits<OutputIt>::value_type;
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using pointer = void;
|
||||
using reference = void;
|
||||
FMT_UNCHECKED_ITERATOR(truncating_iterator_base);
|
||||
|
||||
OutputIt base() const { return out_; }
|
||||
size_t count() const { return count_; }
|
||||
};
|
||||
|
||||
// An output iterator that truncates the output and counts the number of objects
|
||||
// written to it.
|
||||
template <typename OutputIt,
|
||||
typename Enable = typename std::is_void<
|
||||
typename std::iterator_traits<OutputIt>::value_type>::type>
|
||||
class truncating_iterator;
|
||||
|
||||
template <typename OutputIt>
|
||||
class truncating_iterator<OutputIt, std::false_type>
|
||||
: public truncating_iterator_base<OutputIt> {
|
||||
mutable typename truncating_iterator_base<OutputIt>::value_type blackhole_;
|
||||
|
||||
public:
|
||||
using value_type = typename truncating_iterator_base<OutputIt>::value_type;
|
||||
|
||||
truncating_iterator() = default;
|
||||
|
||||
truncating_iterator(OutputIt out, size_t limit)
|
||||
: truncating_iterator_base<OutputIt>(out, limit) {}
|
||||
|
||||
truncating_iterator& operator++() {
|
||||
if (this->count_++ < this->limit_) ++this->out_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
truncating_iterator operator++(int) {
|
||||
auto it = *this;
|
||||
++*this;
|
||||
return it;
|
||||
}
|
||||
|
||||
value_type& operator*() const {
|
||||
return this->count_ < this->limit_ ? *this->out_ : blackhole_;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename OutputIt>
|
||||
class truncating_iterator<OutputIt, std::true_type>
|
||||
: public truncating_iterator_base<OutputIt> {
|
||||
public:
|
||||
truncating_iterator() = default;
|
||||
|
||||
truncating_iterator(OutputIt out, size_t limit)
|
||||
: truncating_iterator_base<OutputIt>(out, limit) {}
|
||||
|
||||
template <typename T> truncating_iterator& operator=(T val) {
|
||||
if (this->count_++ < this->limit_) *this->out_++ = val;
|
||||
return *this;
|
||||
}
|
||||
|
||||
truncating_iterator& operator++() { return *this; }
|
||||
truncating_iterator& operator++(int) { return *this; }
|
||||
truncating_iterator& operator*() { return *this; }
|
||||
};
|
||||
|
||||
// A compile-time string which is compiled into fast formatting code.
|
||||
class compiled_string {};
|
||||
|
||||
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
|
||||
*/
|
||||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
# define FMT_COMPILE(s) \
|
||||
FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit)
|
||||
#else
|
||||
# define FMT_COMPILE(s) FMT_STRING(s)
|
||||
#endif
|
||||
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
template <typename Char, size_t N,
|
||||
fmt::detail_exported::fixed_string<Char, N> Str>
|
||||
struct udl_compiled_string : compiled_string {
|
||||
using char_type = Char;
|
||||
explicit constexpr operator basic_string_view<char_type>() const {
|
||||
return {Str.data, N - 1};
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
template <typename T, typename... Tail>
|
||||
const T& first(const T& value, const Tail&...) {
|
||||
return value;
|
||||
}
|
||||
|
||||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
template <typename... Args> struct type_list {};
|
||||
|
||||
// Returns a reference to the argument at index N from [first, rest...].
|
||||
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 detail::get<N - 1>(rest...);
|
||||
}
|
||||
|
||||
template <typename Char, typename... Args>
|
||||
constexpr int get_arg_index_by_name(basic_string_view<Char> name,
|
||||
type_list<Args...>) {
|
||||
return get_arg_index_by_name<Args...>(name);
|
||||
}
|
||||
|
||||
template <int N, typename> struct get_type_impl;
|
||||
|
||||
template <int N, typename... Args> struct get_type_impl<N, type_list<Args...>> {
|
||||
using type =
|
||||
remove_cvref_t<decltype(detail::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>
|
||||
constexpr 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>
|
||||
constexpr OutputIt format(OutputIt out, const Args&...) const {
|
||||
return write<Char>(out, value);
|
||||
}
|
||||
};
|
||||
|
||||
// This ensures that the argument type is convertible to `const T&`.
|
||||
template <typename T, int N, typename... Args>
|
||||
constexpr const T& get_arg_checked(const Args&... args) {
|
||||
const auto& arg = detail::get<N>(args...);
|
||||
if constexpr (detail::is_named_arg<remove_cvref_t<decltype(arg)>>()) {
|
||||
return arg.value;
|
||||
} else {
|
||||
return arg;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
struct is_compiled_format<code_unit<Char>> : std::true_type {};
|
||||
|
||||
// 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>
|
||||
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||
return write<Char>(out, get_arg_checked<T, N>(args...));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename T, int N>
|
||||
struct is_compiled_format<field<Char, T, N>> : std::true_type {};
|
||||
|
||||
// A replacement field that refers to argument with name.
|
||||
template <typename Char> struct runtime_named_field {
|
||||
using char_type = Char;
|
||||
basic_string_view<Char> name;
|
||||
|
||||
template <typename OutputIt, typename T>
|
||||
constexpr static bool try_format_argument(
|
||||
OutputIt& out,
|
||||
// [[maybe_unused]] due to unused-but-set-parameter warning in GCC 7,8,9
|
||||
[[maybe_unused]] basic_string_view<Char> arg_name, const T& arg) {
|
||||
if constexpr (is_named_arg<typename std::remove_cv<T>::type>::value) {
|
||||
if (arg_name == arg.name) {
|
||||
out = write<Char>(out, arg.value);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
constexpr OutputIt format(OutputIt out, const Args&... args) const {
|
||||
bool found = (try_format_argument(out, name, args) || ...);
|
||||
if (!found) {
|
||||
FMT_THROW(format_error("argument with specified name is not found"));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_compiled_format<runtime_named_field<Char>> : std::true_type {};
|
||||
|
||||
// A replacement field that refers to argument N and has format specifiers.
|
||||
template <typename Char, typename T, int N> struct spec_field {
|
||||
using char_type = Char;
|
||||
formatter<T, Char> fmt;
|
||||
|
||||
template <typename OutputIt, typename... Args>
|
||||
constexpr FMT_INLINE OutputIt format(OutputIt out,
|
||||
const Args&... args) const {
|
||||
const auto& vargs =
|
||||
fmt::make_format_args<basic_format_context<OutputIt, Char>>(args...);
|
||||
basic_format_context<OutputIt, Char> ctx(out, vargs);
|
||||
return fmt.format(get_arg_checked<T, N>(args...), 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>
|
||||
constexpr 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;
|
||||
};
|
||||
|
||||
enum { manual_indexing_id = -1 };
|
||||
|
||||
template <typename T, typename Char>
|
||||
constexpr parse_specs_result<T, Char> parse_specs(basic_string_view<Char> str,
|
||||
size_t pos, int next_arg_id) {
|
||||
str.remove_prefix(pos);
|
||||
auto ctx =
|
||||
compile_parse_context<Char>(str, max_value<int>(), nullptr, next_arg_id);
|
||||
auto f = formatter<T, Char>();
|
||||
auto end = f.parse(ctx);
|
||||
return {f, pos + fmt::detail::to_unsigned(end - str.data()),
|
||||
next_arg_id == 0 ? manual_indexing_id : ctx.next_arg_id()};
|
||||
}
|
||||
|
||||
template <typename Char> struct arg_id_handler {
|
||||
arg_ref<Char> arg_id;
|
||||
|
||||
constexpr int on_auto() {
|
||||
FMT_ASSERT(false, "handler cannot be used with automatic indexing");
|
||||
return 0;
|
||||
}
|
||||
constexpr int on_index(int id) {
|
||||
arg_id = arg_ref<Char>(id);
|
||||
return 0;
|
||||
}
|
||||
constexpr int on_name(basic_string_view<Char> id) {
|
||||
arg_id = arg_ref<Char>(id);
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char> struct parse_arg_id_result {
|
||||
arg_ref<Char> arg_id;
|
||||
const Char* arg_id_end;
|
||||
};
|
||||
|
||||
template <int ID, typename Char>
|
||||
constexpr auto parse_arg_id(const Char* begin, const Char* end) {
|
||||
auto handler = arg_id_handler<Char>{arg_ref<Char>{}};
|
||||
auto arg_id_end = parse_arg_id(begin, end, handler);
|
||||
return parse_arg_id_result<Char>{handler.arg_id, arg_id_end};
|
||||
}
|
||||
|
||||
template <typename T, typename Enable = void> struct field_type {
|
||||
using type = remove_cvref_t<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct field_type<T, enable_if_t<detail::is_named_arg<T>::value>> {
|
||||
using type = remove_cvref_t<decltype(T::value)>;
|
||||
};
|
||||
|
||||
template <typename T, typename Args, size_t END_POS, int ARG_INDEX, int NEXT_ID,
|
||||
typename S>
|
||||
constexpr auto parse_replacement_field_then_tail(S format_str) {
|
||||
using char_type = typename S::char_type;
|
||||
constexpr auto str = basic_string_view<char_type>(format_str);
|
||||
constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type();
|
||||
if constexpr (c == '}') {
|
||||
return parse_tail<Args, END_POS + 1, NEXT_ID>(
|
||||
field<char_type, typename field_type<T>::type, ARG_INDEX>(),
|
||||
format_str);
|
||||
} else if constexpr (c != ':') {
|
||||
FMT_THROW(format_error("expected ':'"));
|
||||
} else {
|
||||
constexpr auto result = parse_specs<typename field_type<T>::type>(
|
||||
str, END_POS + 1, NEXT_ID == manual_indexing_id ? 0 : NEXT_ID);
|
||||
if constexpr (result.end >= str.size() || str[result.end] != '}') {
|
||||
FMT_THROW(format_error("expected '}'"));
|
||||
return 0;
|
||||
} else {
|
||||
return parse_tail<Args, result.end + 1, result.next_arg_id>(
|
||||
spec_field<char_type, typename field_type<T>::type, ARG_INDEX>{
|
||||
result.fmt},
|
||||
format_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compiles a non-empty format string and returns the compiled representation
|
||||
// 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 auto str = basic_string_view<char_type>(format_str);
|
||||
if constexpr (str[POS] == '{') {
|
||||
if constexpr (POS + 1 == str.size())
|
||||
FMT_THROW(format_error("unmatched '{' in format string"));
|
||||
if constexpr (str[POS + 1] == '{') {
|
||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||
} else if constexpr (str[POS + 1] == '}' || str[POS + 1] == ':') {
|
||||
static_assert(ID != manual_indexing_id,
|
||||
"cannot switch from manual to automatic argument indexing");
|
||||
constexpr auto next_id =
|
||||
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
|
||||
return parse_replacement_field_then_tail<get_type<ID, Args>, Args,
|
||||
POS + 1, ID, next_id>(
|
||||
format_str);
|
||||
} else {
|
||||
constexpr auto arg_id_result =
|
||||
parse_arg_id<ID>(str.data() + POS + 1, str.data() + str.size());
|
||||
constexpr auto arg_id_end_pos = arg_id_result.arg_id_end - str.data();
|
||||
constexpr char_type c =
|
||||
arg_id_end_pos != str.size() ? str[arg_id_end_pos] : char_type();
|
||||
static_assert(c == '}' || c == ':', "missing '}' in format string");
|
||||
if constexpr (arg_id_result.arg_id.kind == arg_id_kind::index) {
|
||||
static_assert(
|
||||
ID == manual_indexing_id || ID == 0,
|
||||
"cannot switch from automatic to manual argument indexing");
|
||||
constexpr auto arg_index = arg_id_result.arg_id.val.index;
|
||||
return parse_replacement_field_then_tail<get_type<arg_index, Args>,
|
||||
Args, arg_id_end_pos,
|
||||
arg_index, manual_indexing_id>(
|
||||
format_str);
|
||||
} else if constexpr (arg_id_result.arg_id.kind == arg_id_kind::name) {
|
||||
constexpr auto arg_index =
|
||||
get_arg_index_by_name(arg_id_result.arg_id.val.name, Args{});
|
||||
if constexpr (arg_index >= 0) {
|
||||
constexpr auto next_id =
|
||||
ID != manual_indexing_id ? ID + 1 : manual_indexing_id;
|
||||
return parse_replacement_field_then_tail<
|
||||
decltype(get_type<arg_index, Args>::value), Args, arg_id_end_pos,
|
||||
arg_index, next_id>(format_str);
|
||||
} else if constexpr (c == '}') {
|
||||
return parse_tail<Args, arg_id_end_pos + 1, ID>(
|
||||
runtime_named_field<char_type>{arg_id_result.arg_id.val.name},
|
||||
format_str);
|
||||
} else if constexpr (c == ':') {
|
||||
return unknown_format(); // no type info for specs parsing
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if constexpr (str[POS] == '}') {
|
||||
if constexpr (POS + 1 == str.size())
|
||||
FMT_THROW(format_error("unmatched '}' in format string"));
|
||||
return parse_tail<Args, POS + 2, ID>(make_text(str, POS, 1), format_str);
|
||||
} else {
|
||||
constexpr auto end = parse_text(str, POS + 1);
|
||||
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(detail::is_compiled_string<S>::value)>
|
||||
constexpr auto compile(S format_str) {
|
||||
constexpr auto str = basic_string_view<typename S::char_type>(format_str);
|
||||
if constexpr (str.size() == 0) {
|
||||
return detail::make_text(str, 0, 0);
|
||||
} else {
|
||||
constexpr auto result =
|
||||
detail::compile_format_string<detail::type_list<Args...>, 0, 0>(
|
||||
format_str);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
#endif // defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
} // namespace detail
|
||||
|
||||
FMT_BEGIN_EXPORT
|
||||
|
||||
#if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction)
|
||||
|
||||
template <typename CompiledFormat, typename... Args,
|
||||
typename Char = typename CompiledFormat::char_type,
|
||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||
FMT_INLINE std::basic_string<Char> format(const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
auto s = std::basic_string<Char>();
|
||||
cf.format(std::back_inserter(s), args...);
|
||||
return s;
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename CompiledFormat, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_format<CompiledFormat>::value)>
|
||||
constexpr FMT_INLINE OutputIt format_to(OutputIt out, const CompiledFormat& cf,
|
||||
const Args&... args) {
|
||||
return cf.format(out, args...);
|
||||
}
|
||||
|
||||
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) {
|
||||
if constexpr (std::is_same<typename S::char_type, char>::value) {
|
||||
constexpr auto str = basic_string_view<typename S::char_type>(S());
|
||||
if constexpr (str.size() == 2 && str[0] == '{' && str[1] == '}') {
|
||||
const auto& first = detail::first(args...);
|
||||
if constexpr (detail::is_named_arg<
|
||||
remove_cvref_t<decltype(first)>>::value) {
|
||||
return fmt::to_string(first.value);
|
||||
} else {
|
||||
return fmt::to_string(first);
|
||||
}
|
||||
}
|
||||
}
|
||||
constexpr auto compiled = detail::compile<Args...>(S());
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||
detail::unknown_format>()) {
|
||||
return fmt::format(
|
||||
static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||
std::forward<Args>(args)...);
|
||||
} else {
|
||||
return fmt::format(compiled, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) {
|
||||
constexpr auto compiled = detail::compile<Args...>(S());
|
||||
if constexpr (std::is_same<remove_cvref_t<decltype(compiled)>,
|
||||
detail::unknown_format>()) {
|
||||
return fmt::format_to(
|
||||
out, static_cast<basic_string_view<typename S::char_type>>(S()),
|
||||
std::forward<Args>(args)...);
|
||||
} else {
|
||||
return fmt::format_to(out, compiled, std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename OutputIt, typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
|
||||
const S& format_str, Args&&... args) {
|
||||
auto it = fmt::format_to(detail::truncating_iterator<OutputIt>(out, n),
|
||||
format_str, std::forward<Args>(args)...);
|
||||
return {it.base(), it.count()};
|
||||
}
|
||||
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
FMT_CONSTEXPR20 size_t formatted_size(const S& format_str,
|
||||
const Args&... args) {
|
||||
return fmt::format_to(detail::counting_iterator(), format_str, args...)
|
||||
.count();
|
||||
}
|
||||
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
void print(std::FILE* f, const S& format_str, const Args&... args) {
|
||||
memory_buffer buffer;
|
||||
fmt::format_to(std::back_inserter(buffer), format_str, args...);
|
||||
detail::print(f, {buffer.data(), buffer.size()});
|
||||
}
|
||||
|
||||
template <typename S, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_compiled_string<S>::value)>
|
||||
void print(const S& format_str, const Args&... args) {
|
||||
print(stdout, format_str, args...);
|
||||
}
|
||||
|
||||
#if FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
inline namespace literals {
|
||||
template <detail_exported::fixed_string Str> constexpr auto operator""_cf() {
|
||||
using char_t = remove_cvref_t<decltype(Str.data[0])>;
|
||||
return detail::udl_compiled_string<char_t, sizeof(Str.data) / sizeof(char_t),
|
||||
Str>();
|
||||
}
|
||||
} // namespace literals
|
||||
#endif
|
||||
|
||||
FMT_END_EXPORT
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_COMPILE_H_
|
2905
include/fmt/core.h
Normal file
2905
include/fmt/core.h
Normal file
File diff suppressed because it is too large
Load diff
1662
include/fmt/format-inl.h
Normal file
1662
include/fmt/format-inl.h
Normal file
File diff suppressed because it is too large
Load diff
4731
include/fmt/format.h
Normal file
4731
include/fmt/format.h
Normal file
File diff suppressed because it is too large
Load diff
451
include/fmt/os.h
Normal file
451
include/fmt/os.h
Normal file
|
@ -0,0 +1,451 @@
|
|||
// 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_
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <system_error> // std::system_error
|
||||
|
||||
#if defined __APPLE__ || defined(__FreeBSD__)
|
||||
# include <xlocale.h> // for LC_NUMERIC_MASK on OS X
|
||||
#endif
|
||||
|
||||
#include "format.h"
|
||||
|
||||
#ifndef FMT_USE_FCNTL
|
||||
// UWP doesn't provide _pipe.
|
||||
# if FMT_HAS_INCLUDE("winapifamily.h")
|
||||
# include <winapifamily.h>
|
||||
# endif
|
||||
# if (FMT_HAS_INCLUDE(<fcntl.h>) || defined(__APPLE__) || \
|
||||
defined(__linux__)) && \
|
||||
(!defined(WINAPI_FAMILY) || \
|
||||
(WINAPI_FAMILY == WINAPI_FAMILY_DESKTOP_APP))
|
||||
# include <fcntl.h> // for O_RDONLY
|
||||
# define FMT_USE_FCNTL 1
|
||||
# else
|
||||
# define FMT_USE_FCNTL 0
|
||||
# endif
|
||||
#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
|
||||
FMT_BEGIN_EXPORT
|
||||
|
||||
/**
|
||||
\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>;
|
||||
|
||||
#ifdef _WIN32
|
||||
FMT_API const std::error_category& system_category() noexcept;
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
FMT_API void format_windows_error(buffer<char>& out, int error_code,
|
||||
const char* message) noexcept;
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
FMT_API std::system_error vwindows_error(int error_code, string_view format_str,
|
||||
format_args args);
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a :class:`std::system_error` object with the description
|
||||
of the form
|
||||
|
||||
.. parsed-literal::
|
||||
*<message>*: *<system-message>*
|
||||
|
||||
where *<message>* is the formatted message and *<system-message>* is the
|
||||
system message corresponding to the error code.
|
||||
*error_code* is a Windows error code as given by ``GetLastError``.
|
||||
If *error_code* is not a valid error code such as -1, the system message
|
||||
will look like "error -1".
|
||||
|
||||
**Example**::
|
||||
|
||||
// This throws a system_error with the description
|
||||
// cannot open file 'madeup': The system cannot find the file specified.
|
||||
// or similar (system message may vary).
|
||||
const char *filename = "madeup";
|
||||
LPOFSTRUCT of = LPOFSTRUCT();
|
||||
HFILE file = OpenFile(filename, &of, OF_READ);
|
||||
if (file == HFILE_ERROR) {
|
||||
throw fmt::windows_error(GetLastError(),
|
||||
"cannot open file '{}'", filename);
|
||||
}
|
||||
\endrst
|
||||
*/
|
||||
template <typename... Args>
|
||||
std::system_error windows_error(int error_code, string_view message,
|
||||
const Args&... args) {
|
||||
return vwindows_error(error_code, message, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
// Reports a Windows error without throwing an exception.
|
||||
// Can be used to report errors from destructors.
|
||||
FMT_API void report_windows_error(int error_code, const char* message) noexcept;
|
||||
#else
|
||||
inline const std::error_category& system_category() noexcept {
|
||||
return std::system_category();
|
||||
}
|
||||
#endif // _WIN32
|
||||
|
||||
// std::system is not available on some platforms such as iOS (#2248).
|
||||
#ifdef __OSX__
|
||||
template <typename S, typename... Args, typename Char = char_t<S>>
|
||||
void say(const S& format_str, Args&&... args) {
|
||||
std::system(format("say \"{}\"", format(format_str, args...)).c_str());
|
||||
}
|
||||
#endif
|
||||
|
||||
// A buffered file.
|
||||
class buffered_file {
|
||||
private:
|
||||
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() noexcept : file_(nullptr) {}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
FMT_API ~buffered_file() noexcept;
|
||||
|
||||
public:
|
||||
buffered_file(buffered_file&& other) 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 noexcept { return file_; }
|
||||
|
||||
FMT_API int descriptor() 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, fmt::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 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 FMT_API 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.
|
||||
TRUNC = FMT_POSIX(O_TRUNC) // Truncate the content of the file.
|
||||
};
|
||||
|
||||
// Constructs a file object which doesn't represent any file.
|
||||
file() noexcept : fd_(-1) {}
|
||||
|
||||
// Opens a file and constructs a file object representing this file.
|
||||
file(cstring_view path, int oflag);
|
||||
|
||||
public:
|
||||
file(const file&) = delete;
|
||||
void operator=(const file&) = delete;
|
||||
|
||||
file(file&& other) noexcept : fd_(other.fd_) { other.fd_ = -1; }
|
||||
|
||||
// Move assignment is not noexcept because close may throw.
|
||||
file& operator=(file&& other) {
|
||||
close();
|
||||
fd_ = other.fd_;
|
||||
other.fd_ = -1;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Destroys the object closing the file it represents if any.
|
||||
~file() noexcept;
|
||||
|
||||
// Returns the file descriptor.
|
||||
int descriptor() const noexcept { return fd_; }
|
||||
|
||||
// Closes the file.
|
||||
void close();
|
||||
|
||||
// Returns the file size. The size has signed type for consistency with
|
||||
// stat::st_size.
|
||||
long long size() const;
|
||||
|
||||
// Attempts to read count bytes from the file into the specified buffer.
|
||||
size_t read(void* buffer, size_t count);
|
||||
|
||||
// Attempts to write count bytes from the specified buffer to the file.
|
||||
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.
|
||||
static file dup(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
void dup2(int fd);
|
||||
|
||||
// Makes fd be the copy of this file descriptor, closing fd first if
|
||||
// necessary.
|
||||
void dup2(int fd, std::error_code& ec) noexcept;
|
||||
|
||||
// Creates a pipe setting up read_end and write_end file objects for reading
|
||||
// and writing respectively.
|
||||
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.
|
||||
buffered_file fdopen(const char* mode);
|
||||
|
||||
# if defined(_WIN32) && !defined(__MINGW32__)
|
||||
// Opens a file and constructs a file object representing this file by
|
||||
// wcstring_view filename. Windows only.
|
||||
static file open_windows_file(wcstring_view path, int oflag);
|
||||
# endif
|
||||
};
|
||||
|
||||
// Returns the memory page size.
|
||||
long getpagesize();
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
struct buffer_size {
|
||||
buffer_size() = default;
|
||||
size_t value = 0;
|
||||
buffer_size operator=(size_t val) const {
|
||||
auto bs = buffer_size();
|
||||
bs.value = val;
|
||||
return bs;
|
||||
}
|
||||
};
|
||||
|
||||
struct ostream_params {
|
||||
int oflag = file::WRONLY | file::CREATE | file::TRUNC;
|
||||
size_t buffer_size = BUFSIZ > 32768 ? BUFSIZ : 32768;
|
||||
|
||||
ostream_params() {}
|
||||
|
||||
template <typename... T>
|
||||
ostream_params(T... params, int new_oflag) : ostream_params(params...) {
|
||||
oflag = new_oflag;
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
ostream_params(T... params, detail::buffer_size bs)
|
||||
: ostream_params(params...) {
|
||||
this->buffer_size = bs.value;
|
||||
}
|
||||
|
||||
// Intel has a bug that results in failure to deduce a constructor
|
||||
// for empty parameter packs.
|
||||
# if defined(__INTEL_COMPILER) && __INTEL_COMPILER < 2000
|
||||
ostream_params(int new_oflag) : oflag(new_oflag) {}
|
||||
ostream_params(detail::buffer_size bs) : buffer_size(bs.value) {}
|
||||
# endif
|
||||
};
|
||||
|
||||
class file_buffer final : public buffer<char> {
|
||||
file file_;
|
||||
|
||||
FMT_API void grow(size_t) override;
|
||||
|
||||
public:
|
||||
FMT_API file_buffer(cstring_view path, const ostream_params& params);
|
||||
FMT_API file_buffer(file_buffer&& other);
|
||||
FMT_API ~file_buffer();
|
||||
|
||||
void flush() {
|
||||
if (size() == 0) return;
|
||||
file_.write(data(), size() * sizeof(data()[0]));
|
||||
clear();
|
||||
}
|
||||
|
||||
void close() {
|
||||
flush();
|
||||
file_.close();
|
||||
}
|
||||
};
|
||||
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
// Added {} below to work around default constructor error known to
|
||||
// occur in Xcode versions 7.2.1 and 8.2.1.
|
||||
constexpr detail::buffer_size buffer_size{};
|
||||
|
||||
/** A fast output stream which is not thread-safe. */
|
||||
class FMT_API ostream {
|
||||
private:
|
||||
FMT_MSC_WARNING(suppress : 4251)
|
||||
detail::file_buffer buffer_;
|
||||
|
||||
ostream(cstring_view path, const detail::ostream_params& params)
|
||||
: buffer_(path, params) {}
|
||||
|
||||
public:
|
||||
ostream(ostream&& other) : buffer_(std::move(other.buffer_)) {}
|
||||
|
||||
~ostream();
|
||||
|
||||
void flush() { buffer_.flush(); }
|
||||
|
||||
template <typename... T>
|
||||
friend ostream output_file(cstring_view path, T... params);
|
||||
|
||||
void close() { buffer_.close(); }
|
||||
|
||||
/**
|
||||
Formats ``args`` according to specifications in ``fmt`` and writes the
|
||||
output to the file.
|
||||
*/
|
||||
template <typename... T> void print(format_string<T...> fmt, T&&... args) {
|
||||
vformat_to(detail::buffer_appender<char>(buffer_), fmt,
|
||||
fmt::make_format_args(args...));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\rst
|
||||
Opens a file for writing. Supported parameters passed in *params*:
|
||||
|
||||
* ``<integer>``: Flags passed to `open
|
||||
<https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html>`_
|
||||
(``file::WRONLY | file::CREATE | file::TRUNC`` by default)
|
||||
* ``buffer_size=<integer>``: Output buffer size
|
||||
|
||||
**Example**::
|
||||
|
||||
auto out = fmt::output_file("guide.txt");
|
||||
out.print("Don't {}", "Panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
inline ostream output_file(cstring_view path, T... params) {
|
||||
return {path, detail::ostream_params(params...)};
|
||||
}
|
||||
#endif // FMT_USE_FCNTL
|
||||
|
||||
FMT_END_EXPORT
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_OS_H_
|
209
include/fmt/ostream.h
Normal file
209
include/fmt/ostream.h
Normal file
|
@ -0,0 +1,209 @@
|
|||
// Formatting library for C++ - std::ostream support
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_OSTREAM_H_
|
||||
#define FMT_OSTREAM_H_
|
||||
|
||||
#include <fstream> // std::filebuf
|
||||
|
||||
#if defined(_WIN32) && defined(__GLIBCXX__)
|
||||
# include <ext/stdio_filebuf.h>
|
||||
# include <ext/stdio_sync_filebuf.h>
|
||||
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
|
||||
# include <__std_stream>
|
||||
#endif
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Generate a unique explicit instantion in every translation unit using a tag
|
||||
// type in an anonymous namespace.
|
||||
namespace {
|
||||
struct file_access_tag {};
|
||||
} // namespace
|
||||
template <typename Tag, typename BufType, FILE* BufType::*FileMemberPtr>
|
||||
class file_access {
|
||||
friend auto get_file(BufType& obj) -> FILE* { return obj.*FileMemberPtr; }
|
||||
};
|
||||
|
||||
#if FMT_MSC_VERSION
|
||||
template class file_access<file_access_tag, std::filebuf,
|
||||
&std::filebuf::_Myfile>;
|
||||
auto get_file(std::filebuf&) -> FILE*;
|
||||
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
|
||||
template class file_access<file_access_tag, std::__stdoutbuf<char>,
|
||||
&std::__stdoutbuf<char>::__file_>;
|
||||
auto get_file(std::__stdoutbuf<char>&) -> FILE*;
|
||||
#endif
|
||||
|
||||
inline bool write_ostream_unicode(std::ostream& os, fmt::string_view data) {
|
||||
#if FMT_MSC_VERSION
|
||||
if (auto* buf = dynamic_cast<std::filebuf*>(os.rdbuf()))
|
||||
if (FILE* f = get_file(*buf)) return write_console(f, data);
|
||||
#elif defined(_WIN32) && defined(__GLIBCXX__)
|
||||
auto* rdbuf = os.rdbuf();
|
||||
FILE* c_file;
|
||||
if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf<char>*>(rdbuf))
|
||||
c_file = sfbuf->file();
|
||||
else if (auto* fbuf = dynamic_cast<__gnu_cxx::stdio_filebuf<char>*>(rdbuf))
|
||||
c_file = fbuf->file();
|
||||
else
|
||||
return false;
|
||||
if (c_file) return write_console(c_file, data);
|
||||
#elif defined(_WIN32) && defined(_LIBCPP_VERSION)
|
||||
if (auto* buf = dynamic_cast<std::__stdoutbuf<char>*>(os.rdbuf()))
|
||||
if (FILE* f = get_file(*buf)) return write_console(f, data);
|
||||
#else
|
||||
ignore_unused(os, data);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
inline bool write_ostream_unicode(std::wostream&,
|
||||
fmt::basic_string_view<wchar_t>) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write the content of buf to os.
|
||||
// It is a separate function rather than a part of vprint to simplify testing.
|
||||
template <typename Char>
|
||||
void write_buffer(std::basic_ostream<Char>& os, buffer<Char>& buf) {
|
||||
const Char* buf_data = buf.data();
|
||||
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 {
|
||||
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(buffer<Char>& buf, const T& value,
|
||||
locale_ref loc = locale_ref()) {
|
||||
auto&& format_buf = formatbuf<std::basic_streambuf<Char>>(buf);
|
||||
auto&& output = std::basic_ostream<Char>(&format_buf);
|
||||
#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR)
|
||||
if (loc) output.imbue(loc.get<std::locale>());
|
||||
#endif
|
||||
output << value;
|
||||
output.exceptions(std::ios_base::failbit | std::ios_base::badbit);
|
||||
}
|
||||
|
||||
template <typename T> struct streamed_view { const T& value; };
|
||||
|
||||
} // namespace detail
|
||||
|
||||
// Formats an object of type T that has an overloaded ostream operator<<.
|
||||
template <typename Char>
|
||||
struct basic_ostream_formatter : formatter<basic_string_view<Char>, Char> {
|
||||
void set_debug_format() = delete;
|
||||
|
||||
template <typename T, typename OutputIt>
|
||||
auto format(const T& value, basic_format_context<OutputIt, Char>& ctx) const
|
||||
-> OutputIt {
|
||||
auto buffer = basic_memory_buffer<Char>();
|
||||
detail::format_value(buffer, value, ctx.locale());
|
||||
return formatter<basic_string_view<Char>, Char>::format(
|
||||
{buffer.data(), buffer.size()}, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
using ostream_formatter = basic_ostream_formatter<char>;
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<detail::streamed_view<T>, Char>
|
||||
: basic_ostream_formatter<Char> {
|
||||
template <typename OutputIt>
|
||||
auto format(detail::streamed_view<T> view,
|
||||
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
|
||||
return basic_ostream_formatter<Char>::format(view.value, ctx);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
\rst
|
||||
Returns a view that formats `value` via an ostream ``operator<<``.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print("Current thread id: {}\n",
|
||||
fmt::streamed(std::this_thread::get_id()));
|
||||
\endrst
|
||||
*/
|
||||
template <typename T>
|
||||
auto streamed(const T& value) -> detail::streamed_view<T> {
|
||||
return {value};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
inline void vprint_directly(std::ostream& os, string_view format_str,
|
||||
format_args args) {
|
||||
auto buffer = memory_buffer();
|
||||
detail::vformat_to(buffer, format_str, args);
|
||||
detail::write_buffer(os, buffer);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
FMT_EXPORT template <typename Char>
|
||||
void vprint(std::basic_ostream<Char>& os,
|
||||
basic_string_view<type_identity_t<Char>> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) {
|
||||
auto buffer = basic_memory_buffer<Char>();
|
||||
detail::vformat_to(buffer, format_str, args);
|
||||
if (detail::write_ostream_unicode(os, {buffer.data(), buffer.size()})) return;
|
||||
detail::write_buffer(os, buffer);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to the stream *os*.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::print(cerr, "Don't {}!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
FMT_EXPORT template <typename... T>
|
||||
void print(std::ostream& os, format_string<T...> fmt, T&&... args) {
|
||||
const auto& vargs = fmt::make_format_args(args...);
|
||||
if (detail::is_utf8())
|
||||
vprint(os, fmt, vargs);
|
||||
else
|
||||
detail::vprint_directly(os, fmt, vargs);
|
||||
}
|
||||
|
||||
FMT_EXPORT
|
||||
template <typename... Args>
|
||||
void print(std::wostream& os,
|
||||
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
|
||||
Args&&... args) {
|
||||
vprint(os, fmt, fmt::make_format_args<buffer_context<wchar_t>>(args...));
|
||||
}
|
||||
|
||||
FMT_EXPORT template <typename... T>
|
||||
void println(std::ostream& os, format_string<T...> fmt, T&&... args) {
|
||||
fmt::print(os, "{}\n", fmt::format(fmt, std::forward<T>(args)...));
|
||||
}
|
||||
|
||||
FMT_EXPORT
|
||||
template <typename... Args>
|
||||
void println(std::wostream& os,
|
||||
basic_format_string<wchar_t, type_identity_t<Args>...> fmt,
|
||||
Args&&... args) {
|
||||
print(os, L"{}\n", fmt::format(fmt, std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_OSTREAM_H_
|
667
include/fmt/printf.h
Normal file
667
include/fmt/printf.h
Normal file
|
@ -0,0 +1,667 @@
|
|||
// Formatting library for C++ - legacy printf implementation
|
||||
//
|
||||
// Copyright (c) 2012 - 2016, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_PRINTF_H_
|
||||
#define FMT_PRINTF_H_
|
||||
|
||||
#include <algorithm> // std::max
|
||||
#include <limits> // std::numeric_limits
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_BEGIN_EXPORT
|
||||
|
||||
template <typename T> struct printf_formatter { printf_formatter() = delete; };
|
||||
|
||||
template <typename Char> class basic_printf_context {
|
||||
private:
|
||||
detail::buffer_appender<Char> out_;
|
||||
basic_format_args<basic_printf_context> args_;
|
||||
|
||||
public:
|
||||
using char_type = Char;
|
||||
using parse_context_type = basic_format_parse_context<Char>;
|
||||
template <typename T> using formatter_type = printf_formatter<T>;
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs a ``printf_context`` object. References to the arguments are
|
||||
stored in the context object so make sure they have appropriate lifetimes.
|
||||
\endrst
|
||||
*/
|
||||
basic_printf_context(detail::buffer_appender<Char> out,
|
||||
basic_format_args<basic_printf_context> args)
|
||||
: out_(out), args_(args) {}
|
||||
|
||||
auto out() -> detail::buffer_appender<Char> { return out_; }
|
||||
void advance_to(detail::buffer_appender<Char>) {}
|
||||
|
||||
auto locale() -> detail::locale_ref { return {}; }
|
||||
|
||||
auto arg(int id) const -> basic_format_arg<basic_printf_context> {
|
||||
return args_.get(id);
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void on_error(const char* message) {
|
||||
detail::error_handler().on_error(message);
|
||||
}
|
||||
};
|
||||
|
||||
FMT_BEGIN_DETAIL_NAMESPACE
|
||||
|
||||
// Checks if a value fits in int - used to avoid warnings about comparing
|
||||
// signed and unsigned integers.
|
||||
template <bool IsSigned> struct int_checker {
|
||||
template <typename T> static auto fits_in_int(T value) -> bool {
|
||||
unsigned max = max_value<int>();
|
||||
return value <= max;
|
||||
}
|
||||
static auto fits_in_int(bool) -> bool { return true; }
|
||||
};
|
||||
|
||||
template <> struct int_checker<true> {
|
||||
template <typename T> static auto fits_in_int(T value) -> bool {
|
||||
return value >= (std::numeric_limits<int>::min)() &&
|
||||
value <= max_value<int>();
|
||||
}
|
||||
static auto fits_in_int(int) -> bool { return true; }
|
||||
};
|
||||
|
||||
struct printf_precision_handler {
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
auto operator()(T value) -> int {
|
||||
if (!int_checker<std::numeric_limits<T>::is_signed>::fits_in_int(value))
|
||||
throw_format_error("number is too big");
|
||||
return (std::max)(static_cast<int>(value), 0);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
auto operator()(T) -> int {
|
||||
throw_format_error("precision is not integer");
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// An argument visitor that returns true iff arg is a zero integer.
|
||||
struct is_zero_int {
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
auto operator()(T value) -> bool {
|
||||
return value == 0;
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
auto operator()(T) -> bool {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> struct make_unsigned_or_bool : std::make_unsigned<T> {};
|
||||
|
||||
template <> struct make_unsigned_or_bool<bool> { using type = bool; };
|
||||
|
||||
template <typename T, typename Context> class arg_converter {
|
||||
private:
|
||||
using char_type = typename Context::char_type;
|
||||
|
||||
basic_format_arg<Context>& arg_;
|
||||
char_type type_;
|
||||
|
||||
public:
|
||||
arg_converter(basic_format_arg<Context>& arg, char_type type)
|
||||
: arg_(arg), type_(type) {}
|
||||
|
||||
void operator()(bool value) {
|
||||
if (type_ != 's') operator()<bool>(value);
|
||||
}
|
||||
|
||||
template <typename U, FMT_ENABLE_IF(std::is_integral<U>::value)>
|
||||
void operator()(U value) {
|
||||
bool is_signed = type_ == 'd' || type_ == 'i';
|
||||
using target_type = conditional_t<std::is_same<T, void>::value, U, T>;
|
||||
if (const_check(sizeof(target_type) <= sizeof(int))) {
|
||||
// Extra casts are used to silence warnings.
|
||||
if (is_signed) {
|
||||
auto n = static_cast<int>(static_cast<target_type>(value));
|
||||
arg_ = detail::make_arg<Context>(n);
|
||||
} else {
|
||||
using unsigned_type = typename make_unsigned_or_bool<target_type>::type;
|
||||
auto n = static_cast<unsigned>(static_cast<unsigned_type>(value));
|
||||
arg_ = detail::make_arg<Context>(n);
|
||||
}
|
||||
} else {
|
||||
if (is_signed) {
|
||||
// glibc's printf doesn't sign extend arguments of smaller types:
|
||||
// std::printf("%lld", -42); // prints "4294967254"
|
||||
// but we don't have to do the same because it's a UB.
|
||||
auto n = static_cast<long long>(value);
|
||||
arg_ = detail::make_arg<Context>(n);
|
||||
} else {
|
||||
auto n = static_cast<typename make_unsigned_or_bool<U>::type>(value);
|
||||
arg_ = detail::make_arg<Context>(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename U, FMT_ENABLE_IF(!std::is_integral<U>::value)>
|
||||
void operator()(U) {} // No conversion needed for non-integral types.
|
||||
};
|
||||
|
||||
// Converts an integer argument to T for printf, if T is an integral type.
|
||||
// If T is void, the argument is converted to corresponding signed or unsigned
|
||||
// type depending on the type specifier: 'd' and 'i' - signed, other -
|
||||
// unsigned).
|
||||
template <typename T, typename Context, typename Char>
|
||||
void convert_arg(basic_format_arg<Context>& arg, Char type) {
|
||||
visit_format_arg(arg_converter<T, Context>(arg, type), arg);
|
||||
}
|
||||
|
||||
// Converts an integer argument to char for printf.
|
||||
template <typename Context> class char_converter {
|
||||
private:
|
||||
basic_format_arg<Context>& arg_;
|
||||
|
||||
public:
|
||||
explicit char_converter(basic_format_arg<Context>& arg) : arg_(arg) {}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
void operator()(T value) {
|
||||
auto c = static_cast<typename Context::char_type>(value);
|
||||
arg_ = detail::make_arg<Context>(c);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
void operator()(T) {} // No conversion needed for non-integral types.
|
||||
};
|
||||
|
||||
// An argument visitor that return a pointer to a C string if argument is a
|
||||
// string or null otherwise.
|
||||
template <typename Char> struct get_cstring {
|
||||
template <typename T> auto operator()(T) -> const Char* { return nullptr; }
|
||||
auto operator()(const Char* s) -> const Char* { return s; }
|
||||
};
|
||||
|
||||
// Checks if an argument is a valid printf width specifier and sets
|
||||
// left alignment if it is negative.
|
||||
template <typename Char> class printf_width_handler {
|
||||
private:
|
||||
format_specs<Char>& specs_;
|
||||
|
||||
public:
|
||||
explicit printf_width_handler(format_specs<Char>& specs) : specs_(specs) {}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_integral<T>::value)>
|
||||
auto operator()(T value) -> unsigned {
|
||||
auto width = static_cast<uint32_or_64_or_128_t<T>>(value);
|
||||
if (detail::is_negative(value)) {
|
||||
specs_.align = align::left;
|
||||
width = 0 - width;
|
||||
}
|
||||
unsigned int_max = max_value<int>();
|
||||
if (width > int_max) throw_format_error("number is too big");
|
||||
return static_cast<unsigned>(width);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(!std::is_integral<T>::value)>
|
||||
auto operator()(T) -> unsigned {
|
||||
throw_format_error("width is not integer");
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
// Workaround for a bug with the XL compiler when initializing
|
||||
// printf_arg_formatter's base class.
|
||||
template <typename Char>
|
||||
auto make_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s)
|
||||
-> arg_formatter<Char> {
|
||||
return {iter, s, locale_ref()};
|
||||
}
|
||||
|
||||
// The ``printf`` argument formatter.
|
||||
template <typename Char>
|
||||
class printf_arg_formatter : public arg_formatter<Char> {
|
||||
private:
|
||||
using base = arg_formatter<Char>;
|
||||
using context_type = basic_printf_context<Char>;
|
||||
|
||||
context_type& context_;
|
||||
|
||||
void write_null_pointer(bool is_string = false) {
|
||||
auto s = this->specs;
|
||||
s.type = presentation_type::none;
|
||||
write_bytes(this->out, is_string ? "(null)" : "(nil)", s);
|
||||
}
|
||||
|
||||
public:
|
||||
printf_arg_formatter(buffer_appender<Char> iter, format_specs<Char>& s,
|
||||
context_type& ctx)
|
||||
: base(make_arg_formatter(iter, s)), context_(ctx) {}
|
||||
|
||||
void operator()(monostate value) { base::operator()(value); }
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(detail::is_integral<T>::value)>
|
||||
void operator()(T value) {
|
||||
// MSVC2013 fails to compile separate overloads for bool and Char so use
|
||||
// std::is_same instead.
|
||||
if (!std::is_same<T, Char>::value) {
|
||||
base::operator()(value);
|
||||
return;
|
||||
}
|
||||
format_specs<Char> fmt_specs = this->specs;
|
||||
if (fmt_specs.type != presentation_type::none &&
|
||||
fmt_specs.type != presentation_type::chr) {
|
||||
return (*this)(static_cast<int>(value));
|
||||
}
|
||||
fmt_specs.sign = sign::none;
|
||||
fmt_specs.alt = false;
|
||||
fmt_specs.fill[0] = ' '; // Ignore '0' flag for char types.
|
||||
// align::numeric needs to be overwritten here since the '0' flag is
|
||||
// ignored for non-numeric types
|
||||
if (fmt_specs.align == align::none || fmt_specs.align == align::numeric)
|
||||
fmt_specs.align = align::right;
|
||||
write<Char>(this->out, static_cast<Char>(value), fmt_specs);
|
||||
}
|
||||
|
||||
template <typename T, FMT_ENABLE_IF(std::is_floating_point<T>::value)>
|
||||
void operator()(T value) {
|
||||
base::operator()(value);
|
||||
}
|
||||
|
||||
/** Formats a null-terminated C string. */
|
||||
void operator()(const char* value) {
|
||||
if (value)
|
||||
base::operator()(value);
|
||||
else
|
||||
write_null_pointer(this->specs.type != presentation_type::pointer);
|
||||
}
|
||||
|
||||
/** Formats a null-terminated wide C string. */
|
||||
void operator()(const wchar_t* value) {
|
||||
if (value)
|
||||
base::operator()(value);
|
||||
else
|
||||
write_null_pointer(this->specs.type != presentation_type::pointer);
|
||||
}
|
||||
|
||||
void operator()(basic_string_view<Char> value) { base::operator()(value); }
|
||||
|
||||
/** Formats a pointer. */
|
||||
void operator()(const void* value) {
|
||||
if (value)
|
||||
base::operator()(value);
|
||||
else
|
||||
write_null_pointer();
|
||||
}
|
||||
|
||||
/** Formats an argument of a custom (user-defined) type. */
|
||||
void operator()(typename basic_format_arg<context_type>::handle handle) {
|
||||
auto parse_ctx = basic_format_parse_context<Char>({});
|
||||
handle.format(parse_ctx, context_);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
void parse_flags(format_specs<Char>& specs, const Char*& it, const Char* end) {
|
||||
for (; it != end; ++it) {
|
||||
switch (*it) {
|
||||
case '-':
|
||||
specs.align = align::left;
|
||||
break;
|
||||
case '+':
|
||||
specs.sign = sign::plus;
|
||||
break;
|
||||
case '0':
|
||||
specs.fill[0] = '0';
|
||||
break;
|
||||
case ' ':
|
||||
if (specs.sign != sign::plus) specs.sign = sign::space;
|
||||
break;
|
||||
case '#':
|
||||
specs.alt = true;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Char, typename GetArg>
|
||||
auto parse_header(const Char*& it, const Char* end, format_specs<Char>& specs,
|
||||
GetArg get_arg) -> int {
|
||||
int arg_index = -1;
|
||||
Char c = *it;
|
||||
if (c >= '0' && c <= '9') {
|
||||
// Parse an argument index (if followed by '$') or a width possibly
|
||||
// preceded with '0' flag(s).
|
||||
int value = parse_nonnegative_int(it, end, -1);
|
||||
if (it != end && *it == '$') { // value is an argument index
|
||||
++it;
|
||||
arg_index = value != -1 ? value : max_value<int>();
|
||||
} else {
|
||||
if (c == '0') specs.fill[0] = '0';
|
||||
if (value != 0) {
|
||||
// Nonzero value means that we parsed width and don't need to
|
||||
// parse it or flags again, so return now.
|
||||
if (value == -1) throw_format_error("number is too big");
|
||||
specs.width = value;
|
||||
return arg_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
parse_flags(specs, it, end);
|
||||
// Parse width.
|
||||
if (it != end) {
|
||||
if (*it >= '0' && *it <= '9') {
|
||||
specs.width = parse_nonnegative_int(it, end, -1);
|
||||
if (specs.width == -1) throw_format_error("number is too big");
|
||||
} else if (*it == '*') {
|
||||
++it;
|
||||
specs.width = static_cast<int>(visit_format_arg(
|
||||
detail::printf_width_handler<Char>(specs), get_arg(-1)));
|
||||
}
|
||||
}
|
||||
return arg_index;
|
||||
}
|
||||
|
||||
inline auto parse_printf_presentation_type(char c, type t)
|
||||
-> presentation_type {
|
||||
using pt = presentation_type;
|
||||
constexpr auto integral_set = sint_set | uint_set | bool_set | char_set;
|
||||
switch (c) {
|
||||
case 'd':
|
||||
return in(t, integral_set) ? pt::dec : pt::none;
|
||||
case 'o':
|
||||
return in(t, integral_set) ? pt::oct : pt::none;
|
||||
case 'x':
|
||||
return in(t, integral_set) ? pt::hex_lower : pt::none;
|
||||
case 'X':
|
||||
return in(t, integral_set) ? pt::hex_upper : pt::none;
|
||||
case 'a':
|
||||
return in(t, float_set) ? pt::hexfloat_lower : pt::none;
|
||||
case 'A':
|
||||
return in(t, float_set) ? pt::hexfloat_upper : pt::none;
|
||||
case 'e':
|
||||
return in(t, float_set) ? pt::exp_lower : pt::none;
|
||||
case 'E':
|
||||
return in(t, float_set) ? pt::exp_upper : pt::none;
|
||||
case 'f':
|
||||
return in(t, float_set) ? pt::fixed_lower : pt::none;
|
||||
case 'F':
|
||||
return in(t, float_set) ? pt::fixed_upper : pt::none;
|
||||
case 'g':
|
||||
return in(t, float_set) ? pt::general_lower : pt::none;
|
||||
case 'G':
|
||||
return in(t, float_set) ? pt::general_upper : pt::none;
|
||||
case 'c':
|
||||
return in(t, integral_set) ? pt::chr : pt::none;
|
||||
case 's':
|
||||
return in(t, string_set | cstring_set) ? pt::string : pt::none;
|
||||
case 'p':
|
||||
return in(t, pointer_set | cstring_set) ? pt::pointer : pt::none;
|
||||
default:
|
||||
return pt::none;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Char, typename Context>
|
||||
void vprintf(buffer<Char>& buf, basic_string_view<Char> format,
|
||||
basic_format_args<Context> args) {
|
||||
using iterator = buffer_appender<Char>;
|
||||
auto out = iterator(buf);
|
||||
auto context = basic_printf_context<Char>(out, args);
|
||||
auto parse_ctx = basic_format_parse_context<Char>(format);
|
||||
|
||||
// Returns the argument with specified index or, if arg_index is -1, the next
|
||||
// argument.
|
||||
auto get_arg = [&](int arg_index) {
|
||||
if (arg_index < 0)
|
||||
arg_index = parse_ctx.next_arg_id();
|
||||
else
|
||||
parse_ctx.check_arg_id(--arg_index);
|
||||
return detail::get_arg(context, arg_index);
|
||||
};
|
||||
|
||||
const Char* start = parse_ctx.begin();
|
||||
const Char* end = parse_ctx.end();
|
||||
auto it = start;
|
||||
while (it != end) {
|
||||
if (!find<false, Char>(it, end, '%', it)) {
|
||||
it = end; // find leaves it == nullptr if it doesn't find '%'.
|
||||
break;
|
||||
}
|
||||
Char c = *it++;
|
||||
if (it != end && *it == c) {
|
||||
write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
|
||||
start = ++it;
|
||||
continue;
|
||||
}
|
||||
write(out, basic_string_view<Char>(start, to_unsigned(it - 1 - start)));
|
||||
|
||||
auto specs = format_specs<Char>();
|
||||
specs.align = align::right;
|
||||
|
||||
// Parse argument index, flags and width.
|
||||
int arg_index = parse_header(it, end, specs, get_arg);
|
||||
if (arg_index == 0) throw_format_error("argument not found");
|
||||
|
||||
// Parse precision.
|
||||
if (it != end && *it == '.') {
|
||||
++it;
|
||||
c = it != end ? *it : 0;
|
||||
if ('0' <= c && c <= '9') {
|
||||
specs.precision = parse_nonnegative_int(it, end, 0);
|
||||
} else if (c == '*') {
|
||||
++it;
|
||||
specs.precision = static_cast<int>(
|
||||
visit_format_arg(printf_precision_handler(), get_arg(-1)));
|
||||
} else {
|
||||
specs.precision = 0;
|
||||
}
|
||||
}
|
||||
|
||||
auto arg = get_arg(arg_index);
|
||||
// For d, i, o, u, x, and X conversion specifiers, if a precision is
|
||||
// specified, the '0' flag is ignored
|
||||
if (specs.precision >= 0 && arg.is_integral()) {
|
||||
// Ignore '0' for non-numeric types or if '-' present.
|
||||
specs.fill[0] = ' ';
|
||||
}
|
||||
if (specs.precision >= 0 && arg.type() == type::cstring_type) {
|
||||
auto str = visit_format_arg(get_cstring<Char>(), arg);
|
||||
auto str_end = str + specs.precision;
|
||||
auto nul = std::find(str, str_end, Char());
|
||||
auto sv = basic_string_view<Char>(
|
||||
str, to_unsigned(nul != str_end ? nul - str : specs.precision));
|
||||
arg = make_arg<basic_printf_context<Char>>(sv);
|
||||
}
|
||||
if (specs.alt && visit_format_arg(is_zero_int(), arg)) specs.alt = false;
|
||||
if (specs.fill[0] == '0') {
|
||||
if (arg.is_arithmetic() && specs.align != align::left)
|
||||
specs.align = align::numeric;
|
||||
else
|
||||
specs.fill[0] = ' '; // Ignore '0' flag for non-numeric types or if '-'
|
||||
// flag is also present.
|
||||
}
|
||||
|
||||
// Parse length and convert the argument to the required type.
|
||||
c = it != end ? *it++ : 0;
|
||||
Char t = it != end ? *it : 0;
|
||||
switch (c) {
|
||||
case 'h':
|
||||
if (t == 'h') {
|
||||
++it;
|
||||
t = it != end ? *it : 0;
|
||||
convert_arg<signed char>(arg, t);
|
||||
} else {
|
||||
convert_arg<short>(arg, t);
|
||||
}
|
||||
break;
|
||||
case 'l':
|
||||
if (t == 'l') {
|
||||
++it;
|
||||
t = it != end ? *it : 0;
|
||||
convert_arg<long long>(arg, t);
|
||||
} else {
|
||||
convert_arg<long>(arg, t);
|
||||
}
|
||||
break;
|
||||
case 'j':
|
||||
convert_arg<intmax_t>(arg, t);
|
||||
break;
|
||||
case 'z':
|
||||
convert_arg<size_t>(arg, t);
|
||||
break;
|
||||
case 't':
|
||||
convert_arg<std::ptrdiff_t>(arg, t);
|
||||
break;
|
||||
case 'L':
|
||||
// printf produces garbage when 'L' is omitted for long double, no
|
||||
// need to do the same.
|
||||
break;
|
||||
default:
|
||||
--it;
|
||||
convert_arg<void>(arg, c);
|
||||
}
|
||||
|
||||
// Parse type.
|
||||
if (it == end) throw_format_error("invalid format string");
|
||||
char type = static_cast<char>(*it++);
|
||||
if (arg.is_integral()) {
|
||||
// Normalize type.
|
||||
switch (type) {
|
||||
case 'i':
|
||||
case 'u':
|
||||
type = 'd';
|
||||
break;
|
||||
case 'c':
|
||||
visit_format_arg(char_converter<basic_printf_context<Char>>(arg), arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
specs.type = parse_printf_presentation_type(type, arg.type());
|
||||
if (specs.type == presentation_type::none)
|
||||
throw_format_error("invalid format specifier");
|
||||
|
||||
start = it;
|
||||
|
||||
// Format argument.
|
||||
visit_format_arg(printf_arg_formatter<Char>(out, specs, context), arg);
|
||||
}
|
||||
write(out, basic_string_view<Char>(start, to_unsigned(it - start)));
|
||||
}
|
||||
FMT_END_DETAIL_NAMESPACE
|
||||
|
||||
using printf_context = basic_printf_context<char>;
|
||||
using wprintf_context = basic_printf_context<wchar_t>;
|
||||
|
||||
using printf_args = basic_format_args<printf_context>;
|
||||
using wprintf_args = basic_format_args<wprintf_context>;
|
||||
|
||||
/**
|
||||
\rst
|
||||
Constructs an `~fmt::format_arg_store` object that contains references to
|
||||
arguments and can be implicitly converted to `~fmt::printf_args`.
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
inline auto make_printf_args(const T&... args)
|
||||
-> format_arg_store<printf_context, T...> {
|
||||
return {args...};
|
||||
}
|
||||
|
||||
// DEPRECATED!
|
||||
template <typename... T>
|
||||
inline auto make_wprintf_args(const T&... args)
|
||||
-> format_arg_store<wprintf_context, T...> {
|
||||
return {args...};
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
inline auto vsprintf(
|
||||
basic_string_view<Char> fmt,
|
||||
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
|
||||
-> std::basic_string<Char> {
|
||||
auto buf = basic_memory_buffer<Char>();
|
||||
detail::vprintf(buf, fmt, args);
|
||||
return to_string(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Formats arguments and returns the result as a string.
|
||||
|
||||
**Example**::
|
||||
|
||||
std::string message = fmt::sprintf("The answer is %d", 42);
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... T,
|
||||
typename Char = enable_if_t<detail::is_string<S>::value, char_t<S>>>
|
||||
inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string<Char> {
|
||||
return vsprintf(detail::to_string_view(fmt),
|
||||
fmt::make_format_args<basic_printf_context<Char>>(args...));
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
inline auto vfprintf(
|
||||
std::FILE* f, basic_string_view<Char> fmt,
|
||||
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
|
||||
-> int {
|
||||
auto buf = basic_memory_buffer<Char>();
|
||||
detail::vprintf(buf, fmt, args);
|
||||
size_t size = buf.size();
|
||||
return std::fwrite(buf.data(), sizeof(Char), size, f) < size
|
||||
? -1
|
||||
: static_cast<int>(size);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to the file *f*.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::fprintf(stderr, "Don't %s!", "panic");
|
||||
\endrst
|
||||
*/
|
||||
template <typename S, typename... T, typename Char = char_t<S>>
|
||||
inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int {
|
||||
return vfprintf(f, detail::to_string_view(fmt),
|
||||
fmt::make_format_args<basic_printf_context<Char>>(args...));
|
||||
}
|
||||
|
||||
template <typename Char>
|
||||
FMT_DEPRECATED inline auto vprintf(
|
||||
basic_string_view<Char> fmt,
|
||||
basic_format_args<basic_printf_context<type_identity_t<Char>>> args)
|
||||
-> int {
|
||||
return vfprintf(stdout, fmt, args);
|
||||
}
|
||||
|
||||
/**
|
||||
\rst
|
||||
Prints formatted data to ``stdout``.
|
||||
|
||||
**Example**::
|
||||
|
||||
fmt::printf("Elapsed time: %.2f seconds", 1.23);
|
||||
\endrst
|
||||
*/
|
||||
template <typename... T>
|
||||
inline auto printf(string_view fmt, const T&... args) -> int {
|
||||
return vfprintf(stdout, fmt, make_printf_args(args...));
|
||||
}
|
||||
template <typename... T>
|
||||
FMT_DEPRECATED inline auto printf(basic_string_view<wchar_t> fmt,
|
||||
const T&... args) -> int {
|
||||
return vfprintf(stdout, fmt, make_wprintf_args(args...));
|
||||
}
|
||||
|
||||
FMT_END_EXPORT
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_PRINTF_H_
|
732
include/fmt/ranges.h
Normal file
732
include/fmt/ranges.h
Normal file
|
@ -0,0 +1,732 @@
|
|||
// Formatting library for C++ - experimental range support
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
//
|
||||
// Copyright (c) 2018 - present, Remotion (Igor Schulz)
|
||||
// All Rights Reserved
|
||||
// {fmt} support for ranges, containers and types tuple interface.
|
||||
|
||||
#ifndef FMT_RANGES_H_
|
||||
#define FMT_RANGES_H_
|
||||
|
||||
#include <initializer_list>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename Range, typename OutputIt>
|
||||
auto copy(const Range& range, OutputIt out) -> OutputIt {
|
||||
for (auto it = range.begin(), end = range.end(); it != end; ++it)
|
||||
*out++ = *it;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename OutputIt>
|
||||
auto copy(const char* str, OutputIt out) -> OutputIt {
|
||||
while (*str) *out++ = *str++;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename OutputIt> auto copy(char ch, OutputIt out) -> OutputIt {
|
||||
*out++ = ch;
|
||||
return out;
|
||||
}
|
||||
|
||||
template <typename OutputIt> auto copy(wchar_t ch, OutputIt out) -> OutputIt {
|
||||
*out++ = ch;
|
||||
return out;
|
||||
}
|
||||
|
||||
// Returns true if T has a std::string-like interface, like std::string_view.
|
||||
template <typename T> class is_std_string_like {
|
||||
template <typename U>
|
||||
static auto check(U* p)
|
||||
-> decltype((void)p->find('a'), p->length(), (void)p->data(), int());
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
static constexpr const bool value =
|
||||
is_string<T>::value ||
|
||||
std::is_convertible<T, std_string_view<char>>::value ||
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
template <typename Char>
|
||||
struct is_std_string_like<fmt::basic_string_view<Char>> : std::true_type {};
|
||||
|
||||
template <typename T> class is_map {
|
||||
template <typename U> static auto check(U*) -> typename U::mapped_type;
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
#ifdef FMT_FORMAT_MAP_AS_LIST // DEPRECATED!
|
||||
static constexpr const bool value = false;
|
||||
#else
|
||||
static constexpr const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename T> class is_set {
|
||||
template <typename U> static auto check(U*) -> typename U::key_type;
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
#ifdef FMT_FORMAT_SET_AS_LIST // DEPRECATED!
|
||||
static constexpr const bool value = false;
|
||||
#else
|
||||
static constexpr const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value && !is_map<T>::value;
|
||||
#endif
|
||||
};
|
||||
|
||||
template <typename... Ts> struct conditional_helper {};
|
||||
|
||||
template <typename T, typename _ = void> struct is_range_ : std::false_type {};
|
||||
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION > 1800
|
||||
|
||||
# define FMT_DECLTYPE_RETURN(val) \
|
||||
->decltype(val) { return val; } \
|
||||
static_assert( \
|
||||
true, "") // This makes it so that a semicolon is required after the
|
||||
// macro, which helps clang-format handle the formatting.
|
||||
|
||||
// C array overload
|
||||
template <typename T, std::size_t N>
|
||||
auto range_begin(const T (&arr)[N]) -> const T* {
|
||||
return arr;
|
||||
}
|
||||
template <typename T, std::size_t N>
|
||||
auto range_end(const T (&arr)[N]) -> const T* {
|
||||
return arr + N;
|
||||
}
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct has_member_fn_begin_end_t : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_member_fn_begin_end_t<T, void_t<decltype(std::declval<T>().begin()),
|
||||
decltype(std::declval<T>().end())>>
|
||||
: std::true_type {};
|
||||
|
||||
// Member function overload
|
||||
template <typename T>
|
||||
auto range_begin(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).begin());
|
||||
template <typename T>
|
||||
auto range_end(T&& rng) FMT_DECLTYPE_RETURN(static_cast<T&&>(rng).end());
|
||||
|
||||
// ADL overload. Only participates in overload resolution if member functions
|
||||
// are not found.
|
||||
template <typename T>
|
||||
auto range_begin(T&& rng)
|
||||
-> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
|
||||
decltype(begin(static_cast<T&&>(rng)))> {
|
||||
return begin(static_cast<T&&>(rng));
|
||||
}
|
||||
template <typename T>
|
||||
auto range_end(T&& rng) -> enable_if_t<!has_member_fn_begin_end_t<T&&>::value,
|
||||
decltype(end(static_cast<T&&>(rng)))> {
|
||||
return end(static_cast<T&&>(rng));
|
||||
}
|
||||
|
||||
template <typename T, typename Enable = void>
|
||||
struct has_const_begin_end : std::false_type {};
|
||||
template <typename T, typename Enable = void>
|
||||
struct has_mutable_begin_end : std::false_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_const_begin_end<
|
||||
T,
|
||||
void_t<
|
||||
decltype(detail::range_begin(std::declval<const remove_cvref_t<T>&>())),
|
||||
decltype(detail::range_end(std::declval<const remove_cvref_t<T>&>()))>>
|
||||
: std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct has_mutable_begin_end<
|
||||
T, void_t<decltype(detail::range_begin(std::declval<T>())),
|
||||
decltype(detail::range_end(std::declval<T>())),
|
||||
// the extra int here is because older versions of MSVC don't
|
||||
// SFINAE properly unless there are distinct types
|
||||
int>> : std::true_type {};
|
||||
|
||||
template <typename T>
|
||||
struct is_range_<T, void>
|
||||
: std::integral_constant<bool, (has_const_begin_end<T>::value ||
|
||||
has_mutable_begin_end<T>::value)> {};
|
||||
# undef FMT_DECLTYPE_RETURN
|
||||
#endif
|
||||
|
||||
// tuple_size and tuple_element check.
|
||||
template <typename T> class is_tuple_like_ {
|
||||
template <typename U>
|
||||
static auto check(U* p) -> decltype(std::tuple_size<U>::value, int());
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
static constexpr const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
// Check for integer_sequence
|
||||
#if defined(__cpp_lib_integer_sequence) || FMT_MSC_VERSION >= 1900
|
||||
template <typename T, T... N>
|
||||
using integer_sequence = std::integer_sequence<T, 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 {
|
||||
using value_type = T;
|
||||
|
||||
static FMT_CONSTEXPR size_t size() { return sizeof...(N); }
|
||||
};
|
||||
|
||||
template <size_t... N> using index_sequence = integer_sequence<size_t, N...>;
|
||||
|
||||
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 <size_t N>
|
||||
using make_index_sequence = make_integer_sequence<size_t, N>;
|
||||
#endif
|
||||
|
||||
template <typename T>
|
||||
using tuple_index_sequence = make_index_sequence<std::tuple_size<T>::value>;
|
||||
|
||||
template <typename T, typename C, bool = is_tuple_like_<T>::value>
|
||||
class is_tuple_formattable_ {
|
||||
public:
|
||||
static constexpr const bool value = false;
|
||||
};
|
||||
template <typename T, typename C> class is_tuple_formattable_<T, C, true> {
|
||||
template <std::size_t... Is>
|
||||
static std::true_type check2(index_sequence<Is...>,
|
||||
integer_sequence<bool, (Is == Is)...>);
|
||||
static std::false_type check2(...);
|
||||
template <std::size_t... Is>
|
||||
static decltype(check2(
|
||||
index_sequence<Is...>{},
|
||||
integer_sequence<
|
||||
bool, (is_formattable<typename std::tuple_element<Is, T>::type,
|
||||
C>::value)...>{})) check(index_sequence<Is...>);
|
||||
|
||||
public:
|
||||
static constexpr const bool value =
|
||||
decltype(check(tuple_index_sequence<T>{}))::value;
|
||||
};
|
||||
|
||||
template <typename Tuple, typename F, size_t... Is>
|
||||
FMT_CONSTEXPR void for_each(index_sequence<Is...>, Tuple&& t, F&& f) {
|
||||
using std::get;
|
||||
// Using a free function get<Is>(Tuple) now.
|
||||
const int unused[] = {0, ((void)f(get<Is>(t)), 0)...};
|
||||
ignore_unused(unused);
|
||||
}
|
||||
|
||||
template <typename Tuple, typename F>
|
||||
FMT_CONSTEXPR void for_each(Tuple&& t, F&& f) {
|
||||
for_each(tuple_index_sequence<remove_cvref_t<Tuple>>(),
|
||||
std::forward<Tuple>(t), std::forward<F>(f));
|
||||
}
|
||||
|
||||
template <typename Tuple1, typename Tuple2, typename F, size_t... Is>
|
||||
void for_each2(index_sequence<Is...>, Tuple1&& t1, Tuple2&& t2, F&& f) {
|
||||
using std::get;
|
||||
const int unused[] = {0, ((void)f(get<Is>(t1), get<Is>(t2)), 0)...};
|
||||
ignore_unused(unused);
|
||||
}
|
||||
|
||||
template <typename Tuple1, typename Tuple2, typename F>
|
||||
void for_each2(Tuple1&& t1, Tuple2&& t2, F&& f) {
|
||||
for_each2(tuple_index_sequence<remove_cvref_t<Tuple1>>(),
|
||||
std::forward<Tuple1>(t1), std::forward<Tuple2>(t2),
|
||||
std::forward<F>(f));
|
||||
}
|
||||
|
||||
namespace tuple {
|
||||
// Workaround a bug in MSVC 2019 (v140).
|
||||
template <typename Char, typename... T>
|
||||
using result_t = std::tuple<formatter<remove_cvref_t<T>, Char>...>;
|
||||
|
||||
using std::get;
|
||||
template <typename Tuple, typename Char, std::size_t... Is>
|
||||
auto get_formatters(index_sequence<Is...>)
|
||||
-> result_t<Char, decltype(get<Is>(std::declval<Tuple>()))...>;
|
||||
} // namespace tuple
|
||||
|
||||
#if FMT_MSC_VERSION && FMT_MSC_VERSION < 1920
|
||||
// Older MSVC doesn't get the reference type correctly for arrays.
|
||||
template <typename R> struct range_reference_type_impl {
|
||||
using type = decltype(*detail::range_begin(std::declval<R&>()));
|
||||
};
|
||||
|
||||
template <typename T, std::size_t N> struct range_reference_type_impl<T[N]> {
|
||||
using type = T&;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using range_reference_type = typename range_reference_type_impl<T>::type;
|
||||
#else
|
||||
template <typename Range>
|
||||
using range_reference_type =
|
||||
decltype(*detail::range_begin(std::declval<Range&>()));
|
||||
#endif
|
||||
|
||||
// We don't use the Range's value_type for anything, but we do need the Range's
|
||||
// reference type, with cv-ref stripped.
|
||||
template <typename Range>
|
||||
using uncvref_type = remove_cvref_t<range_reference_type<Range>>;
|
||||
|
||||
template <typename Formatter>
|
||||
FMT_CONSTEXPR auto maybe_set_debug_format(Formatter& f, bool set)
|
||||
-> decltype(f.set_debug_format(set)) {
|
||||
f.set_debug_format(set);
|
||||
}
|
||||
template <typename Formatter>
|
||||
FMT_CONSTEXPR void maybe_set_debug_format(Formatter&, ...) {}
|
||||
|
||||
// These are not generic lambdas for compatibility with C++11.
|
||||
template <typename ParseContext> struct parse_empty_specs {
|
||||
template <typename Formatter> FMT_CONSTEXPR void operator()(Formatter& f) {
|
||||
f.parse(ctx);
|
||||
detail::maybe_set_debug_format(f, true);
|
||||
}
|
||||
ParseContext& ctx;
|
||||
};
|
||||
template <typename FormatContext> struct format_tuple_element {
|
||||
using char_type = typename FormatContext::char_type;
|
||||
|
||||
template <typename T>
|
||||
void operator()(const formatter<T, char_type>& f, const T& v) {
|
||||
if (i > 0)
|
||||
ctx.advance_to(detail::copy_str<char_type>(separator, ctx.out()));
|
||||
ctx.advance_to(f.format(v, ctx));
|
||||
++i;
|
||||
}
|
||||
|
||||
int i;
|
||||
FormatContext& ctx;
|
||||
basic_string_view<char_type> separator;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <typename T> struct is_tuple_like {
|
||||
static constexpr const bool value =
|
||||
detail::is_tuple_like_<T>::value && !detail::is_range_<T>::value;
|
||||
};
|
||||
|
||||
template <typename T, typename C> struct is_tuple_formattable {
|
||||
static constexpr const bool value =
|
||||
detail::is_tuple_formattable_<T, C>::value;
|
||||
};
|
||||
|
||||
template <typename Tuple, typename Char>
|
||||
struct formatter<Tuple, Char,
|
||||
enable_if_t<fmt::is_tuple_like<Tuple>::value &&
|
||||
fmt::is_tuple_formattable<Tuple, Char>::value>> {
|
||||
private:
|
||||
decltype(detail::tuple::get_formatters<Tuple, Char>(
|
||||
detail::tuple_index_sequence<Tuple>())) formatters_;
|
||||
|
||||
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
|
||||
basic_string_view<Char> opening_bracket_ =
|
||||
detail::string_literal<Char, '('>{};
|
||||
basic_string_view<Char> closing_bracket_ =
|
||||
detail::string_literal<Char, ')'>{};
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR formatter() {}
|
||||
|
||||
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
|
||||
separator_ = sep;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
|
||||
basic_string_view<Char> close) {
|
||||
opening_bracket_ = open;
|
||||
closing_bracket_ = close;
|
||||
}
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
auto it = ctx.begin();
|
||||
if (it != ctx.end() && *it != '}')
|
||||
FMT_THROW(format_error("invalid format specifier"));
|
||||
detail::for_each(formatters_, detail::parse_empty_specs<ParseContext>{ctx});
|
||||
return it;
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const Tuple& value, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
ctx.advance_to(detail::copy_str<Char>(opening_bracket_, ctx.out()));
|
||||
detail::for_each2(
|
||||
formatters_, value,
|
||||
detail::format_tuple_element<FormatContext>{0, ctx, separator_});
|
||||
return detail::copy_str<Char>(closing_bracket_, ctx.out());
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T, typename Char> struct is_range {
|
||||
static constexpr const bool value =
|
||||
detail::is_range_<T>::value && !detail::is_std_string_like<T>::value &&
|
||||
!std::is_convertible<T, std::basic_string<Char>>::value &&
|
||||
!std::is_convertible<T, detail::std_string_view<Char>>::value;
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
template <typename Context> struct range_mapper {
|
||||
using mapper = arg_mapper<Context>;
|
||||
|
||||
template <typename T,
|
||||
FMT_ENABLE_IF(has_formatter<remove_cvref_t<T>, Context>::value)>
|
||||
static auto map(T&& value) -> T&& {
|
||||
return static_cast<T&&>(value);
|
||||
}
|
||||
template <typename T,
|
||||
FMT_ENABLE_IF(!has_formatter<remove_cvref_t<T>, Context>::value)>
|
||||
static auto map(T&& value)
|
||||
-> decltype(mapper().map(static_cast<T&&>(value))) {
|
||||
return mapper().map(static_cast<T&&>(value));
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Char, typename Element>
|
||||
using range_formatter_type =
|
||||
formatter<remove_cvref_t<decltype(range_mapper<buffer_context<Char>>{}.map(
|
||||
std::declval<Element>()))>,
|
||||
Char>;
|
||||
|
||||
template <typename R>
|
||||
using maybe_const_range =
|
||||
conditional_t<has_const_begin_end<R>::value, const R, R>;
|
||||
|
||||
// Workaround a bug in MSVC 2015 and earlier.
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
|
||||
template <typename R, typename Char>
|
||||
struct is_formattable_delayed
|
||||
: is_formattable<uncvref_type<maybe_const_range<R>>, Char> {};
|
||||
#endif
|
||||
} // namespace detail
|
||||
|
||||
template <typename T, typename Char, typename Enable = void>
|
||||
struct range_formatter;
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct range_formatter<
|
||||
T, Char,
|
||||
enable_if_t<conjunction<std::is_same<T, remove_cvref_t<T>>,
|
||||
is_formattable<T, Char>>::value>> {
|
||||
private:
|
||||
detail::range_formatter_type<Char, T> underlying_;
|
||||
basic_string_view<Char> separator_ = detail::string_literal<Char, ',', ' '>{};
|
||||
basic_string_view<Char> opening_bracket_ =
|
||||
detail::string_literal<Char, '['>{};
|
||||
basic_string_view<Char> closing_bracket_ =
|
||||
detail::string_literal<Char, ']'>{};
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR range_formatter() {}
|
||||
|
||||
FMT_CONSTEXPR auto underlying() -> detail::range_formatter_type<Char, T>& {
|
||||
return underlying_;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void set_separator(basic_string_view<Char> sep) {
|
||||
separator_ = sep;
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void set_brackets(basic_string_view<Char> open,
|
||||
basic_string_view<Char> close) {
|
||||
opening_bracket_ = open;
|
||||
closing_bracket_ = close;
|
||||
}
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
auto it = ctx.begin();
|
||||
auto end = ctx.end();
|
||||
|
||||
if (it != end && *it == 'n') {
|
||||
set_brackets({}, {});
|
||||
++it;
|
||||
}
|
||||
|
||||
if (it != end && *it != '}') {
|
||||
if (*it != ':') FMT_THROW(format_error("invalid format specifier"));
|
||||
++it;
|
||||
} else {
|
||||
detail::maybe_set_debug_format(underlying_, true);
|
||||
}
|
||||
|
||||
ctx.advance_to(it);
|
||||
return underlying_.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename R, typename FormatContext>
|
||||
auto format(R&& range, FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||
detail::range_mapper<buffer_context<Char>> mapper;
|
||||
auto out = ctx.out();
|
||||
out = detail::copy_str<Char>(opening_bracket_, out);
|
||||
int i = 0;
|
||||
auto it = detail::range_begin(range);
|
||||
auto end = detail::range_end(range);
|
||||
for (; it != end; ++it) {
|
||||
if (i > 0) out = detail::copy_str<Char>(separator_, out);
|
||||
ctx.advance_to(out);
|
||||
out = underlying_.format(mapper.map(*it), ctx);
|
||||
++i;
|
||||
}
|
||||
out = detail::copy_str<Char>(closing_bracket_, out);
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
enum class range_format { disabled, map, set, sequence, string, debug_string };
|
||||
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
struct range_format_kind_
|
||||
: std::integral_constant<range_format,
|
||||
std::is_same<uncvref_type<T>, T>::value
|
||||
? range_format::disabled
|
||||
: is_map<T>::value ? range_format::map
|
||||
: is_set<T>::value ? range_format::set
|
||||
: range_format::sequence> {};
|
||||
|
||||
template <range_format K, typename R, typename Char, typename Enable = void>
|
||||
struct range_default_formatter;
|
||||
|
||||
template <range_format K>
|
||||
using range_format_constant = std::integral_constant<range_format, K>;
|
||||
|
||||
template <range_format K, typename R, typename Char>
|
||||
struct range_default_formatter<
|
||||
K, R, Char,
|
||||
enable_if_t<(K == range_format::sequence || K == range_format::map ||
|
||||
K == range_format::set)>> {
|
||||
using range_type = detail::maybe_const_range<R>;
|
||||
range_formatter<detail::uncvref_type<range_type>, Char> underlying_;
|
||||
|
||||
FMT_CONSTEXPR range_default_formatter() { init(range_format_constant<K>()); }
|
||||
|
||||
FMT_CONSTEXPR void init(range_format_constant<range_format::set>) {
|
||||
underlying_.set_brackets(detail::string_literal<Char, '{'>{},
|
||||
detail::string_literal<Char, '}'>{});
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void init(range_format_constant<range_format::map>) {
|
||||
underlying_.set_brackets(detail::string_literal<Char, '{'>{},
|
||||
detail::string_literal<Char, '}'>{});
|
||||
underlying_.underlying().set_brackets({}, {});
|
||||
underlying_.underlying().set_separator(
|
||||
detail::string_literal<Char, ':', ' '>{});
|
||||
}
|
||||
|
||||
FMT_CONSTEXPR void init(range_format_constant<range_format::sequence>) {}
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return underlying_.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(range_type& range, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
return underlying_.format(range, ctx);
|
||||
}
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename T, typename Char, typename Enable = void>
|
||||
struct range_format_kind
|
||||
: conditional_t<
|
||||
is_range<T, Char>::value, detail::range_format_kind_<T>,
|
||||
std::integral_constant<range_format, range_format::disabled>> {};
|
||||
|
||||
template <typename R, typename Char>
|
||||
struct formatter<
|
||||
R, Char,
|
||||
enable_if_t<conjunction<bool_constant<range_format_kind<R, Char>::value !=
|
||||
range_format::disabled>
|
||||
// Workaround a bug in MSVC 2015 and earlier.
|
||||
#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910
|
||||
,
|
||||
detail::is_formattable_delayed<R, Char>
|
||||
#endif
|
||||
>::value>>
|
||||
: detail::range_default_formatter<range_format_kind<R, Char>::value, R,
|
||||
Char> {
|
||||
};
|
||||
|
||||
template <typename Char, typename... T> struct tuple_join_view : detail::view {
|
||||
const std::tuple<T...>& tuple;
|
||||
basic_string_view<Char> sep;
|
||||
|
||||
tuple_join_view(const std::tuple<T...>& t, basic_string_view<Char> s)
|
||||
: tuple(t), sep{s} {}
|
||||
};
|
||||
|
||||
// Define FMT_TUPLE_JOIN_SPECIFIERS to enable experimental format specifiers
|
||||
// support in tuple_join. It is disabled by default because of issues with
|
||||
// the dynamic width and precision.
|
||||
#ifndef FMT_TUPLE_JOIN_SPECIFIERS
|
||||
# define FMT_TUPLE_JOIN_SPECIFIERS 0
|
||||
#endif
|
||||
|
||||
template <typename Char, typename... T>
|
||||
struct formatter<tuple_join_view<Char, T...>, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return do_parse(ctx, std::integral_constant<size_t, sizeof...(T)>());
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const tuple_join_view<Char, T...>& value,
|
||||
FormatContext& ctx) const -> typename FormatContext::iterator {
|
||||
return do_format(value, ctx,
|
||||
std::integral_constant<size_t, sizeof...(T)>());
|
||||
}
|
||||
|
||||
private:
|
||||
std::tuple<formatter<typename std::decay<T>::type, Char>...> formatters_;
|
||||
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
|
||||
std::integral_constant<size_t, 0>)
|
||||
-> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename ParseContext, size_t N>
|
||||
FMT_CONSTEXPR auto do_parse(ParseContext& ctx,
|
||||
std::integral_constant<size_t, N>)
|
||||
-> decltype(ctx.begin()) {
|
||||
auto end = ctx.begin();
|
||||
#if FMT_TUPLE_JOIN_SPECIFIERS
|
||||
end = std::get<sizeof...(T) - N>(formatters_).parse(ctx);
|
||||
if (N > 1) {
|
||||
auto end1 = do_parse(ctx, std::integral_constant<size_t, N - 1>());
|
||||
if (end != end1)
|
||||
FMT_THROW(format_error("incompatible format specs for tuple elements"));
|
||||
}
|
||||
#endif
|
||||
return end;
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto do_format(const tuple_join_view<Char, T...>&, FormatContext& ctx,
|
||||
std::integral_constant<size_t, 0>) const ->
|
||||
typename FormatContext::iterator {
|
||||
return ctx.out();
|
||||
}
|
||||
|
||||
template <typename FormatContext, size_t N>
|
||||
auto do_format(const tuple_join_view<Char, T...>& value, FormatContext& ctx,
|
||||
std::integral_constant<size_t, N>) const ->
|
||||
typename FormatContext::iterator {
|
||||
auto out = std::get<sizeof...(T) - N>(formatters_)
|
||||
.format(std::get<sizeof...(T) - N>(value.tuple), ctx);
|
||||
if (N > 1) {
|
||||
out = std::copy(value.sep.begin(), value.sep.end(), out);
|
||||
ctx.advance_to(out);
|
||||
return do_format(value, ctx, std::integral_constant<size_t, N - 1>());
|
||||
}
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
// Check if T has an interface like a container adaptor (e.g. std::stack,
|
||||
// std::queue, std::priority_queue).
|
||||
template <typename T> class is_container_adaptor_like {
|
||||
template <typename U> static auto check(U* p) -> typename U::container_type;
|
||||
template <typename> static void check(...);
|
||||
|
||||
public:
|
||||
static constexpr const bool value =
|
||||
!std::is_void<decltype(check<T>(nullptr))>::value;
|
||||
};
|
||||
|
||||
template <typename Container> struct all {
|
||||
const Container& c;
|
||||
auto begin() const -> typename Container::const_iterator { return c.begin(); }
|
||||
auto end() const -> typename Container::const_iterator { return c.end(); }
|
||||
};
|
||||
} // namespace detail
|
||||
|
||||
template <typename T, typename Char>
|
||||
struct formatter<T, Char,
|
||||
enable_if_t<detail::is_container_adaptor_like<T>::value>>
|
||||
: formatter<detail::all<typename T::container_type>, Char> {
|
||||
using all = detail::all<typename T::container_type>;
|
||||
template <typename FormatContext>
|
||||
auto format(const T& t, FormatContext& ctx) const -> decltype(ctx.out()) {
|
||||
struct getter : T {
|
||||
static auto get(const T& t) -> all {
|
||||
return {t.*(&getter::c)}; // Access c through the derived class.
|
||||
}
|
||||
};
|
||||
return formatter<all>::format(getter::get(t), ctx);
|
||||
}
|
||||
};
|
||||
|
||||
FMT_BEGIN_EXPORT
|
||||
|
||||
/**
|
||||
\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 auto join(const std::tuple<T...>& tuple, string_view sep)
|
||||
-> tuple_join_view<char, T...> {
|
||||
return {tuple, sep};
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
FMT_CONSTEXPR auto join(const std::tuple<T...>& tuple,
|
||||
basic_string_view<wchar_t> sep)
|
||||
-> tuple_join_view<wchar_t, T...> {
|
||||
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>
|
||||
auto join(std::initializer_list<T> list, string_view sep)
|
||||
-> join_view<const T*, const T*> {
|
||||
return join(std::begin(list), std::end(list), sep);
|
||||
}
|
||||
|
||||
FMT_END_EXPORT
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_RANGES_H_
|
349
include/fmt/std.h
Normal file
349
include/fmt/std.h
Normal file
|
@ -0,0 +1,349 @@
|
|||
// Formatting library for C++ - formatters for standard library types
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_STD_H_
|
||||
#define FMT_STD_H_
|
||||
|
||||
#include <cstdlib>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <typeinfo>
|
||||
#include <utility>
|
||||
|
||||
#include "ostream.h"
|
||||
|
||||
#if FMT_HAS_INCLUDE(<version>)
|
||||
# include <version>
|
||||
#endif
|
||||
// Checking FMT_CPLUSPLUS for warning suppression in MSVC.
|
||||
#if FMT_CPLUSPLUS >= 201703L
|
||||
# if FMT_HAS_INCLUDE(<filesystem>)
|
||||
# include <filesystem>
|
||||
# endif
|
||||
# if FMT_HAS_INCLUDE(<variant>)
|
||||
# include <variant>
|
||||
# endif
|
||||
# if FMT_HAS_INCLUDE(<optional>)
|
||||
# include <optional>
|
||||
# endif
|
||||
#endif
|
||||
|
||||
// GCC 4 does not support FMT_HAS_INCLUDE.
|
||||
#if FMT_HAS_INCLUDE(<cxxabi.h>) || defined(__GLIBCXX__)
|
||||
# include <cxxabi.h>
|
||||
// Android NDK with gabi++ library on some architectures does not implement
|
||||
// abi::__cxa_demangle().
|
||||
# ifndef __GABIXX_CXXABI_H__
|
||||
# define FMT_HAS_ABI_CXA_DEMANGLE
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifdef __cpp_lib_filesystem
|
||||
FMT_BEGIN_NAMESPACE
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename Char>
|
||||
void write_escaped_path(basic_memory_buffer<Char>& quoted,
|
||||
const std::filesystem::path& p) {
|
||||
write_escaped_string<Char>(std::back_inserter(quoted), p.string<Char>());
|
||||
}
|
||||
# ifdef _WIN32
|
||||
template <>
|
||||
inline void write_escaped_path<char>(memory_buffer& quoted,
|
||||
const std::filesystem::path& p) {
|
||||
auto buf = basic_memory_buffer<wchar_t>();
|
||||
write_escaped_string<wchar_t>(std::back_inserter(buf), p.native());
|
||||
// Convert UTF-16 to UTF-8.
|
||||
if (!to_utf8<wchar_t>::convert(quoted, {buf.data(), buf.size()}))
|
||||
FMT_THROW(std::runtime_error("invalid utf16"));
|
||||
}
|
||||
# endif
|
||||
template <>
|
||||
inline void write_escaped_path<std::filesystem::path::value_type>(
|
||||
basic_memory_buffer<std::filesystem::path::value_type>& quoted,
|
||||
const std::filesystem::path& p) {
|
||||
write_escaped_string<std::filesystem::path::value_type>(
|
||||
std::back_inserter(quoted), p.native());
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
FMT_EXPORT
|
||||
template <typename Char>
|
||||
struct formatter<std::filesystem::path, Char>
|
||||
: formatter<basic_string_view<Char>> {
|
||||
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
|
||||
auto out = formatter<basic_string_view<Char>>::parse(ctx);
|
||||
this->set_debug_format(false);
|
||||
return out;
|
||||
}
|
||||
template <typename FormatContext>
|
||||
auto format(const std::filesystem::path& p, FormatContext& ctx) const ->
|
||||
typename FormatContext::iterator {
|
||||
auto quoted = basic_memory_buffer<Char>();
|
||||
detail::write_escaped_path(quoted, p);
|
||||
return formatter<basic_string_view<Char>>::format(
|
||||
basic_string_view<Char>(quoted.data(), quoted.size()), ctx);
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
#endif
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_EXPORT
|
||||
template <typename Char>
|
||||
struct formatter<std::thread::id, Char> : basic_ostream_formatter<Char> {};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#ifdef __cpp_lib_optional
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_EXPORT
|
||||
template <typename T, typename Char>
|
||||
struct formatter<std::optional<T>, Char,
|
||||
std::enable_if_t<is_formattable<T, Char>::value>> {
|
||||
private:
|
||||
formatter<T, Char> underlying_;
|
||||
static constexpr basic_string_view<Char> optional =
|
||||
detail::string_literal<Char, 'o', 'p', 't', 'i', 'o', 'n', 'a', 'l',
|
||||
'('>{};
|
||||
static constexpr basic_string_view<Char> none =
|
||||
detail::string_literal<Char, 'n', 'o', 'n', 'e'>{};
|
||||
|
||||
template <class U>
|
||||
FMT_CONSTEXPR static auto maybe_set_debug_format(U& u, bool set)
|
||||
-> decltype(u.set_debug_format(set)) {
|
||||
u.set_debug_format(set);
|
||||
}
|
||||
|
||||
template <class U>
|
||||
FMT_CONSTEXPR static void maybe_set_debug_format(U&, ...) {}
|
||||
|
||||
public:
|
||||
template <typename ParseContext> FMT_CONSTEXPR auto parse(ParseContext& ctx) {
|
||||
maybe_set_debug_format(underlying_, true);
|
||||
return underlying_.parse(ctx);
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(std::optional<T> const& opt, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
if (!opt) return detail::write<Char>(ctx.out(), none);
|
||||
|
||||
auto out = ctx.out();
|
||||
out = detail::write<Char>(out, optional);
|
||||
ctx.advance_to(out);
|
||||
out = underlying_.format(*opt, ctx);
|
||||
return detail::write(out, ')');
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
#endif // __cpp_lib_optional
|
||||
|
||||
#ifdef __cpp_lib_variant
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_EXPORT
|
||||
template <typename Char> struct formatter<std::monostate, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const std::monostate&, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
out = detail::write<Char>(out, "monostate");
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
using variant_index_sequence =
|
||||
std::make_index_sequence<std::variant_size<T>::value>;
|
||||
|
||||
template <typename> struct is_variant_like_ : std::false_type {};
|
||||
template <typename... Types>
|
||||
struct is_variant_like_<std::variant<Types...>> : std::true_type {};
|
||||
|
||||
// formattable element check.
|
||||
template <typename T, typename C> class is_variant_formattable_ {
|
||||
template <std::size_t... Is>
|
||||
static std::conjunction<
|
||||
is_formattable<std::variant_alternative_t<Is, T>, C>...>
|
||||
check(std::index_sequence<Is...>);
|
||||
|
||||
public:
|
||||
static constexpr const bool value =
|
||||
decltype(check(variant_index_sequence<T>{}))::value;
|
||||
};
|
||||
|
||||
template <typename Char, typename OutputIt, typename T>
|
||||
auto write_variant_alternative(OutputIt out, const T& v) -> OutputIt {
|
||||
if constexpr (is_string<T>::value)
|
||||
return write_escaped_string<Char>(out, detail::to_string_view(v));
|
||||
else if constexpr (std::is_same_v<T, Char>)
|
||||
return write_escaped_char(out, v);
|
||||
else
|
||||
return write<Char>(out, v);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
template <typename T> struct is_variant_like {
|
||||
static constexpr const bool value = detail::is_variant_like_<T>::value;
|
||||
};
|
||||
|
||||
template <typename T, typename C> struct is_variant_formattable {
|
||||
static constexpr const bool value =
|
||||
detail::is_variant_formattable_<T, C>::value;
|
||||
};
|
||||
|
||||
FMT_EXPORT
|
||||
template <typename Variant, typename Char>
|
||||
struct formatter<
|
||||
Variant, Char,
|
||||
std::enable_if_t<std::conjunction_v<
|
||||
is_variant_like<Variant>, is_variant_formattable<Variant, Char>>>> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
auto format(const Variant& value, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
|
||||
out = detail::write<Char>(out, "variant(");
|
||||
try {
|
||||
std::visit(
|
||||
[&](const auto& v) {
|
||||
out = detail::write_variant_alternative<Char>(out, v);
|
||||
},
|
||||
value);
|
||||
} catch (const std::bad_variant_access&) {
|
||||
detail::write<Char>(out, "valueless by exception");
|
||||
}
|
||||
*out++ = ')';
|
||||
return out;
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
#endif // __cpp_lib_variant
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
FMT_EXPORT
|
||||
template <typename Char> struct formatter<std::error_code, Char> {
|
||||
template <typename ParseContext>
|
||||
FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) {
|
||||
return ctx.begin();
|
||||
}
|
||||
|
||||
template <typename FormatContext>
|
||||
FMT_CONSTEXPR auto format(const std::error_code& ec, FormatContext& ctx) const
|
||||
-> decltype(ctx.out()) {
|
||||
auto out = ctx.out();
|
||||
out = detail::write_bytes(out, ec.category().name(), format_specs<Char>());
|
||||
out = detail::write<Char>(out, Char(':'));
|
||||
out = detail::write<Char>(out, ec.value());
|
||||
return out;
|
||||
}
|
||||
};
|
||||
|
||||
FMT_EXPORT
|
||||
template <typename T, typename Char>
|
||||
struct formatter<
|
||||
T, Char,
|
||||
typename std::enable_if<std::is_base_of<std::exception, T>::value>::type> {
|
||||
private:
|
||||
bool with_typename_ = false;
|
||||
|
||||
public:
|
||||
FMT_CONSTEXPR auto parse(basic_format_parse_context<Char>& ctx)
|
||||
-> decltype(ctx.begin()) {
|
||||
auto it = ctx.begin();
|
||||
auto end = ctx.end();
|
||||
if (it == end || *it == '}') return it;
|
||||
if (*it == 't') {
|
||||
++it;
|
||||
with_typename_ = true;
|
||||
}
|
||||
return it;
|
||||
}
|
||||
|
||||
template <typename OutputIt>
|
||||
auto format(const std::exception& ex,
|
||||
basic_format_context<OutputIt, Char>& ctx) const -> OutputIt {
|
||||
format_specs<Char> spec;
|
||||
auto out = ctx.out();
|
||||
if (!with_typename_)
|
||||
return detail::write_bytes(out, string_view(ex.what()), spec);
|
||||
|
||||
const std::type_info& ti = typeid(ex);
|
||||
#ifdef FMT_HAS_ABI_CXA_DEMANGLE
|
||||
int status = 0;
|
||||
std::size_t size = 0;
|
||||
std::unique_ptr<char, decltype(&std::free)> demangled_name_ptr(
|
||||
abi::__cxa_demangle(ti.name(), nullptr, &size, &status), &std::free);
|
||||
|
||||
string_view demangled_name_view;
|
||||
if (demangled_name_ptr) {
|
||||
demangled_name_view = demangled_name_ptr.get();
|
||||
|
||||
// Normalization of stdlib inline namespace names.
|
||||
// libc++ inline namespaces.
|
||||
// std::__1::* -> std::*
|
||||
// std::__1::__fs::* -> std::*
|
||||
// libstdc++ inline namespaces.
|
||||
// std::__cxx11::* -> std::*
|
||||
// std::filesystem::__cxx11::* -> std::filesystem::*
|
||||
if (demangled_name_view.starts_with("std::")) {
|
||||
char* begin = demangled_name_ptr.get();
|
||||
char* to = begin + 5; // std::
|
||||
for (char *from = to, *end = begin + demangled_name_view.size();
|
||||
from < end;) {
|
||||
// This is safe, because demangled_name is NUL-terminated.
|
||||
if (from[0] == '_' && from[1] == '_') {
|
||||
char* next = from + 1;
|
||||
while (next < end && *next != ':') next++;
|
||||
if (next[0] == ':' && next[1] == ':') {
|
||||
from = next + 2;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
*to++ = *from++;
|
||||
}
|
||||
demangled_name_view = {begin, detail::to_unsigned(to - begin)};
|
||||
}
|
||||
} else {
|
||||
demangled_name_view = string_view(ti.name());
|
||||
}
|
||||
out = detail::write_bytes(out, demangled_name_view, spec);
|
||||
#elif FMT_MSC_VERSION
|
||||
string_view demangled_name_view(ti.name());
|
||||
if (demangled_name_view.starts_with("class "))
|
||||
demangled_name_view.remove_prefix(6);
|
||||
else if (demangled_name_view.starts_with("struct "))
|
||||
demangled_name_view.remove_prefix(7);
|
||||
out = detail::write_bytes(out, demangled_name_view, spec);
|
||||
#else
|
||||
out = detail::write_bytes(out, string_view(ti.name()), spec);
|
||||
#endif
|
||||
out = detail::write<Char>(out, Char(':'));
|
||||
out = detail::write<Char>(out, Char(' '));
|
||||
out = detail::write_bytes(out, string_view(ex.what()), spec);
|
||||
|
||||
return out;
|
||||
}
|
||||
};
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_STD_H_
|
258
include/fmt/xchar.h
Normal file
258
include/fmt/xchar.h
Normal file
|
@ -0,0 +1,258 @@
|
|||
// Formatting library for C++ - optional wchar_t and exotic character support
|
||||
//
|
||||
// Copyright (c) 2012 - present, Victor Zverovich
|
||||
// All rights reserved.
|
||||
//
|
||||
// For the license information refer to format.h.
|
||||
|
||||
#ifndef FMT_XCHAR_H_
|
||||
#define FMT_XCHAR_H_
|
||||
|
||||
#include <cwchar>
|
||||
|
||||
#include "format.h"
|
||||
|
||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
# include <locale>
|
||||
#endif
|
||||
|
||||
FMT_BEGIN_NAMESPACE
|
||||
namespace detail {
|
||||
|
||||
template <typename T>
|
||||
using is_exotic_char = bool_constant<!std::is_same<T, char>::value>;
|
||||
|
||||
inline auto write_loc(std::back_insert_iterator<detail::buffer<wchar_t>> out,
|
||||
loc_value value, const format_specs<wchar_t>& specs,
|
||||
locale_ref loc) -> bool {
|
||||
#ifndef FMT_STATIC_THOUSANDS_SEPARATOR
|
||||
auto& numpunct =
|
||||
std::use_facet<std::numpunct<wchar_t>>(loc.get<std::locale>());
|
||||
auto separator = std::wstring();
|
||||
auto grouping = numpunct.grouping();
|
||||
if (!grouping.empty()) separator = std::wstring(1, numpunct.thousands_sep());
|
||||
return value.visit(loc_writer<wchar_t>{out, specs, separator, grouping, {}});
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
FMT_BEGIN_EXPORT
|
||||
|
||||
using wstring_view = basic_string_view<wchar_t>;
|
||||
using wformat_parse_context = basic_format_parse_context<wchar_t>;
|
||||
using wformat_context = buffer_context<wchar_t>;
|
||||
using wformat_args = basic_format_args<wformat_context>;
|
||||
using wmemory_buffer = basic_memory_buffer<wchar_t>;
|
||||
|
||||
#if FMT_GCC_VERSION && FMT_GCC_VERSION < 409
|
||||
// Workaround broken conversion on older gcc.
|
||||
template <typename... Args> using wformat_string = wstring_view;
|
||||
inline auto runtime(wstring_view s) -> wstring_view { return s; }
|
||||
#else
|
||||
template <typename... Args>
|
||||
using wformat_string = basic_format_string<wchar_t, type_identity_t<Args>...>;
|
||||
inline auto runtime(wstring_view s) -> runtime_format_string<wchar_t> {
|
||||
return {{s}};
|
||||
}
|
||||
#endif
|
||||
|
||||
template <> struct is_char<wchar_t> : std::true_type {};
|
||||
template <> struct is_char<detail::char8_type> : std::true_type {};
|
||||
template <> struct is_char<char16_t> : std::true_type {};
|
||||
template <> struct is_char<char32_t> : std::true_type {};
|
||||
|
||||
template <typename... T>
|
||||
constexpr format_arg_store<wformat_context, T...> make_wformat_args(
|
||||
const T&... args) {
|
||||
return {args...};
|
||||
}
|
||||
|
||||
inline namespace literals {
|
||||
#if FMT_USE_USER_DEFINED_LITERALS && !FMT_USE_NONTYPE_TEMPLATE_ARGS
|
||||
constexpr detail::udl_arg<wchar_t> operator"" _a(const wchar_t* s, size_t) {
|
||||
return {s};
|
||||
}
|
||||
#endif
|
||||
} // namespace literals
|
||||
|
||||
template <typename It, typename Sentinel>
|
||||
auto join(It begin, Sentinel end, wstring_view sep)
|
||||
-> join_view<It, Sentinel, wchar_t> {
|
||||
return {begin, end, sep};
|
||||
}
|
||||
|
||||
template <typename Range>
|
||||
auto join(Range&& range, wstring_view sep)
|
||||
-> join_view<detail::iterator_t<Range>, detail::sentinel_t<Range>,
|
||||
wchar_t> {
|
||||
return join(std::begin(range), std::end(range), sep);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto join(std::initializer_list<T> list, wstring_view sep)
|
||||
-> join_view<const T*, const T*, wchar_t> {
|
||||
return join(std::begin(list), std::end(list), sep);
|
||||
}
|
||||
|
||||
template <typename Char, FMT_ENABLE_IF(!std::is_same<Char, char>::value)>
|
||||
auto vformat(basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||
-> std::basic_string<Char> {
|
||||
auto buf = basic_memory_buffer<Char>();
|
||||
detail::vformat_to(buf, format_str, args);
|
||||
return to_string(buf);
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
auto format(wformat_string<T...> fmt, T&&... args) -> std::wstring {
|
||||
return vformat(fmt::wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||
}
|
||||
|
||||
// Pass char_t as a default template parameter instead of using
|
||||
// std::basic_string<char_t<S>> to reduce the symbol size.
|
||||
template <typename S, typename... T, typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(!std::is_same<Char, char>::value &&
|
||||
!std::is_same<Char, wchar_t>::value)>
|
||||
auto format(const S& format_str, T&&... args) -> std::basic_string<Char> {
|
||||
return vformat(detail::to_string_view(format_str),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
|
||||
template <typename Locale, typename S, typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto vformat(
|
||||
const Locale& loc, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||
-> std::basic_string<Char> {
|
||||
return detail::vformat(loc, detail::to_string_view(format_str), args);
|
||||
}
|
||||
|
||||
template <typename Locale, typename S, typename... T, typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_locale<Locale>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto format(const Locale& loc, const S& format_str, T&&... args)
|
||||
-> std::basic_string<Char> {
|
||||
return detail::vformat(loc, detail::to_string_view(format_str),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
auto vformat_to(OutputIt out, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||
-> OutputIt {
|
||||
auto&& buf = detail::get_buffer<Char>(out);
|
||||
detail::vformat_to(buf, detail::to_string_view(format_str), args);
|
||||
return detail::get_iterator(buf, out);
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... T,
|
||||
typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto format_to(OutputIt out, const S& fmt, T&&... args) -> OutputIt {
|
||||
return vformat_to(out, detail::to_string_view(fmt),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
|
||||
template <typename Locale, typename S, typename OutputIt, typename... Args,
|
||||
typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_locale<Locale>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto vformat_to(
|
||||
OutputIt out, const Locale& loc, const S& format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args) -> OutputIt {
|
||||
auto&& buf = detail::get_buffer<Char>(out);
|
||||
vformat_to(buf, detail::to_string_view(format_str), args,
|
||||
detail::locale_ref(loc));
|
||||
return detail::get_iterator(buf, out);
|
||||
}
|
||||
|
||||
template <
|
||||
typename OutputIt, typename Locale, typename S, typename... T,
|
||||
typename Char = char_t<S>,
|
||||
bool enable = detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_locale<Locale>::value&& detail::is_exotic_char<Char>::value>
|
||||
inline auto format_to(OutputIt out, const Locale& loc, const S& format_str,
|
||||
T&&... args) ->
|
||||
typename std::enable_if<enable, OutputIt>::type {
|
||||
return vformat_to(out, loc, detail::to_string_view(format_str),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename Char, typename... Args,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto vformat_to_n(
|
||||
OutputIt out, size_t n, basic_string_view<Char> format_str,
|
||||
basic_format_args<buffer_context<type_identity_t<Char>>> args)
|
||||
-> format_to_n_result<OutputIt> {
|
||||
using traits = detail::fixed_buffer_traits;
|
||||
auto buf = detail::iterator_buffer<OutputIt, Char, traits>(out, n);
|
||||
detail::vformat_to(buf, format_str, args);
|
||||
return {buf.out(), buf.count()};
|
||||
}
|
||||
|
||||
template <typename OutputIt, typename S, typename... T,
|
||||
typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_output_iterator<OutputIt, Char>::value&&
|
||||
detail::is_exotic_char<Char>::value)>
|
||||
inline auto format_to_n(OutputIt out, size_t n, const S& fmt, T&&... args)
|
||||
-> format_to_n_result<OutputIt> {
|
||||
return vformat_to_n(out, n, detail::to_string_view(fmt),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
}
|
||||
|
||||
template <typename S, typename... T, typename Char = char_t<S>,
|
||||
FMT_ENABLE_IF(detail::is_exotic_char<Char>::value)>
|
||||
inline auto formatted_size(const S& fmt, T&&... args) -> size_t {
|
||||
auto buf = detail::counting_buffer<Char>();
|
||||
detail::vformat_to(buf, detail::to_string_view(fmt),
|
||||
fmt::make_format_args<buffer_context<Char>>(args...));
|
||||
return buf.count();
|
||||
}
|
||||
|
||||
inline void vprint(std::FILE* f, wstring_view fmt, wformat_args args) {
|
||||
auto buf = wmemory_buffer();
|
||||
detail::vformat_to(buf, fmt, args);
|
||||
buf.push_back(L'\0');
|
||||
if (std::fputws(buf.data(), f) == -1)
|
||||
FMT_THROW(system_error(errno, FMT_STRING("cannot write to file")));
|
||||
}
|
||||
|
||||
inline void vprint(wstring_view fmt, wformat_args args) {
|
||||
vprint(stdout, fmt, args);
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
void print(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
|
||||
return vprint(f, wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||
}
|
||||
|
||||
template <typename... T> void print(wformat_string<T...> fmt, T&&... args) {
|
||||
return vprint(wstring_view(fmt), fmt::make_wformat_args(args...));
|
||||
}
|
||||
|
||||
template <typename... T>
|
||||
void println(std::FILE* f, wformat_string<T...> fmt, T&&... args) {
|
||||
return print(f, L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
|
||||
}
|
||||
|
||||
template <typename... T> void println(wformat_string<T...> fmt, T&&... args) {
|
||||
return print(L"{}\n", fmt::format(fmt, std::forward<T>(args)...));
|
||||
}
|
||||
|
||||
/**
|
||||
Converts *value* to ``std::wstring`` using the default format for type *T*.
|
||||
*/
|
||||
template <typename T> inline auto to_wstring(const T& value) -> std::wstring {
|
||||
return format(FMT_STRING(L"{}"), value);
|
||||
}
|
||||
FMT_END_EXPORT
|
||||
FMT_END_NAMESPACE
|
||||
|
||||
#endif // FMT_XCHAR_H_
|
1
lib/fmt
1
lib/fmt
|
@ -1 +0,0 @@
|
|||
Subproject commit a54cb108d4634e768ad3ce0855313991ccc768de
|
99
src/btop.cpp
99
src/btop.cpp
|
@ -4,7 +4,7 @@
|
|||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -43,18 +43,16 @@ tab-size = 4
|
|||
#include <semaphore>
|
||||
#endif
|
||||
|
||||
#include <btop_shared.hpp>
|
||||
#include <btop_tools.hpp>
|
||||
#include <btop_config.hpp>
|
||||
#include <btop_input.hpp>
|
||||
#include <btop_theme.hpp>
|
||||
#include <btop_draw.hpp>
|
||||
#include <btop_menu.hpp>
|
||||
#include "btop_shared.hpp"
|
||||
#include "btop_tools.hpp"
|
||||
#include "btop_config.hpp"
|
||||
#include "btop_input.hpp"
|
||||
#include "btop_theme.hpp"
|
||||
#include "btop_draw.hpp"
|
||||
#include "btop_menu.hpp"
|
||||
|
||||
using std::atomic;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using std::endl;
|
||||
using std::flush;
|
||||
using std::min;
|
||||
using std::string;
|
||||
|
@ -63,7 +61,6 @@ using std::to_string;
|
|||
using std::vector;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
namespace rng = std::ranges;
|
||||
|
||||
using namespace Tools;
|
||||
using namespace std::chrono_literals;
|
||||
|
@ -96,9 +93,9 @@ namespace Global {
|
|||
string exit_error_msg;
|
||||
atomic<bool> thread_exception (false);
|
||||
|
||||
bool debuginit{}; // defaults to false
|
||||
bool debug{}; // defaults to false
|
||||
bool utf_force{}; // defaults to false
|
||||
bool debuginit{}; // defaults to false
|
||||
bool debug{}; // defaults to false
|
||||
bool utf_force{}; // defaults to false
|
||||
|
||||
uint64_t start_time;
|
||||
|
||||
|
@ -108,8 +105,8 @@ namespace Global {
|
|||
atomic<bool> should_sleep (false);
|
||||
atomic<bool> _runner_started (false);
|
||||
|
||||
bool arg_tty{}; // defaults to false
|
||||
bool arg_low_color{}; // defaults to false
|
||||
bool arg_tty{}; // defaults to false
|
||||
bool arg_low_color{}; // defaults to false
|
||||
int arg_preset = -1;
|
||||
}
|
||||
|
||||
|
@ -249,14 +246,14 @@ void clean_quit(int sig) {
|
|||
Runner::stop();
|
||||
if (Global::_runner_started) {
|
||||
#ifdef __APPLE__
|
||||
if (pthread_join(Runner::runner_id, NULL) != 0) {
|
||||
if (pthread_join(Runner::runner_id, nullptr) != 0) {
|
||||
Logger::warning("Failed to join _runner thread on exit!");
|
||||
pthread_cancel(Runner::runner_id);
|
||||
}
|
||||
#else
|
||||
struct timespec ts;
|
||||
ts.tv_sec = 5;
|
||||
if (pthread_timedjoin_np(Runner::runner_id, NULL, &ts) != 0) {
|
||||
if (pthread_timedjoin_np(Runner::runner_id, nullptr, &ts) != 0) {
|
||||
Logger::warning("Failed to join _runner thread on exit!");
|
||||
pthread_cancel(Runner::runner_id);
|
||||
}
|
||||
|
@ -363,14 +360,14 @@ namespace Runner {
|
|||
pthread_mutex_t& pt_mutex;
|
||||
public:
|
||||
int status;
|
||||
thread_lock(pthread_mutex_t& mtx) : pt_mutex(mtx) {
|
||||
pthread_mutex_init(&pt_mutex, NULL);
|
||||
status = pthread_mutex_lock(&pt_mutex);
|
||||
}
|
||||
~thread_lock() {
|
||||
if (status == 0)
|
||||
pthread_mutex_unlock(&pt_mutex);
|
||||
}
|
||||
thread_lock(pthread_mutex_t& mtx) : pt_mutex(mtx) {
|
||||
pthread_mutex_init(&pt_mutex, nullptr);
|
||||
status = pthread_mutex_lock(&pt_mutex);
|
||||
}
|
||||
~thread_lock() {
|
||||
if (status == 0)
|
||||
pthread_mutex_unlock(&pt_mutex);
|
||||
}
|
||||
};
|
||||
|
||||
//* Wrapper for raising priviliges when using SUID bit
|
||||
|
@ -378,18 +375,18 @@ namespace Runner {
|
|||
int status = -1;
|
||||
public:
|
||||
gain_priv() {
|
||||
if (Global::real_uid != Global::set_uid)
|
||||
this->status = seteuid(Global::set_uid);
|
||||
if (Global::real_uid != Global::set_uid)
|
||||
this->status = seteuid(Global::set_uid);
|
||||
}
|
||||
~gain_priv() {
|
||||
if (status == 0)
|
||||
status = seteuid(Global::real_uid);
|
||||
if (status == 0)
|
||||
status = seteuid(Global::real_uid);
|
||||
}
|
||||
};
|
||||
|
||||
string output;
|
||||
string empty_bg;
|
||||
bool pause_output{}; // defaults to false
|
||||
bool pause_output{}; // defaults to false
|
||||
sigset_t mask;
|
||||
pthread_t runner_id;
|
||||
pthread_mutex_t mtx;
|
||||
|
@ -454,14 +451,14 @@ namespace Runner {
|
|||
}
|
||||
|
||||
//? ------------------------------- Secondary thread: async launcher and drawing ----------------------------------
|
||||
void * _runner(void *) {
|
||||
void * _runner(void *) {
|
||||
//? Block some signals in this thread to avoid deadlock from any signal handlers trying to stop this thread
|
||||
sigemptyset(&mask);
|
||||
// sigaddset(&mask, SIGINT);
|
||||
// sigaddset(&mask, SIGTSTP);
|
||||
sigaddset(&mask, SIGWINCH);
|
||||
sigaddset(&mask, SIGTERM);
|
||||
pthread_sigmask(SIG_BLOCK, &mask, NULL);
|
||||
pthread_sigmask(SIG_BLOCK, &mask, nullptr);
|
||||
|
||||
//? pthread_mutex_lock to lock thread and monitor health from main thread
|
||||
thread_lock pt_lck(mtx);
|
||||
|
@ -549,7 +546,7 @@ namespace Runner {
|
|||
if (Global::debug) debug_timer("cpu", draw_done);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
throw std::runtime_error("Cpu:: -> " + string{e.what()});
|
||||
throw std::runtime_error("Cpu:: -> " + string{e.what()});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -586,7 +583,7 @@ namespace Runner {
|
|||
if (Global::debug) debug_timer("mem", draw_done);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
throw std::runtime_error("Mem:: -> " + string{e.what()});
|
||||
throw std::runtime_error("Mem:: -> " + string{e.what()});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -606,7 +603,7 @@ namespace Runner {
|
|||
if (Global::debug) debug_timer("net", draw_done);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
throw std::runtime_error("Net:: -> " + string{e.what()});
|
||||
throw std::runtime_error("Net:: -> " + string{e.what()});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -626,13 +623,13 @@ namespace Runner {
|
|||
if (Global::debug) debug_timer("proc", draw_done);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
throw std::runtime_error("Proc:: -> " + string{e.what()});
|
||||
throw std::runtime_error("Proc:: -> " + string{e.what()});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Global::exit_error_msg = "Exception in runner thread -> " + string{e.what()};
|
||||
Global::exit_error_msg = "Exception in runner thread -> " + string{e.what()};
|
||||
Global::thread_exception = true;
|
||||
Input::interrupt = true;
|
||||
stopping = true;
|
||||
|
@ -711,14 +708,14 @@ namespace Runner {
|
|||
//? ------------------------------------------ Secondary thread end -----------------------------------------------
|
||||
|
||||
//* Runs collect and draw in a secondary thread, unlocks and locks config to update cached values
|
||||
void run(const string& box, bool no_update, bool force_redraw) {
|
||||
void run(const string& box, bool no_update, bool force_redraw) {
|
||||
atomic_wait_for(active, true, 5000);
|
||||
if (active) {
|
||||
Logger::error("Stall in Runner thread, restarting!");
|
||||
active = false;
|
||||
// exit(1);
|
||||
pthread_cancel(Runner::runner_id);
|
||||
if (pthread_create(&Runner::runner_id, NULL, &Runner::_runner, NULL) != 0) {
|
||||
if (pthread_create(&Runner::runner_id, nullptr, &Runner::_runner, nullptr) != 0) {
|
||||
Global::exit_error_msg = "Failed to re-create _runner thread!";
|
||||
clean_quit(1);
|
||||
}
|
||||
|
@ -806,7 +803,7 @@ int main(int argc, char **argv) {
|
|||
|
||||
//? Setup paths for config, log and user themes
|
||||
for (const auto& env : {"XDG_CONFIG_HOME", "HOME"}) {
|
||||
if (std::getenv(env) != NULL and access(std::getenv(env), W_OK) != -1) {
|
||||
if (std::getenv(env) != nullptr and access(std::getenv(env), W_OK) != -1) {
|
||||
Config::conf_dir = fs::path(std::getenv(env)) / (((string)env == "HOME") ? ".config/btop" : "btop");
|
||||
break;
|
||||
}
|
||||
|
@ -873,17 +870,17 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
|
||||
//? Try to find and set a UTF-8 locale
|
||||
if (std::setlocale(LC_ALL, "") != NULL and not s_contains((string)std::setlocale(LC_ALL, ""), ";")
|
||||
if (std::setlocale(LC_ALL, "") != nullptr and not s_contains((string)std::setlocale(LC_ALL, ""), ";")
|
||||
and str_to_upper(s_replace((string)std::setlocale(LC_ALL, ""), "-", "")).ends_with("UTF8")) {
|
||||
Logger::debug("Using locale " + (string)std::setlocale(LC_ALL, ""));
|
||||
}
|
||||
else {
|
||||
string found;
|
||||
bool set_failure{}; // defaults to false
|
||||
bool set_failure{}; // defaults to false
|
||||
for (const auto loc_env : array{"LANG", "LC_ALL"}) {
|
||||
if (std::getenv(loc_env) != NULL and str_to_upper(s_replace((string)std::getenv(loc_env), "-", "")).ends_with("UTF8")) {
|
||||
if (std::getenv(loc_env) != nullptr and str_to_upper(s_replace((string)std::getenv(loc_env), "-", "")).ends_with("UTF8")) {
|
||||
found = std::getenv(loc_env);
|
||||
if (std::setlocale(LC_ALL, found.c_str()) == NULL) {
|
||||
if (std::setlocale(LC_ALL, found.c_str()) == nullptr) {
|
||||
set_failure = true;
|
||||
Logger::warning("Failed to set locale " + found + " continuing anyway.");
|
||||
}
|
||||
|
@ -896,7 +893,7 @@ int main(int argc, char **argv) {
|
|||
for (auto& l : ssplit(loc, ';')) {
|
||||
if (str_to_upper(s_replace(l, "-", "")).ends_with("UTF8")) {
|
||||
found = l.substr(l.find('=') + 1);
|
||||
if (std::setlocale(LC_ALL, found.c_str()) != NULL) {
|
||||
if (std::setlocale(LC_ALL, found.c_str()) != nullptr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -917,10 +914,10 @@ int main(int argc, char **argv) {
|
|||
if (cur_locale.empty()) {
|
||||
Logger::warning("No UTF-8 locale detected! Some symbols might not display correctly.");
|
||||
}
|
||||
else if (std::setlocale(LC_ALL, string(cur_locale + ".UTF-8").c_str()) != NULL) {
|
||||
else if (std::setlocale(LC_ALL, string(cur_locale + ".UTF-8").c_str()) != nullptr) {
|
||||
Logger::debug("Setting LC_ALL=" + cur_locale + ".UTF-8");
|
||||
}
|
||||
else if(std::setlocale(LC_ALL, "en_US.UTF-8") != NULL) {
|
||||
else if(std::setlocale(LC_ALL, "en_US.UTF-8") != nullptr) {
|
||||
Logger::debug("Setting LC_ALL=en_US.UTF-8");
|
||||
}
|
||||
else {
|
||||
|
@ -975,7 +972,7 @@ int main(int argc, char **argv) {
|
|||
Shared::init();
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Global::exit_error_msg = "Exception in Shared::init() -> " + string{e.what()};
|
||||
Global::exit_error_msg = "Exception in Shared::init() -> " + string{e.what()};
|
||||
clean_quit(1);
|
||||
}
|
||||
|
||||
|
@ -992,7 +989,7 @@ int main(int argc, char **argv) {
|
|||
|
||||
//? Start runner thread
|
||||
Runner::thread_sem_init();
|
||||
if (pthread_create(&Runner::runner_id, NULL, &Runner::_runner, NULL) != 0) {
|
||||
if (pthread_create(&Runner::runner_id, nullptr, &Runner::_runner, nullptr) != 0) {
|
||||
Global::exit_error_msg = "Failed to create _runner thread!";
|
||||
clean_quit(1);
|
||||
}
|
||||
|
@ -1087,7 +1084,7 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Global::exit_error_msg = "Exception in main loop -> " + string{e.what()};
|
||||
Global::exit_error_msg = "Exception in main loop -> " + string{e.what()};
|
||||
clean_quit(1);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -17,14 +17,16 @@ tab-size = 4
|
|||
*/
|
||||
|
||||
#include <array>
|
||||
#include <ranges>
|
||||
#include <atomic>
|
||||
#include <fstream>
|
||||
#include <ranges>
|
||||
#include <string_view>
|
||||
|
||||
#include <btop_config.hpp>
|
||||
#include <btop_shared.hpp>
|
||||
#include <btop_tools.hpp>
|
||||
#include <fmt/core.h>
|
||||
|
||||
#include "btop_config.hpp"
|
||||
#include "btop_shared.hpp"
|
||||
#include "btop_tools.hpp"
|
||||
|
||||
using std::array;
|
||||
using std::atomic;
|
||||
|
@ -105,7 +107,7 @@ namespace Config {
|
|||
|
||||
{"proc_left", "#* Show proc box on left side of screen instead of right."},
|
||||
|
||||
{"proc_filter_kernel", "#* (Linux) Filter processes tied to the Linux kernel(similar behavior to htop)."},
|
||||
{"proc_filter_kernel", "#* (Linux) Filter processes tied to the Linux kernel(similar behavior to htop)."},
|
||||
|
||||
{"cpu_graph_upper", "#* Sets the CPU stat shown in upper half of the CPU graph, \"total\" is always available.\n"
|
||||
"#* Select from a list of detected attributes from the options menu."},
|
||||
|
@ -207,7 +209,7 @@ namespace Config {
|
|||
{"custom_gpu_name5", "#* Custom gpu5 model name, empty string to disable."},
|
||||
};
|
||||
|
||||
unordered_flat_map<string, string> strings = {
|
||||
unordered_flat_map<std::string_view, string> strings = {
|
||||
{"color_theme", "Default"},
|
||||
{"shown_boxes", "cpu mem net proc"},
|
||||
{"graph_symbol", "braille"},
|
||||
|
@ -240,9 +242,9 @@ namespace Config {
|
|||
{"custom_gpu_name4", ""},
|
||||
{"custom_gpu_name5", ""},
|
||||
};
|
||||
unordered_flat_map<string, string> stringsTmp;
|
||||
unordered_flat_map<std::string_view, string> stringsTmp;
|
||||
|
||||
unordered_flat_map<string, bool> bools = {
|
||||
unordered_flat_map<std::string_view, bool> bools = {
|
||||
{"theme_background", true},
|
||||
{"truecolor", true},
|
||||
{"rounded_corners", true},
|
||||
|
@ -255,7 +257,7 @@ namespace Config {
|
|||
{"proc_cpu_graphs", true},
|
||||
{"proc_info_smaps", false},
|
||||
{"proc_left", false},
|
||||
{"proc_filter_kernel", false},
|
||||
{"proc_filter_kernel", false},
|
||||
{"cpu_invert_lower", true},
|
||||
{"cpu_single_graph", false},
|
||||
{"cpu_bottom", false},
|
||||
|
@ -290,9 +292,9 @@ namespace Config {
|
|||
{"nvml_measure_pcie_speeds", true},
|
||||
{"gpu_mirror_graph", true},
|
||||
};
|
||||
unordered_flat_map<string, bool> boolsTmp;
|
||||
unordered_flat_map<std::string_view, bool> boolsTmp;
|
||||
|
||||
unordered_flat_map<string, int> ints = {
|
||||
unordered_flat_map<std::string_view, int> ints = {
|
||||
{"update_ms", 2000},
|
||||
{"net_download", 100},
|
||||
{"net_upload", 100},
|
||||
|
@ -303,9 +305,9 @@ namespace Config {
|
|||
{"proc_selected", 0},
|
||||
{"proc_last_selected", 0},
|
||||
};
|
||||
unordered_flat_map<string, int> intsTmp;
|
||||
unordered_flat_map<std::string_view, int> intsTmp;
|
||||
|
||||
bool _locked(const string& name) {
|
||||
bool _locked(const std::string_view name) {
|
||||
atomic_wait(writelock, true);
|
||||
if (not write_new and rng::find_if(descriptions, [&name](const auto& a) { return a.at(0) == name; }) != descriptions.end())
|
||||
write_new = true;
|
||||
|
@ -392,7 +394,7 @@ namespace Config {
|
|||
|
||||
string validError;
|
||||
|
||||
bool intValid(const string& name, const string& value) {
|
||||
bool intValid(const std::string_view name, const string& value) {
|
||||
int i_value;
|
||||
try {
|
||||
i_value = stoi(value);
|
||||
|
@ -406,7 +408,7 @@ namespace Config {
|
|||
return false;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
validError = string{e.what()};
|
||||
validError = string{e.what()};
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -422,7 +424,7 @@ namespace Config {
|
|||
return false;
|
||||
}
|
||||
|
||||
bool stringValid(const string& name, const string& value) {
|
||||
bool stringValid(const std::string_view name, const string& value) {
|
||||
if (name == "log_level" and not v_contains(Logger::log_levels, value))
|
||||
validError = "Invalid log_level: " + value;
|
||||
|
||||
|
@ -430,7 +432,7 @@ namespace Config {
|
|||
validError = "Invalid graph symbol identifier: " + value;
|
||||
|
||||
else if (name.starts_with("graph_symbol_") and (value != "default" and not v_contains(valid_graph_symbols, value)))
|
||||
validError = "Invalid graph symbol identifier for" + name + ": " + value;
|
||||
validError = fmt::format("Invalid graph symbol identifier for {}: {}", name, value);
|
||||
|
||||
else if (name == "shown_boxes" and not value.empty() and not check_boxes(value))
|
||||
validError = "Invalid box name(s) in shown_boxes!";
|
||||
|
@ -479,7 +481,7 @@ namespace Config {
|
|||
return false;
|
||||
}
|
||||
|
||||
string getAsString(const string& name) {
|
||||
string getAsString(const std::string_view name) {
|
||||
if (bools.contains(name))
|
||||
return (bools.at(name) ? "True" : "False");
|
||||
else if (ints.contains(name))
|
||||
|
@ -489,7 +491,7 @@ namespace Config {
|
|||
return "";
|
||||
}
|
||||
|
||||
void flip(const string& name) {
|
||||
void flip(const std::string_view name) {
|
||||
if (_locked(name)) {
|
||||
if (boolsTmp.contains(name)) boolsTmp.at(name) = not boolsTmp.at(name);
|
||||
else boolsTmp.insert_or_assign(name, (not bools.at(name)));
|
||||
|
@ -526,7 +528,7 @@ namespace Config {
|
|||
boolsTmp.clear();
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Global::exit_error_msg = "Exception during Config::unlock() : " + string{e.what()};
|
||||
Global::exit_error_msg = "Exception during Config::unlock() : " + string{e.what()};
|
||||
clean_quit(1);
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -20,9 +20,10 @@ tab-size = 4
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <robin_hood.h>
|
||||
#include <filesystem>
|
||||
|
||||
#include <robin_hood.h>
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
using robin_hood::unordered_flat_map;
|
||||
|
@ -33,12 +34,12 @@ namespace Config {
|
|||
extern std::filesystem::path conf_dir;
|
||||
extern std::filesystem::path conf_file;
|
||||
|
||||
extern unordered_flat_map<string, string> strings;
|
||||
extern unordered_flat_map<string, string> stringsTmp;
|
||||
extern unordered_flat_map<string, bool> bools;
|
||||
extern unordered_flat_map<string, bool> boolsTmp;
|
||||
extern unordered_flat_map<string, int> ints;
|
||||
extern unordered_flat_map<string, int> intsTmp;
|
||||
extern unordered_flat_map<std::string_view, string> strings;
|
||||
extern unordered_flat_map<std::string_view, string> stringsTmp;
|
||||
extern unordered_flat_map<std::string_view, bool> bools;
|
||||
extern unordered_flat_map<std::string_view, bool> boolsTmp;
|
||||
extern unordered_flat_map<std::string_view, int> ints;
|
||||
extern unordered_flat_map<std::string_view, int> intsTmp;
|
||||
|
||||
const vector<string> valid_graph_symbols = { "braille", "block", "tty" };
|
||||
const vector<string> valid_graph_symbols_def = { "default", "braille", "block", "tty" };
|
||||
|
@ -62,44 +63,44 @@ namespace Config {
|
|||
//* Apply selected preset
|
||||
void apply_preset(const string& preset);
|
||||
|
||||
bool _locked(const string& name);
|
||||
bool _locked(const std::string_view name);
|
||||
|
||||
//* Return bool for config key <name>
|
||||
inline bool getB(const string& name) { return bools.at(name); }
|
||||
inline bool getB(const std::string_view name) { return bools.at(name); }
|
||||
|
||||
//* Return integer for config key <name>
|
||||
inline const int& getI(const string& name) { return ints.at(name); }
|
||||
inline const int& getI(const std::string_view name) { return ints.at(name); }
|
||||
|
||||
//* Return string for config key <name>
|
||||
inline const string& getS(const string& name) { return strings.at(name); }
|
||||
inline const string& getS(const std::string_view name) { return strings.at(name); }
|
||||
|
||||
string getAsString(const string& name);
|
||||
string getAsString(const std::string_view name);
|
||||
|
||||
extern string validError;
|
||||
|
||||
bool intValid(const string& name, const string& value);
|
||||
bool stringValid(const string& name, const string& value);
|
||||
bool intValid(const std::string_view name, const string& value);
|
||||
bool stringValid(const std::string_view name, const string& value);
|
||||
|
||||
//* Set config key <name> to bool <value>
|
||||
inline void set(const string& name, bool value) {
|
||||
inline void set(const std::string_view name, bool value) {
|
||||
if (_locked(name)) boolsTmp.insert_or_assign(name, value);
|
||||
else bools.at(name) = value;
|
||||
}
|
||||
|
||||
//* Set config key <name> to int <value>
|
||||
inline void set(const string& name, const int& value) {
|
||||
inline void set(const std::string_view name, const int& value) {
|
||||
if (_locked(name)) intsTmp.insert_or_assign(name, value);
|
||||
else ints.at(name) = value;
|
||||
}
|
||||
|
||||
//* Set config key <name> to string <value>
|
||||
inline void set(const string& name, const string& value) {
|
||||
inline void set(const std::string_view name, const string& value) {
|
||||
if (_locked(name)) stringsTmp.insert_or_assign(name, value);
|
||||
else strings.at(name) = value;
|
||||
}
|
||||
|
||||
//* Flip config key bool <name>
|
||||
void flip(const string& name);
|
||||
void flip(const std::string_view name);
|
||||
|
||||
//* Lock config and cache changes until unlocked
|
||||
void lock();
|
||||
|
|
|
@ -20,17 +20,18 @@ tab-size = 4
|
|||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <ranges>
|
||||
|
||||
#include <btop_draw.hpp>
|
||||
#include <btop_config.hpp>
|
||||
#include <btop_theme.hpp>
|
||||
#include <btop_shared.hpp>
|
||||
#include <btop_tools.hpp>
|
||||
#include <btop_input.hpp>
|
||||
#include <btop_menu.hpp>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "btop_draw.hpp"
|
||||
#include "btop_config.hpp"
|
||||
#include "btop_theme.hpp"
|
||||
#include "btop_shared.hpp"
|
||||
#include "btop_tools.hpp"
|
||||
#include "btop_input.hpp"
|
||||
#include "btop_menu.hpp"
|
||||
|
||||
|
||||
using std::array;
|
||||
using std::clamp;
|
||||
using std::cmp_equal;
|
||||
|
@ -108,8 +109,8 @@ namespace Draw {
|
|||
if (redraw) banner.clear();
|
||||
if (banner.empty()) {
|
||||
string b_color, bg, fg, oc, letter;
|
||||
auto lowcolor = Config::getB("lowcolor");
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
auto lowcolor = Config::getB("lowcolor");
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
for (size_t z = 0; const auto& line : Global::Banner_src) {
|
||||
if (const auto w = ulen(line[1]); w > width) width = w;
|
||||
if (tty_mode) {
|
||||
|
@ -158,10 +159,10 @@ namespace Draw {
|
|||
upos++;
|
||||
pos = uresize(text, upos).size();
|
||||
}
|
||||
else if (key == "home" and pos > 0) {
|
||||
else if (key == "home" and not text.empty() and pos > 0) {
|
||||
pos = upos = 0;
|
||||
}
|
||||
else if (key == "end" and pos < text.size()) {
|
||||
else if (key == "end" and not text.empty() and pos < text.size()) {
|
||||
pos = text.size();
|
||||
upos = ulen(text);
|
||||
}
|
||||
|
@ -204,6 +205,10 @@ namespace Draw {
|
|||
}
|
||||
|
||||
string TextEdit::operator()(const size_t limit) {
|
||||
string out;
|
||||
size_t c_upos = upos;
|
||||
if (text.empty())
|
||||
return Fx::ul + " " + Fx::uul;
|
||||
if (limit > 0 and ulen(text) + 1 > limit) {
|
||||
try {
|
||||
const size_t half = (size_t)round((double)limit / 2);
|
||||
|
@ -216,29 +221,39 @@ namespace Draw {
|
|||
else
|
||||
first = luresize(text.substr(0, pos), half);
|
||||
|
||||
return first + Fx::bl + "█" + Fx::ubl + uresize(text.substr(pos), limit - ulen(first));
|
||||
out = first + uresize(text.substr(pos), limit - ulen(first));
|
||||
c_upos = ulen(first);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Logger::error("In TextEdit::operator() : " + string{e.what()});
|
||||
Logger::error("In TextEdit::operator() : " + string{e.what()});
|
||||
return "";
|
||||
}
|
||||
}
|
||||
return text.substr(0, pos) + Fx::bl + "█" + Fx::ubl + text.substr(pos);
|
||||
else
|
||||
out = text;
|
||||
|
||||
if (c_upos == 0)
|
||||
return Fx::ul + uresize(out, 1) + Fx::uul + luresize(out, ulen(out) - 1);
|
||||
else if (c_upos == ulen(out))
|
||||
return out + Fx::ul + " " + Fx::uul;
|
||||
else
|
||||
return uresize(out, c_upos) + Fx::ul + luresize(uresize(out, c_upos + 1), 1) + Fx::uul + luresize(out, ulen(out) - c_upos - 1);
|
||||
}
|
||||
|
||||
void TextEdit::clear() {
|
||||
this->text.clear();
|
||||
}
|
||||
|
||||
string createBox(const int x, const int y, const int width,
|
||||
const int height, string line_color, bool fill,
|
||||
const string title, const string title2, const int num) {
|
||||
string createBox(const int x, const int y, const int width,
|
||||
const int height, string line_color, bool fill,
|
||||
const string title, const string title2, const int num) {
|
||||
string out;
|
||||
|
||||
if (line_color.empty())
|
||||
line_color = Theme::c("div_line");
|
||||
if (line_color.empty())
|
||||
line_color = Theme::c("div_line");
|
||||
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
auto rounded = Config::getB("rounded_corners");
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
auto rounded = Config::getB("rounded_corners");
|
||||
const string numbering = (num == 0) ? "" : Theme::c("hi_fg") + (tty_mode ? std::to_string(num) : Symbols::superscript.at(clamp(num, 0, 9)));
|
||||
const auto& right_up = (tty_mode or not rounded ? Symbols::right_up : Symbols::round_right_up);
|
||||
const auto& left_up = (tty_mode or not rounded ? Symbols::left_up : Symbols::round_left_up);
|
||||
|
@ -248,12 +263,12 @@ namespace Draw {
|
|||
out = Fx::reset + line_color;
|
||||
|
||||
//? Draw horizontal lines
|
||||
for (const int& hpos : {y, y + height - 1}) {
|
||||
for (const int& hpos : {y, y + height - 1}) {
|
||||
out += Mv::to(hpos, x) + Symbols::h_line * (width - 1);
|
||||
}
|
||||
|
||||
//? Draw vertical lines and fill if enabled
|
||||
for (const int& hpos : iota(y + 1, y + height - 1)) {
|
||||
for (const int& hpos : iota(y + 1, y + height - 1)) {
|
||||
out += Mv::to(hpos, x) + Symbols::v_line
|
||||
+ ((fill) ? string(width - 2, ' ') : Mv::r(width - 2))
|
||||
+ Symbols::v_line;
|
||||
|
@ -291,11 +306,11 @@ namespace Draw {
|
|||
{"/uptime", ""}
|
||||
};
|
||||
|
||||
static time_t c_time{}; // defaults to 0
|
||||
static size_t clock_len{}; // defaults to 0
|
||||
static time_t c_time{}; // defaults to 0
|
||||
static size_t clock_len{}; // defaults to 0
|
||||
static string clock_str;
|
||||
|
||||
if (auto n_time = time(NULL); not force and n_time == c_time)
|
||||
if (auto n_time = time(nullptr); not force and n_time == c_time)
|
||||
return false;
|
||||
else {
|
||||
c_time = n_time;
|
||||
|
@ -305,7 +320,7 @@ namespace Draw {
|
|||
}
|
||||
|
||||
auto& out = Global::clock;
|
||||
auto cpu_bottom = Config::getB("cpu_bottom");
|
||||
auto cpu_bottom = Config::getB("cpu_bottom");
|
||||
const auto& x = Cpu::x;
|
||||
const auto y = (cpu_bottom ? Cpu::y + Cpu::height - 1 : Cpu::y);
|
||||
const auto& width = Cpu::width;
|
||||
|
@ -345,8 +360,8 @@ namespace Draw {
|
|||
//* Meter class ------------------------------------------------------------------------------------------------------------>
|
||||
Meter::Meter() {}
|
||||
|
||||
Meter::Meter(const int width, const string& color_gradient, bool invert)
|
||||
: width(width), color_gradient(color_gradient), invert(invert) {}
|
||||
Meter::Meter(const int width, const string& color_gradient, bool invert)
|
||||
: width(width), color_gradient(color_gradient), invert(invert) {}
|
||||
|
||||
string Meter::operator()(int value) {
|
||||
if (width < 1) return "";
|
||||
|
@ -368,7 +383,7 @@ namespace Draw {
|
|||
|
||||
//* Graph class ------------------------------------------------------------------------------------------------------------>
|
||||
void Graph::_create(const deque<long long>& data, int data_offset) {
|
||||
bool mult = (data.size() - data_offset > 1);
|
||||
bool mult = (data.size() - data_offset > 1);
|
||||
const auto& graph_symbol = Symbols::graph_symbols.at(symbol + '_' + (invert ? "down" : "up"));
|
||||
array<int, 2> result;
|
||||
const float mod = (height == 1) ? 0.3 : 0.1;
|
||||
|
@ -438,11 +453,11 @@ namespace Draw {
|
|||
|
||||
Graph::Graph() {}
|
||||
|
||||
Graph::Graph(int width, int height, const string& color_gradient,
|
||||
const deque<long long>& data, const string& symbol,
|
||||
bool invert, bool no_zero, long long max_value, long long offset)
|
||||
: width(width), height(height), color_gradient(color_gradient),
|
||||
invert(invert), no_zero(no_zero), offset(offset) {
|
||||
Graph::Graph(int width, int height, const string& color_gradient,
|
||||
const deque<long long>& data, const string& symbol,
|
||||
bool invert, bool no_zero, long long max_value, long long offset)
|
||||
: width(width), height(height), color_gradient(color_gradient),
|
||||
invert(invert), no_zero(no_zero), offset(offset) {
|
||||
if (Config::getB("tty_mode") or symbol == "tty") this->symbol = "tty";
|
||||
else if (symbol != "default") this->symbol = symbol;
|
||||
else this->symbol = Config::getS("graph_symbol");
|
||||
|
@ -466,7 +481,7 @@ namespace Draw {
|
|||
this->_create(data, data_offset);
|
||||
}
|
||||
|
||||
string& Graph::operator()(const deque<long long>& data, bool data_same) {
|
||||
string& Graph::operator()(const deque<long long>& data, bool data_same) {
|
||||
if (data_same) return out;
|
||||
|
||||
//? Make room for new characters on graph
|
||||
|
@ -514,23 +529,23 @@ namespace Cpu {
|
|||
string draw(const cpu_info& cpu, const vector<Gpu::gpu_info>& gpus, bool force_redraw, bool data_same) {
|
||||
if (Runner::stopping) return "";
|
||||
if (force_redraw) redraw = true;
|
||||
bool show_temps = (Config::getB("check_temp") and got_sensors);
|
||||
auto single_graph = Config::getB("cpu_single_graph");
|
||||
bool hide_cores = show_temps and (cpu_temp_only or not Config::getB("show_coretemp"));
|
||||
bool show_temps = (Config::getB("check_temp") and got_sensors);
|
||||
auto single_graph = Config::getB("cpu_single_graph");
|
||||
bool hide_cores = show_temps and (cpu_temp_only or not Config::getB("show_coretemp"));
|
||||
const int extra_width = (hide_cores ? max(6, 6 * b_column_size) : 0);
|
||||
auto& graph_up_field = Config::getS("cpu_graph_upper");
|
||||
auto& graph_lo_field = Config::getS("cpu_graph_lower");
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
auto& graph_symbol = (tty_mode ? "tty" : Config::getS("graph_symbol_cpu"));
|
||||
auto& graph_bg = Symbols::graph_symbols.at((graph_symbol == "default" ? Config::getS("graph_symbol") + "_up" : graph_symbol + "_up")).at(6);
|
||||
auto& temp_scale = Config::getS("temp_scale");
|
||||
auto cpu_bottom = Config::getB("cpu_bottom");
|
||||
auto cpu_bottom = Config::getB("cpu_bottom");
|
||||
const string& title_left = Theme::c("cpu_box") + (cpu_bottom ? Symbols::title_left_down : Symbols::title_left);
|
||||
const string& title_right = Theme::c("cpu_box") + (cpu_bottom ? Symbols::title_right_down : Symbols::title_right);
|
||||
static int bat_pos = 0, bat_len = 0;
|
||||
if (cpu.cpu_percent.at("total").empty()
|
||||
or cpu.core_percent.at(0).empty()
|
||||
or (show_temps and cpu.temp.at(0).empty())) return "";
|
||||
if (cpu.cpu_percent.at("total").empty()
|
||||
or cpu.core_percent.at(0).empty()
|
||||
or (show_temps and cpu.temp.at(0).empty())) return "";
|
||||
string out;
|
||||
out.reserve(width * height);
|
||||
|
||||
|
@ -664,8 +679,8 @@ namespace Cpu {
|
|||
|
||||
//? Draw battery if enabled and present
|
||||
if (Config::getB("show_battery") and has_battery) {
|
||||
static int old_percent{}; // defaults to = 0
|
||||
static long old_seconds{}; // defaults to = 0
|
||||
static int old_percent{}; // defaults to = 0
|
||||
static long old_seconds{}; // defaults to = 0
|
||||
static string old_status;
|
||||
static Draw::Meter bat_meter {10, "cpu", true};
|
||||
static const unordered_flat_map<string, string> bat_symbols = {
|
||||
|
@ -758,7 +773,7 @@ namespace Cpu {
|
|||
}
|
||||
out += Theme::c("div_line") + Symbols::v_line;
|
||||
|
||||
} catch (const std::exception& e) { throw std::runtime_error("graphs, clock, meter : " + string{e.what()}); }
|
||||
} catch (const std::exception& e) { throw std::runtime_error("graphs, clock, meter : " + string{e.what()}); }
|
||||
|
||||
//? Core text and graphs
|
||||
int cx = 0, cy = 1, cc = 0, core_width = (b_column_size == 0 ? 2 : 3);
|
||||
|
@ -1048,17 +1063,17 @@ namespace Mem {
|
|||
unordered_flat_map<string, Draw::Meter> disk_meters_free;
|
||||
unordered_flat_map<string, Draw::Graph> io_graphs;
|
||||
|
||||
string draw(const mem_info& mem, bool force_redraw, bool data_same) {
|
||||
string draw(const mem_info& mem, bool force_redraw, bool data_same) {
|
||||
if (Runner::stopping) return "";
|
||||
if (force_redraw) redraw = true;
|
||||
auto show_swap = Config::getB("show_swap");
|
||||
auto swap_disk = Config::getB("swap_disk");
|
||||
auto show_disks = Config::getB("show_disks");
|
||||
auto show_io_stat = Config::getB("show_io_stat");
|
||||
auto io_mode = Config::getB("io_mode");
|
||||
auto io_graph_combined = Config::getB("io_graph_combined");
|
||||
auto use_graphs = Config::getB("mem_graphs");
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
auto show_swap = Config::getB("show_swap");
|
||||
auto swap_disk = Config::getB("swap_disk");
|
||||
auto show_disks = Config::getB("show_disks");
|
||||
auto show_io_stat = Config::getB("show_io_stat");
|
||||
auto io_mode = Config::getB("io_mode");
|
||||
auto io_graph_combined = Config::getB("io_graph_combined");
|
||||
auto use_graphs = Config::getB("mem_graphs");
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
auto& graph_symbol = (tty_mode ? "tty" : Config::getS("graph_symbol_mem"));
|
||||
auto& graph_bg = Symbols::graph_symbols.at((graph_symbol == "default" ? Config::getS("graph_symbol") + "_up" : graph_symbol + "_up")).at(6);
|
||||
auto totalMem = Mem::get_totalMem();
|
||||
|
@ -1124,20 +1139,20 @@ namespace Mem {
|
|||
if (io_graph_combined) {
|
||||
deque<long long> combined(disk.io_read.size(), 0);
|
||||
rng::transform(disk.io_read, disk.io_write, combined.begin(), std::plus<long long>());
|
||||
io_graphs[name] = Draw::Graph{
|
||||
disks_width - (io_mode ? 0 : 6),
|
||||
disks_io_h, "available", combined,
|
||||
graph_symbol, false, true, speed};
|
||||
io_graphs[name] = Draw::Graph{
|
||||
disks_width - (io_mode ? 0 : 6),
|
||||
disks_io_h, "available", combined,
|
||||
graph_symbol, false, true, speed};
|
||||
}
|
||||
else {
|
||||
io_graphs[name + "_read"] = Draw::Graph{
|
||||
disks_width, half_height, "free",
|
||||
disk.io_read, graph_symbol, false,
|
||||
true, speed};
|
||||
io_graphs[name + "_write"] = Draw::Graph{
|
||||
disks_width, disks_io_h - half_height,
|
||||
"used", disk.io_write, graph_symbol,
|
||||
true, true, speed};
|
||||
io_graphs[name + "_read"] = Draw::Graph{
|
||||
disks_width, half_height, "free",
|
||||
disk.io_read, graph_symbol, false,
|
||||
true, speed};
|
||||
io_graphs[name + "_write"] = Draw::Graph{
|
||||
disks_width, disks_io_h - half_height,
|
||||
"used", disk.io_write, graph_symbol,
|
||||
true, true, speed};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1207,7 +1222,7 @@ namespace Mem {
|
|||
if (show_disks) {
|
||||
const auto& disks = mem.disks;
|
||||
cx = mem_width; cy = 0;
|
||||
bool big_disk = disks_width >= 25;
|
||||
bool big_disk = disks_width >= 25;
|
||||
divider = Mv::l(1) + Theme::c("div_line") + Symbols::div_left + Symbols::h_line * disks_width + Theme::c("mem_box") + Fx::ub + Symbols::div_right + Mv::l(disks_width);
|
||||
const string hu_div = Theme::c("div_line") + Symbols::h_line + Theme::c("main_fg");
|
||||
if (io_mode) {
|
||||
|
@ -1302,12 +1317,12 @@ namespace Net {
|
|||
unordered_flat_map<string, Draw::Graph> graphs;
|
||||
string box;
|
||||
|
||||
string draw(const net_info& net, bool force_redraw, bool data_same) {
|
||||
string draw(const net_info& net, bool force_redraw, bool data_same) {
|
||||
if (Runner::stopping) return "";
|
||||
if (force_redraw) redraw = true;
|
||||
auto net_sync = Config::getB("net_sync");
|
||||
auto net_auto = Config::getB("net_auto");
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
auto net_sync = Config::getB("net_sync");
|
||||
auto net_auto = Config::getB("net_auto");
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
auto& graph_symbol = (tty_mode ? "tty" : Config::getS("graph_symbol_net"));
|
||||
string ip_addr = (net.ipv4.empty() ? net.ipv6 : net.ipv4);
|
||||
if (old_ip != ip_addr) {
|
||||
|
@ -1329,13 +1344,13 @@ namespace Net {
|
|||
graphs.clear();
|
||||
if (net.bandwidth.at("download").empty() or net.bandwidth.at("upload").empty())
|
||||
return out + Fx::reset;
|
||||
graphs["download"] = Draw::Graph{
|
||||
width - b_width - 2, u_graph_height, "download",
|
||||
net.bandwidth.at("download"), graph_symbol,
|
||||
false, true, down_max};
|
||||
graphs["upload"] = Draw::Graph{
|
||||
width - b_width - 2, d_graph_height, "upload",
|
||||
net.bandwidth.at("upload"), graph_symbol, true, true, up_max};
|
||||
graphs["download"] = Draw::Graph{
|
||||
width - b_width - 2, u_graph_height, "download",
|
||||
net.bandwidth.at("download"), graph_symbol,
|
||||
false, true, down_max};
|
||||
graphs["upload"] = Draw::Graph{
|
||||
width - b_width - 2, d_graph_height, "upload",
|
||||
net.bandwidth.at("upload"), graph_symbol, true, true, up_max};
|
||||
|
||||
//? Interface selector and buttons
|
||||
|
||||
|
@ -1416,7 +1431,7 @@ namespace Proc {
|
|||
auto selected = Config::getI("proc_selected");
|
||||
auto last_selected = Config::getI("proc_last_selected");
|
||||
const int select_max = (Config::getB("show_detailed") ? Proc::select_max - 8 : Proc::select_max);
|
||||
auto vim_keys = Config::getB("vim_keys");
|
||||
auto vim_keys = Config::getB("vim_keys");
|
||||
|
||||
int numpids = Proc::numpids;
|
||||
if ((cmd_key == "up" or (vim_keys and cmd_key == "k")) and selected > 0) {
|
||||
|
@ -1471,18 +1486,18 @@ namespace Proc {
|
|||
return (not changed ? -1 : selected);
|
||||
}
|
||||
|
||||
string draw(const vector<proc_info>& plist, bool force_redraw, bool data_same) {
|
||||
string draw(const vector<proc_info>& plist, bool force_redraw, bool data_same) {
|
||||
if (Runner::stopping) return "";
|
||||
auto proc_tree = Config::getB("proc_tree");
|
||||
bool show_detailed = (Config::getB("show_detailed") and cmp_equal(Proc::detailed.last_pid, Config::getI("detailed_pid")));
|
||||
bool proc_gradient = (Config::getB("proc_gradient") and not Config::getB("lowcolor") and Theme::gradients.contains("proc"));
|
||||
auto proc_colors = Config::getB("proc_colors");
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
auto proc_tree = Config::getB("proc_tree");
|
||||
bool show_detailed = (Config::getB("show_detailed") and cmp_equal(Proc::detailed.last_pid, Config::getI("detailed_pid")));
|
||||
bool proc_gradient = (Config::getB("proc_gradient") and not Config::getB("lowcolor") and Theme::gradients.contains("proc"));
|
||||
auto proc_colors = Config::getB("proc_colors");
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
auto& graph_symbol = (tty_mode ? "tty" : Config::getS("graph_symbol_proc"));
|
||||
auto& graph_bg = Symbols::graph_symbols.at((graph_symbol == "default" ? Config::getS("graph_symbol") + "_up" : graph_symbol + "_up")).at(6);
|
||||
auto mem_bytes = Config::getB("proc_mem_bytes");
|
||||
auto vim_keys = Config::getB("vim_keys");
|
||||
auto show_graphs = Config::getB("proc_cpu_graphs");
|
||||
auto mem_bytes = Config::getB("proc_mem_bytes");
|
||||
auto vim_keys = Config::getB("vim_keys");
|
||||
auto show_graphs = Config::getB("proc_cpu_graphs");
|
||||
start = Config::getI("proc_start");
|
||||
selected = Config::getI("proc_selected");
|
||||
const int y = show_detailed ? Proc::y + 8 : Proc::y;
|
||||
|
@ -1517,7 +1532,7 @@ namespace Proc {
|
|||
|
||||
//? Detailed box
|
||||
if (show_detailed) {
|
||||
bool alive = detailed.status != "Dead";
|
||||
bool alive = detailed.status != "Dead";
|
||||
dgraph_x = x;
|
||||
dgraph_width = max(width / 3, width - 121);
|
||||
d_width = width - dgraph_width - 1;
|
||||
|
@ -1589,7 +1604,7 @@ namespace Proc {
|
|||
}
|
||||
|
||||
//? Filter
|
||||
auto filtering = Config::getB("proc_filtering"); // ? filter(20) : Config::getS("proc_filter"))
|
||||
auto filtering = Config::getB("proc_filtering"); // ? filter(20) : Config::getS("proc_filter"))
|
||||
const auto filter_text = (filtering) ? filter(max(6, width - 58)) : uresize(Config::getS("proc_filter"), max(6, width - 58));
|
||||
out += Mv::to(y, x+9) + title_left + (not filter_text.empty() ? Fx::b : "") + Theme::c("hi_fg") + 'f'
|
||||
+ Theme::c("title") + (not filter_text.empty() ? ' ' + filter_text : "ilter")
|
||||
|
@ -1670,7 +1685,7 @@ namespace Proc {
|
|||
|
||||
//? Draw details box if shown
|
||||
if (show_detailed) {
|
||||
bool alive = detailed.status != "Dead";
|
||||
bool alive = detailed.status != "Dead";
|
||||
const int item_fit = floor((double)(d_width - 2) / 10);
|
||||
const int item_width = floor((double)(d_width - 2) / min(item_fit, 8));
|
||||
|
||||
|
@ -1729,7 +1744,7 @@ namespace Proc {
|
|||
}
|
||||
|
||||
//? Update graphs for processes with above 0.0% cpu usage, delete if below 0.1% 10x times
|
||||
bool has_graph = show_graphs ? p_counters.contains(p.pid) : false;
|
||||
bool has_graph = show_graphs ? p_counters.contains(p.pid) : false;
|
||||
if (show_graphs and ((p.cpu_p > 0 and not has_graph) or (not data_same and has_graph))) {
|
||||
if (not has_graph) {
|
||||
p_graphs[p.pid] = Draw::Graph{5, 1, "", {}, graph_symbol};
|
||||
|
@ -1886,10 +1901,10 @@ namespace Draw {
|
|||
void calcSizes() {
|
||||
atomic_wait(Runner::active);
|
||||
Config::unlock();
|
||||
auto boxes = Config::getS("shown_boxes");
|
||||
auto cpu_bottom = Config::getB("cpu_bottom");
|
||||
auto mem_below_net = Config::getB("mem_below_net");
|
||||
auto proc_left = Config::getB("proc_left");
|
||||
auto boxes = Config::getS("shown_boxes");
|
||||
auto cpu_bottom = Config::getB("cpu_bottom");
|
||||
auto mem_below_net = Config::getB("mem_below_net");
|
||||
auto proc_left = Config::getB("proc_left");
|
||||
|
||||
Cpu::box.clear();
|
||||
Gpu::box.clear();
|
||||
|
@ -2030,9 +2045,9 @@ namespace Draw {
|
|||
//* Calculate and draw mem box outlines
|
||||
if (Mem::shown) {
|
||||
using namespace Mem;
|
||||
auto show_disks = Config::getB("show_disks");
|
||||
auto swap_disk = Config::getB("swap_disk");
|
||||
auto mem_graphs = Config::getB("mem_graphs");
|
||||
auto show_disks = Config::getB("show_disks");
|
||||
auto swap_disk = Config::getB("swap_disk");
|
||||
auto mem_graphs = Config::getB("mem_graphs");
|
||||
|
||||
width = round((double)Term::width * (Proc::shown ? width_p : 100) / 100);
|
||||
height = ceil((double)Term::height * (100 - Net::height_p * Net::shown*4 / ((Gpu::shown != 0 and Cpu::shown) + 4)) / 100) - Cpu::height - Gpu::height*Gpu::shown;
|
||||
|
|
|
@ -66,8 +66,8 @@ namespace Draw {
|
|||
|
||||
//* An editable text field
|
||||
class TextEdit {
|
||||
size_t pos{}; // defaults to 0
|
||||
size_t upos{}; // defaults to 0
|
||||
size_t pos{}; // defaults to 0
|
||||
size_t upos{}; // defaults to 0
|
||||
bool numeric;
|
||||
public:
|
||||
string text;
|
||||
|
@ -79,11 +79,11 @@ namespace Draw {
|
|||
};
|
||||
|
||||
//* Create a box and return as a string
|
||||
string createBox(const int x, const int y, const int width,
|
||||
const int height, string line_color = "", bool fill = false,
|
||||
const string title = "", const string title2 = "", const int num = 0);
|
||||
string createBox(const int x, const int y, const int width,
|
||||
const int height, string line_color = "", bool fill = false,
|
||||
const string title = "", const string title2 = "", const int num = 0);
|
||||
|
||||
bool update_clock(bool force = false);
|
||||
bool update_clock(bool force = false);
|
||||
|
||||
//* Class holding a percentage meter
|
||||
class Meter {
|
||||
|
@ -93,7 +93,7 @@ namespace Draw {
|
|||
array<string, 101> cache;
|
||||
public:
|
||||
Meter();
|
||||
Meter(const int width, const string& color_gradient, bool invert = false);
|
||||
Meter(const int width, const string& color_gradient, bool invert = false);
|
||||
|
||||
//* Return a string representation of the meter with given value
|
||||
string operator()(int value);
|
||||
|
@ -115,15 +115,15 @@ namespace Draw {
|
|||
|
||||
public:
|
||||
Graph();
|
||||
Graph(int width, int height,
|
||||
const string& color_gradient,
|
||||
const deque<long long>& data,
|
||||
const string& symbol="default",
|
||||
bool invert=false, bool no_zero=false,
|
||||
long long max_value=0, long long offset=0);
|
||||
Graph(int width, int height,
|
||||
const string& color_gradient,
|
||||
const deque<long long>& data,
|
||||
const string& symbol="default",
|
||||
bool invert=false, bool no_zero=false,
|
||||
long long max_value=0, long long offset=0);
|
||||
|
||||
//* Add last value from back of <data> and return string representation of graph
|
||||
string& operator()(const deque<long long>& data, bool data_same=false);
|
||||
string& operator()(const deque<long long>& data, bool data_same=false);
|
||||
|
||||
//* Return string representation of graph
|
||||
string& operator()();
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -21,17 +21,17 @@ tab-size = 4
|
|||
#include <vector>
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
#include <btop_input.hpp>
|
||||
#include <btop_tools.hpp>
|
||||
#include <btop_config.hpp>
|
||||
#include <btop_shared.hpp>
|
||||
#include <btop_menu.hpp>
|
||||
#include <btop_draw.hpp>
|
||||
#include <signal.h>
|
||||
|
||||
#include "btop_input.hpp"
|
||||
#include "btop_tools.hpp"
|
||||
#include "btop_config.hpp"
|
||||
#include "btop_shared.hpp"
|
||||
#include "btop_menu.hpp"
|
||||
#include "btop_draw.hpp"
|
||||
|
||||
|
||||
using std::cin;
|
||||
using std::vector;
|
||||
|
||||
using namespace Tools;
|
||||
using namespace std::literals; // for operator""s
|
||||
|
@ -238,8 +238,8 @@ namespace Input {
|
|||
void process(const string& key) {
|
||||
if (key.empty()) return;
|
||||
try {
|
||||
auto filtering = Config::getB("proc_filtering");
|
||||
auto vim_keys = Config::getB("vim_keys");
|
||||
auto filtering = Config::getB("proc_filtering");
|
||||
auto vim_keys = Config::getB("vim_keys");
|
||||
auto help_key = (vim_keys ? "H" : "h");
|
||||
auto kill_key = (vim_keys ? "K" : "k");
|
||||
//? Global input actions
|
||||
|
@ -299,12 +299,12 @@ namespace Input {
|
|||
if (key == "enter" or key == "down") {
|
||||
Config::set("proc_filter", Proc::filter.text);
|
||||
Config::set("proc_filtering", false);
|
||||
old_filter.clear();
|
||||
if(key == "down"){
|
||||
process("down");
|
||||
return;
|
||||
}
|
||||
}
|
||||
old_filter.clear();
|
||||
if(key == "down"){
|
||||
process("down");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (key == "escape" or key == "mouse_click") {
|
||||
Config::set("proc_filter", old_filter);
|
||||
Config::set("proc_filtering", false);
|
||||
|
@ -551,7 +551,7 @@ namespace Input {
|
|||
}
|
||||
|
||||
catch (const std::exception& e) {
|
||||
throw std::runtime_error("Input::process(\"" + key + "\") : " + string{e.what()});
|
||||
throw std::runtime_error("Input::process(\"" + key + "\") : " + string{e.what()});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -32,7 +32,7 @@ using std::string;
|
|||
|
||||
/* The input functions relies on the following std::cin options being set:
|
||||
cin.sync_with_stdio(false);
|
||||
cin.tie(NULL);
|
||||
cin.tie(nullptr);
|
||||
These will automatically be set when running Term::init() from btop_tools.cpp
|
||||
*/
|
||||
|
||||
|
|
|
@ -19,24 +19,21 @@ tab-size = 4
|
|||
#include <deque>
|
||||
#include <robin_hood.h>
|
||||
#include <array>
|
||||
#include <ranges>
|
||||
#include <signal.h>
|
||||
#include <errno.h>
|
||||
#include <cmath>
|
||||
#include <filesystem>
|
||||
|
||||
#include <btop_menu.hpp>
|
||||
#include <btop_tools.hpp>
|
||||
#include <btop_config.hpp>
|
||||
#include <btop_theme.hpp>
|
||||
#include <btop_draw.hpp>
|
||||
#include <btop_shared.hpp>
|
||||
#include "btop_menu.hpp"
|
||||
#include "btop_tools.hpp"
|
||||
#include "btop_config.hpp"
|
||||
#include "btop_theme.hpp"
|
||||
#include "btop_draw.hpp"
|
||||
#include "btop_shared.hpp"
|
||||
|
||||
using robin_hood::unordered_flat_map;
|
||||
using std::array;
|
||||
using std::ceil;
|
||||
using std::clamp;
|
||||
using std::deque;
|
||||
using std::max;
|
||||
using std::min;
|
||||
using std::ref;
|
||||
|
@ -45,7 +42,6 @@ using std::views::iota;
|
|||
using namespace Tools;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
namespace rng = std::ranges;
|
||||
|
||||
namespace Menu {
|
||||
|
||||
|
@ -685,19 +681,19 @@ namespace Menu {
|
|||
"Show cpu graph for each process.",
|
||||
"",
|
||||
"True or False"},
|
||||
{"proc_filter_kernel",
|
||||
"(Linux) Filter kernel processes from output.",
|
||||
"",
|
||||
"Set to 'True' to filter out internal",
|
||||
"processes started by the Linux kernel."},
|
||||
{"proc_filter_kernel",
|
||||
"(Linux) Filter kernel processes from output.",
|
||||
"",
|
||||
"Set to 'True' to filter out internal",
|
||||
"processes started by the Linux kernel."},
|
||||
}
|
||||
};
|
||||
|
||||
msgBox::msgBox() {}
|
||||
msgBox::msgBox(int width, int boxtype, vector<string> content, string title)
|
||||
: width(width), boxtype(boxtype) {
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
auto rounded = Config::getB("rounded_corners");
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
auto rounded = Config::getB("rounded_corners");
|
||||
const auto& right_up = (tty_mode or not rounded ? Symbols::right_up : Symbols::round_right_up);
|
||||
const auto& left_up = (tty_mode or not rounded ? Symbols::left_up : Symbols::round_left_up);
|
||||
const auto& right_down = (tty_mode or not rounded ? Symbols::right_down : Symbols::round_right_down);
|
||||
|
@ -786,10 +782,10 @@ namespace Menu {
|
|||
};
|
||||
|
||||
int signalChoose(const string& key) {
|
||||
auto s_pid = (Config::getB("show_detailed") and Config::getI("selected_pid") == 0 ? Config::getI("detailed_pid") : Config::getI("selected_pid"));
|
||||
static int x{}; // defaults to 0
|
||||
static int y{}; // defaults to 0
|
||||
static int selected_signal = -1;
|
||||
auto s_pid = (Config::getB("show_detailed") and Config::getI("selected_pid") == 0 ? Config::getI("detailed_pid") : Config::getI("selected_pid"));
|
||||
static int x{}; // defaults to 0
|
||||
static int y{}; // defaults to 0
|
||||
static int selected_signal = -1;
|
||||
|
||||
if (bg.empty()) selected_signal = -1;
|
||||
auto& out = Global::overlay;
|
||||
|
@ -916,7 +912,7 @@ namespace Menu {
|
|||
}
|
||||
|
||||
int signalSend(const string& key) {
|
||||
auto s_pid = (Config::getB("show_detailed") and Config::getI("selected_pid") == 0 ? Config::getI("detailed_pid") : Config::getI("selected_pid"));
|
||||
auto s_pid = (Config::getB("show_detailed") and Config::getI("selected_pid") == 0 ? Config::getI("detailed_pid") : Config::getI("selected_pid"));
|
||||
if (s_pid == 0) return Closed;
|
||||
if (redraw) {
|
||||
atomic_wait(Runner::active);
|
||||
|
@ -989,11 +985,11 @@ namespace Menu {
|
|||
|
||||
int mainMenu(const string& key) {
|
||||
enum MenuItems { Options, Help, Quit };
|
||||
static int y{}; // defaults to 0
|
||||
static int selected{}; // defaults to 0
|
||||
static int y{}; // defaults to 0
|
||||
static int selected{}; // defaults to 0
|
||||
static vector<string> colors_selected;
|
||||
static vector<string> colors_normal;
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
if (bg.empty()) selected = 0;
|
||||
int retval = Changed;
|
||||
|
||||
|
@ -1068,18 +1064,18 @@ namespace Menu {
|
|||
|
||||
int optionsMenu(const string& key) {
|
||||
enum Predispositions { isBool, isInt, isString, is2D, isBrowseable, isEditable};
|
||||
static int y{}; // defaults to 0
|
||||
static int x{}; // defaults to 0
|
||||
static int height{}; // defaults to 0
|
||||
static int page{}; // defaults to 0
|
||||
static int pages{}; // defaults to 0
|
||||
static int selected{}; // defaults to 0
|
||||
static int select_max{}; // defaults to 0
|
||||
static int item_height{}; // defaults to 0
|
||||
static int selected_cat{}; // defaults to 0
|
||||
static int max_items{}; // defaults to 0
|
||||
static int last_sel{}; // defaults to 0
|
||||
static bool editing{}; // defaults to false
|
||||
static int y{}; // defaults to 0
|
||||
static int x{}; // defaults to 0
|
||||
static int height{}; // defaults to 0
|
||||
static int page{}; // defaults to 0
|
||||
static int pages{}; // defaults to 0
|
||||
static int selected{}; // defaults to 0
|
||||
static int select_max{}; // defaults to 0
|
||||
static int item_height{}; // defaults to 0
|
||||
static int selected_cat{}; // defaults to 0
|
||||
static int max_items{}; // defaults to 0
|
||||
static int last_sel{}; // defaults to 0
|
||||
static bool editing{}; // defaults to false
|
||||
static Draw::TextEdit editor;
|
||||
static string warnings;
|
||||
static bitset<8> selPred;
|
||||
|
@ -1099,8 +1095,8 @@ namespace Menu {
|
|||
{"cpu_sensor", std::cref(Cpu::available_sensors)},
|
||||
{"selected_battery", std::cref(Config::available_batteries)},
|
||||
};
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
auto vim_keys = Config::getB("vim_keys");
|
||||
auto tty_mode = Config::getB("tty_mode");
|
||||
auto vim_keys = Config::getB("vim_keys");
|
||||
if (max_items == 0) {
|
||||
for (const auto& cat : categories) {
|
||||
if ((int)cat.size() > max_items) max_items = cat.size();
|
||||
|
@ -1112,9 +1108,9 @@ namespace Menu {
|
|||
Theme::updateThemes();
|
||||
}
|
||||
int retval = Changed;
|
||||
bool recollect{}; // defaults to false
|
||||
bool screen_redraw{}; // defaults to false
|
||||
bool theme_refresh{}; // defaults to false
|
||||
bool recollect{}; // defaults to false
|
||||
bool screen_redraw{}; // defaults to false
|
||||
bool theme_refresh{}; // defaults to false
|
||||
|
||||
//? Draw background if needed else process input
|
||||
if (redraw) {
|
||||
|
@ -1408,11 +1404,11 @@ namespace Menu {
|
|||
}
|
||||
|
||||
int helpMenu(const string& key) {
|
||||
static int y{}; // defaults to 0
|
||||
static int x{}; // defaults to 0
|
||||
static int height{}; // defaults to 0
|
||||
static int page{}; // defaults to 0
|
||||
static int pages{}; // defaults to 0
|
||||
static int y{}; // defaults to 0
|
||||
static int x{}; // defaults to 0
|
||||
static int height{}; // defaults to 0
|
||||
static int page{}; // defaults to 0
|
||||
static int pages{}; // defaults to 0
|
||||
|
||||
if (bg.empty()) page = 0;
|
||||
int retval = Changed;
|
||||
|
|
|
@ -23,7 +23,7 @@ tab-size = 4
|
|||
#include <vector>
|
||||
#include <bitset>
|
||||
|
||||
#include <btop_input.hpp>
|
||||
#include "btop_input.hpp"
|
||||
|
||||
using std::atomic;
|
||||
using std::bitset;
|
||||
|
@ -46,12 +46,12 @@ namespace Menu {
|
|||
//? Strings in content vector is not checked for box width overflow
|
||||
class msgBox {
|
||||
string box_contents, button_left, button_right;
|
||||
int height{}; // defaults to 0
|
||||
int width{}; // defaults to 0
|
||||
int boxtype{}; // defaults to 0
|
||||
int selected{}; // defaults to 0
|
||||
int x{}; // defaults to 0
|
||||
int y{}; // defaults to 0
|
||||
int height{}; // defaults to 0
|
||||
int width{}; // defaults to 0
|
||||
int boxtype{}; // defaults to 0
|
||||
int selected{}; // defaults to 0
|
||||
int x{}; // defaults to 0
|
||||
int y{}; // defaults to 0
|
||||
public:
|
||||
enum BoxTypes { OK, YES_NO, NO_YES };
|
||||
enum msgReturn {
|
||||
|
|
|
@ -18,15 +18,15 @@ tab-size = 4
|
|||
|
||||
#include <ranges>
|
||||
|
||||
#include <btop_shared.hpp>
|
||||
#include <btop_tools.hpp>
|
||||
#include "btop_shared.hpp"
|
||||
#include "btop_tools.hpp"
|
||||
|
||||
namespace rng = std::ranges;
|
||||
using namespace Tools;
|
||||
|
||||
|
||||
namespace Proc {
|
||||
void proc_sorter(vector<proc_info>& proc_vec, const string& sorting, bool reverse, bool tree) {
|
||||
void proc_sorter(vector<proc_info>& proc_vec, const string& sorting, bool reverse, bool tree) {
|
||||
if (reverse) {
|
||||
switch (v_index(sort_vector, sorting)) {
|
||||
case 0: rng::stable_sort(proc_vec, rng::less{}, &proc_info::pid); break;
|
||||
|
@ -70,7 +70,7 @@ namespace Proc {
|
|||
}
|
||||
}
|
||||
|
||||
void tree_sort(vector<tree_proc>& proc_vec, const string& sorting, bool reverse, int& c_index, const int index_max, bool collapsed) {
|
||||
void tree_sort(vector<tree_proc>& proc_vec, const string& sorting, bool reverse, int& c_index, const int index_max, bool collapsed) {
|
||||
if (proc_vec.size() > 1) {
|
||||
if (reverse) {
|
||||
switch (v_index(sort_vector, sorting)) {
|
||||
|
@ -98,8 +98,8 @@ namespace Proc {
|
|||
}
|
||||
}
|
||||
|
||||
void _tree_gen(proc_info& cur_proc, vector<proc_info>& in_procs, vector<tree_proc>& out_procs,
|
||||
int cur_depth, bool collapsed, const string& filter, bool found, bool no_update, bool should_filter) {
|
||||
void _tree_gen(proc_info& cur_proc, vector<proc_info>& in_procs, vector<tree_proc>& out_procs,
|
||||
int cur_depth, bool collapsed, const string& filter, bool found, bool no_update, bool should_filter) {
|
||||
auto cur_pos = out_procs.size();
|
||||
bool filtering = false;
|
||||
|
||||
|
@ -132,7 +132,7 @@ namespace Proc {
|
|||
std::string_view cmd_view = cur_proc.cmd;
|
||||
cmd_view = cmd_view.substr((size_t)0, std::min(cmd_view.find(' '), cmd_view.size()));
|
||||
cmd_view = cmd_view.substr(std::min(cmd_view.find_last_of('/') + 1, cmd_view.size()));
|
||||
cur_proc.short_cmd = string{cmd_view};
|
||||
cur_proc.short_cmd = string{cmd_view};
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -68,7 +68,7 @@ namespace Runner {
|
|||
extern bool pause_output;
|
||||
extern string debug_bg;
|
||||
|
||||
void run(const string& box="", bool no_update = false, bool force_redraw = false);
|
||||
void run(const string& box="", bool no_update = false, bool force_redraw = false);
|
||||
void stop();
|
||||
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ namespace Cpu {
|
|||
};
|
||||
|
||||
//* Collect cpu stats and temperatures
|
||||
auto collect(bool no_update = false) -> cpu_info&;
|
||||
auto collect(bool no_update = false) -> cpu_info&;
|
||||
|
||||
//* Draw contents of cpu box using <cpu> as source
|
||||
string draw(const cpu_info& cpu, const vector<Gpu::gpu_info>& gpu, bool force_redraw = false, bool data_same = false);
|
||||
|
@ -212,20 +212,20 @@ namespace Mem {
|
|||
extern string box;
|
||||
extern int x, y, width, height, min_width, min_height;
|
||||
extern bool has_swap, shown, redraw;
|
||||
const array mem_names { "used"s, "available"s, "cached"s, "free"s };
|
||||
const array swap_names { "swap_used"s, "swap_free"s };
|
||||
const array mem_names { "used"s, "available"s, "cached"s, "free"s };
|
||||
const array swap_names { "swap_used"s, "swap_free"s };
|
||||
extern int disk_ios;
|
||||
|
||||
struct disk_info {
|
||||
std::filesystem::path dev;
|
||||
string name;
|
||||
string fstype{}; // defaults to ""
|
||||
std::filesystem::path stat{}; // defaults to ""
|
||||
int64_t total{}; // defaults to 0
|
||||
int64_t used{}; // defaults to 0
|
||||
int64_t free{}; // defaults to 0
|
||||
int used_percent{}; // defaults to 0
|
||||
int free_percent{}; // defaults to 0
|
||||
string fstype{}; // defaults to ""
|
||||
std::filesystem::path stat{}; // defaults to ""
|
||||
int64_t total{}; // defaults to 0
|
||||
int64_t used{}; // defaults to 0
|
||||
int64_t free{}; // defaults to 0
|
||||
int used_percent{}; // defaults to 0
|
||||
int free_percent{}; // defaults to 0
|
||||
|
||||
array<int64_t, 3> old_io = {0, 0, 0};
|
||||
deque<long long> io_read = {};
|
||||
|
@ -248,10 +248,10 @@ namespace Mem {
|
|||
uint64_t get_totalMem();
|
||||
|
||||
//* Collect mem & disks stats
|
||||
auto collect(bool no_update = false) -> mem_info&;
|
||||
auto collect(bool no_update = false) -> mem_info&;
|
||||
|
||||
//* Draw contents of mem box using <mem> as source
|
||||
string draw(const mem_info& mem, bool force_redraw = false, bool data_same = false);
|
||||
string draw(const mem_info& mem, bool force_redraw = false, bool data_same = false);
|
||||
|
||||
}
|
||||
|
||||
|
@ -265,29 +265,29 @@ namespace Net {
|
|||
extern unordered_flat_map<string, uint64_t> graph_max;
|
||||
|
||||
struct net_stat {
|
||||
uint64_t speed{}; // defaults to 0
|
||||
uint64_t top{}; // defaults to 0
|
||||
uint64_t total{}; // defaults to 0
|
||||
uint64_t last{}; // defaults to 0
|
||||
uint64_t offset{}; // defaults to 0
|
||||
uint64_t rollover{}; // defaults to 0
|
||||
uint64_t speed{}; // defaults to 0
|
||||
uint64_t top{}; // defaults to 0
|
||||
uint64_t total{}; // defaults to 0
|
||||
uint64_t last{}; // defaults to 0
|
||||
uint64_t offset{}; // defaults to 0
|
||||
uint64_t rollover{}; // defaults to 0
|
||||
};
|
||||
|
||||
struct net_info {
|
||||
unordered_flat_map<string, deque<long long>> bandwidth = { {"download", {}}, {"upload", {}} };
|
||||
unordered_flat_map<string, net_stat> stat = { {"download", {}}, {"upload", {}} };
|
||||
string ipv4{}; // defaults to ""
|
||||
string ipv6{}; // defaults to ""
|
||||
bool connected{}; // defaults to false
|
||||
string ipv4{}; // defaults to ""
|
||||
string ipv6{}; // defaults to ""
|
||||
bool connected{}; // defaults to false
|
||||
};
|
||||
|
||||
extern unordered_flat_map<string, net_info> current_net;
|
||||
|
||||
//* Collect net upload/download stats
|
||||
auto collect(bool no_update=false) -> net_info&;
|
||||
auto collect(bool no_update=false) -> net_info&;
|
||||
|
||||
//* Draw contents of net box using <net> as source
|
||||
string draw(const net_info& net, bool force_redraw = false, bool data_same = false);
|
||||
string draw(const net_info& net, bool force_redraw = false, bool data_same = false);
|
||||
}
|
||||
|
||||
namespace Proc {
|
||||
|
@ -330,32 +330,32 @@ namespace Proc {
|
|||
|
||||
//* Container for process information
|
||||
struct proc_info {
|
||||
size_t pid{}; // defaults to 0
|
||||
string name{}; // defaults to ""
|
||||
string cmd{}; // defaults to ""
|
||||
string short_cmd{}; // defaults to ""
|
||||
size_t threads{}; // defaults to 0
|
||||
int name_offset{}; // defaults to 0
|
||||
string user{}; // defaults to ""
|
||||
uint64_t mem{}; // defaults to 0
|
||||
double cpu_p{}; // defaults to = 0.0
|
||||
double cpu_c{}; // defaults to = 0.0
|
||||
size_t pid{}; // defaults to 0
|
||||
string name{}; // defaults to ""
|
||||
string cmd{}; // defaults to ""
|
||||
string short_cmd{}; // defaults to ""
|
||||
size_t threads{}; // defaults to 0
|
||||
int name_offset{}; // defaults to 0
|
||||
string user{}; // defaults to ""
|
||||
uint64_t mem{}; // defaults to 0
|
||||
double cpu_p{}; // defaults to = 0.0
|
||||
double cpu_c{}; // defaults to = 0.0
|
||||
char state = '0';
|
||||
int64_t p_nice{}; // defaults to 0
|
||||
uint64_t ppid{}; // defaults to 0
|
||||
uint64_t cpu_s{}; // defaults to 0
|
||||
uint64_t cpu_t{}; // defaults to 0
|
||||
string prefix{}; // defaults to ""
|
||||
size_t depth{}; // defaults to 0
|
||||
size_t tree_index{}; // defaults to 0
|
||||
bool collapsed{}; // defaults to false
|
||||
bool filtered{}; // defaults to false
|
||||
int64_t p_nice{}; // defaults to 0
|
||||
uint64_t ppid{}; // defaults to 0
|
||||
uint64_t cpu_s{}; // defaults to 0
|
||||
uint64_t cpu_t{}; // defaults to 0
|
||||
string prefix{}; // defaults to ""
|
||||
size_t depth{}; // defaults to 0
|
||||
size_t tree_index{}; // defaults to 0
|
||||
bool collapsed{}; // defaults to false
|
||||
bool filtered{}; // defaults to false
|
||||
};
|
||||
|
||||
//* Container for process info box
|
||||
struct detail_container {
|
||||
size_t last_pid{}; // defaults to 0
|
||||
bool skip_smaps{}; // defaults to false
|
||||
size_t last_pid{}; // defaults to 0
|
||||
bool skip_smaps{}; // defaults to false
|
||||
proc_info entry;
|
||||
string elapsed, parent, status, io_read, io_write, memory;
|
||||
long long first_mem = -1;
|
||||
|
@ -367,13 +367,13 @@ namespace Proc {
|
|||
extern detail_container detailed;
|
||||
|
||||
//* Collect and sort process information from /proc
|
||||
auto collect(bool no_update = false) -> vector<proc_info>&;
|
||||
auto collect(bool no_update = false) -> vector<proc_info>&;
|
||||
|
||||
//* Update current selection and view, returns -1 if no change otherwise the current selection
|
||||
int selection(const string& cmd_key);
|
||||
|
||||
//* Draw contents of proc box using <plist> as data source
|
||||
string draw(const vector<proc_info>& plist, bool force_redraw = false, bool data_same = false);
|
||||
string draw(const vector<proc_info>& plist, bool force_redraw = false, bool data_same = false);
|
||||
|
||||
struct tree_proc {
|
||||
std::reference_wrapper<proc_info> entry;
|
||||
|
@ -381,14 +381,14 @@ namespace Proc {
|
|||
};
|
||||
|
||||
//* Sort vector of proc_info's
|
||||
void proc_sorter(vector<proc_info>& proc_vec, const string& sorting, bool reverse, bool tree = false);
|
||||
void proc_sorter(vector<proc_info>& proc_vec, const string& sorting, bool reverse, bool tree = false);
|
||||
|
||||
//* Recursive sort of process tree
|
||||
void tree_sort(vector<tree_proc>& proc_vec, const string& sorting,
|
||||
bool reverse, int& c_index, const int index_max, bool collapsed = false);
|
||||
void tree_sort(vector<tree_proc>& proc_vec, const string& sorting,
|
||||
bool reverse, int& c_index, const int index_max, bool collapsed = false);
|
||||
|
||||
//* Generate process tree list
|
||||
void _tree_gen(proc_info& cur_proc, vector<proc_info>& in_procs, vector<tree_proc>& out_procs,
|
||||
int cur_depth, bool collapsed, const string& filter,
|
||||
bool found = false, bool no_update = false, bool should_filter = false);
|
||||
void _tree_gen(proc_info& cur_proc, vector<proc_info>& in_procs, vector<tree_proc>& out_procs,
|
||||
int cur_depth, bool collapsed, const string& filter,
|
||||
bool found = false, bool no_update = false, bool should_filter = false);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -17,19 +17,13 @@ tab-size = 4
|
|||
*/
|
||||
|
||||
#include <cmath>
|
||||
#include <ranges>
|
||||
#include <fstream>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <btop_tools.hpp>
|
||||
#include <btop_config.hpp>
|
||||
#include <btop_theme.hpp>
|
||||
#include "btop_tools.hpp"
|
||||
#include "btop_config.hpp"
|
||||
#include "btop_theme.hpp"
|
||||
|
||||
using std::ceil;
|
||||
using std::clamp;
|
||||
using std::max;
|
||||
using std::min;
|
||||
using std::quoted;
|
||||
using std::round;
|
||||
using std::stoi;
|
||||
using std::to_string;
|
||||
|
@ -38,7 +32,6 @@ using std::views::iota;
|
|||
|
||||
using namespace Tools;
|
||||
|
||||
namespace rng = std::ranges;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
string Term::fg, Term::bg;
|
||||
|
@ -157,15 +150,15 @@ namespace Theme {
|
|||
}
|
||||
}
|
||||
|
||||
string hex_to_color(string hexa, bool t_to_256, const string& depth) {
|
||||
string hex_to_color(string hexa, bool t_to_256, const string& depth) {
|
||||
if (hexa.size() > 1) {
|
||||
hexa.erase(0, 1);
|
||||
for (auto& c : hexa) {
|
||||
if (not isxdigit(c)) {
|
||||
Logger::error("Invalid hex value: " + hexa);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
for (auto& c : hexa) {
|
||||
if (not isxdigit(c)) {
|
||||
Logger::error("Invalid hex value: " + hexa);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
string pre = Fx::e + (depth == "fg" ? "38" : "48") + ";" + (t_to_256 ? "5;" : "2;");
|
||||
|
||||
if (hexa.size() == 2) {
|
||||
|
@ -196,7 +189,7 @@ namespace Theme {
|
|||
return "";
|
||||
}
|
||||
|
||||
string dec_to_color(int r, int g, int b, bool t_to_256, const string& depth) {
|
||||
string dec_to_color(int r, int g, int b, bool t_to_256, const string& depth) {
|
||||
string pre = Fx::e + (depth == "fg" ? "38" : "48") + ";" + (t_to_256 ? "5;" : "2;");
|
||||
r = std::clamp(r, 0, 255);
|
||||
g = std::clamp(g, 0, 255);
|
||||
|
@ -210,17 +203,17 @@ namespace Theme {
|
|||
array<int, 3> hex_to_dec(string hexa) {
|
||||
if (hexa.size() > 1) {
|
||||
hexa.erase(0, 1);
|
||||
for (auto& c : hexa) {
|
||||
if (not isxdigit(c))
|
||||
return array{-1, -1, -1};
|
||||
}
|
||||
for (auto& c : hexa) {
|
||||
if (not isxdigit(c))
|
||||
return array{-1, -1, -1};
|
||||
}
|
||||
|
||||
if (hexa.size() == 2) {
|
||||
int h_int = stoi(hexa, nullptr, 16);
|
||||
return array{h_int, h_int, h_int};
|
||||
return array{h_int, h_int, h_int};
|
||||
}
|
||||
else if (hexa.size() == 6) {
|
||||
return array{
|
||||
return array{
|
||||
stoi(hexa.substr(0, 2), nullptr, 16),
|
||||
stoi(hexa.substr(2, 2), nullptr, 16),
|
||||
stoi(hexa.substr(4, 2), nullptr, 16)
|
||||
|
@ -234,7 +227,7 @@ namespace Theme {
|
|||
void generateColors(const unordered_flat_map<string, string>& source) {
|
||||
vector<string> t_rgb;
|
||||
string depth;
|
||||
bool t_to_256 = Config::getB("lowcolor");
|
||||
bool t_to_256 = Config::getB("lowcolor");
|
||||
colors.clear(); rgbs.clear();
|
||||
for (const auto& [name, color] : Default_theme) {
|
||||
if (name == "main_bg" and not Config::getB("theme_background")) {
|
||||
|
@ -260,11 +253,11 @@ namespace Theme {
|
|||
}
|
||||
else if (not source.at(name).empty()) {
|
||||
t_rgb = ssplit(source.at(name));
|
||||
if (t_rgb.size() != 3) {
|
||||
Logger::error("Invalid RGB decimal value: \"" + source.at(name) + "\"");
|
||||
} else {
|
||||
if (t_rgb.size() != 3) {
|
||||
Logger::error("Invalid RGB decimal value: \"" + source.at(name) + "\"");
|
||||
} else {
|
||||
colors[name] = dec_to_color(stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2]), t_to_256, depth);
|
||||
rgbs[name] = array{stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2])};
|
||||
rgbs[name] = array{stoi(t_rgb[0]), stoi(t_rgb[1]), stoi(t_rgb[2])};
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -297,16 +290,16 @@ namespace Theme {
|
|||
//* Generate color gradients from two or three colors, 101 values indexed 0-100
|
||||
void generateGradients() {
|
||||
gradients.clear();
|
||||
bool t_to_256 = Config::getB("lowcolor");
|
||||
bool t_to_256 = Config::getB("lowcolor");
|
||||
|
||||
//? Insert values for processes greyscale gradient and processes color gradient
|
||||
rgbs.insert({
|
||||
{ "proc_start", rgbs["main_fg"] },
|
||||
{ "proc_mid", {-1, -1, -1} },
|
||||
{ "proc_end", rgbs["inactive_fg"] },
|
||||
{ "proc_color_start", rgbs["inactive_fg"] },
|
||||
{ "proc_color_mid", {-1, -1, -1} },
|
||||
{ "proc_color_end", rgbs["process_start"] },
|
||||
rgbs.insert({
|
||||
{ "proc_start", rgbs["main_fg"] },
|
||||
{ "proc_mid", {-1, -1, -1} },
|
||||
{ "proc_end", rgbs["inactive_fg"] },
|
||||
{ "proc_color_start", rgbs["inactive_fg"] },
|
||||
{ "proc_color_mid", {-1, -1, -1} },
|
||||
{ "proc_color_end", rgbs["process_start"] },
|
||||
});
|
||||
|
||||
for (const auto& [name, source_arr] : rgbs) {
|
||||
|
@ -329,10 +322,10 @@ namespace Theme {
|
|||
|
||||
//? Split iteration in two passes of 50 + 51 instead of one pass of 101 if gradient has start, mid and end values defined
|
||||
int current_range = (input_colors[1][0] >= 0) ? 50 : 100;
|
||||
for (int rgb : iota(0, 3)) {
|
||||
for (int rgb : iota(0, 3)) {
|
||||
int start = 0, offset = 0;
|
||||
int end = (current_range == 50) ? 1 : 2;
|
||||
for (int i : iota(0, 101)) {
|
||||
for (int i : iota(0, 101)) {
|
||||
output_colors[i][rgb] = input_colors[start][rgb] + (i - offset) * (input_colors[end][rgb] - input_colors[start][rgb]) / current_range;
|
||||
|
||||
//? Switch source arrays from start->mid to mid->end at 50 passes if mid is defined
|
||||
|
@ -367,7 +360,7 @@ namespace Theme {
|
|||
const string base_name = rtrim(c.first, "_start");
|
||||
string section = "_start";
|
||||
int split = colors.at(base_name + "_mid").empty() ? 50 : 33;
|
||||
for (int i : iota(0, 101)) {
|
||||
for (int i : iota(0, 101)) {
|
||||
gradients[base_name][i] = colors.at(base_name + section);
|
||||
if (i == split) {
|
||||
section = (split == 33) ? "_mid" : "_end";
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -40,13 +40,13 @@ namespace Theme {
|
|||
//* Args hexa: ["#000000"-"#ffffff"] for color, ["#00"-"#ff"] for greyscale
|
||||
//* t_to_256: [true|false] convert 24bit value to 256 color value
|
||||
//* depth: ["fg"|"bg"] for either a foreground color or a background color
|
||||
string hex_to_color(string hexa, bool t_to_256=false, const string& depth="fg");
|
||||
string hex_to_color(string hexa, bool t_to_256=false, const string& depth="fg");
|
||||
|
||||
//* Generate escape sequence for 24-bit or 256 color and return as a string
|
||||
//* Args r: [0-255], g: [0-255], b: [0-255]
|
||||
//* t_to_256: [true|false] convert 24bit value to 256 color value
|
||||
//* depth: ["fg"|"bg"] for either a foreground color or a background color
|
||||
string dec_to_color(int r, int g, int b, bool t_to_256=false, const string& depth="fg");
|
||||
string dec_to_color(int r, int g, int b, bool t_to_256=false, const string& depth="fg");
|
||||
|
||||
//* Update list of paths for available themes
|
||||
void updateThemes();
|
||||
|
|
|
@ -32,9 +32,9 @@ tab-size = 4
|
|||
|
||||
#include "robin_hood.h"
|
||||
#include "widechar_width.hpp"
|
||||
#include <btop_shared.hpp>
|
||||
#include <btop_tools.hpp>
|
||||
#include <btop_config.hpp>
|
||||
#include "btop_shared.hpp"
|
||||
#include "btop_tools.hpp"
|
||||
#include "btop_config.hpp"
|
||||
|
||||
using std::cin;
|
||||
using std::cout;
|
||||
|
@ -55,9 +55,9 @@ namespace rng = std::ranges;
|
|||
//* Collection of escape codes and functions for terminal manipulation
|
||||
namespace Term {
|
||||
|
||||
atomic<bool> initialized{}; // defaults to false
|
||||
atomic<int> width{}; // defaults to 0
|
||||
atomic<int> height{}; // defaults to 0
|
||||
atomic<bool> initialized{}; // defaults to false
|
||||
atomic<int> width{}; // defaults to 0
|
||||
atomic<int> height{}; // defaults to 0
|
||||
string current_tty;
|
||||
|
||||
namespace {
|
||||
|
@ -80,7 +80,7 @@ namespace Term {
|
|||
else settings.c_lflag &= ~(ICANON);
|
||||
if (tcsetattr(STDIN_FILENO, TCSANOW, &settings)) return false;
|
||||
if (on) setlinebuf(stdin);
|
||||
else setbuf(stdin, NULL);
|
||||
else setbuf(stdin, nullptr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -127,15 +127,15 @@ namespace Term {
|
|||
initialized = (bool)isatty(STDIN_FILENO);
|
||||
if (initialized) {
|
||||
tcgetattr(STDIN_FILENO, &initial_settings);
|
||||
current_tty = (ttyname(STDIN_FILENO) != NULL ? static_cast<string>(ttyname(STDIN_FILENO)) : "unknown");
|
||||
current_tty = (ttyname(STDIN_FILENO) != nullptr ? static_cast<string>(ttyname(STDIN_FILENO)) : "unknown");
|
||||
|
||||
//? Disable stream sync
|
||||
cin.sync_with_stdio(false);
|
||||
cout.sync_with_stdio(false);
|
||||
|
||||
//? Disable stream ties
|
||||
cin.tie(NULL);
|
||||
cout.tie(NULL);
|
||||
cin.tie(nullptr);
|
||||
cout.tie(nullptr);
|
||||
echo(false);
|
||||
linebuffered(false);
|
||||
refresh();
|
||||
|
@ -212,9 +212,9 @@ namespace Tools {
|
|||
return chars;
|
||||
}
|
||||
|
||||
string uresize(string str, const size_t len, bool wide) {
|
||||
if (len < 1 or str.empty())
|
||||
return "";
|
||||
string uresize(string str, const size_t len, bool wide) {
|
||||
if (len < 1 or str.empty())
|
||||
return "";
|
||||
|
||||
if (wide) {
|
||||
try {
|
||||
|
@ -242,9 +242,9 @@ namespace Tools {
|
|||
return str;
|
||||
}
|
||||
|
||||
string luresize(string str, const size_t len, bool wide) {
|
||||
if (len < 1 or str.empty())
|
||||
return "";
|
||||
string luresize(string str, const size_t len, bool wide) {
|
||||
if (len < 1 or str.empty())
|
||||
return "";
|
||||
|
||||
for (size_t x = 0, last_pos = 0, i = str.size() - 1; i > 0 ; i--) {
|
||||
if (wide and static_cast<unsigned char>(str.at(i)) > 0xef) {
|
||||
|
@ -273,19 +273,19 @@ namespace Tools {
|
|||
}
|
||||
|
||||
string ltrim(const string& str, const string& t_str) {
|
||||
std::string_view str_v{str};
|
||||
while (str_v.starts_with(t_str))
|
||||
str_v.remove_prefix(t_str.size());
|
||||
std::string_view str_v{str};
|
||||
while (str_v.starts_with(t_str))
|
||||
str_v.remove_prefix(t_str.size());
|
||||
|
||||
return string{str_v};
|
||||
return string{str_v};
|
||||
}
|
||||
|
||||
string rtrim(const string& str, const string& t_str) {
|
||||
std::string_view str_v{str};
|
||||
while (str_v.ends_with(t_str))
|
||||
str_v.remove_suffix(t_str.size());
|
||||
std::string_view str_v{str};
|
||||
while (str_v.ends_with(t_str))
|
||||
str_v.remove_suffix(t_str.size());
|
||||
|
||||
return string{str_v};
|
||||
return string{str_v};
|
||||
}
|
||||
|
||||
auto ssplit(const string& str, const char& delim) -> vector<string> {
|
||||
|
@ -299,56 +299,56 @@ namespace Tools {
|
|||
return out;
|
||||
}
|
||||
|
||||
string ljust(string str, const size_t x, bool utf, bool wide, bool limit) {
|
||||
string ljust(string str, const size_t x, bool utf, bool wide, bool limit) {
|
||||
if (utf) {
|
||||
if (limit and ulen(str, wide) > x)
|
||||
return uresize(str, x, wide);
|
||||
if (limit and ulen(str, wide) > x)
|
||||
return uresize(str, x, wide);
|
||||
|
||||
return str + string(max((int)(x - ulen(str)), 0), ' ');
|
||||
}
|
||||
else {
|
||||
if (limit and str.size() > x) {
|
||||
str.resize(x);
|
||||
return str;
|
||||
}
|
||||
if (limit and str.size() > x) {
|
||||
str.resize(x);
|
||||
return str;
|
||||
}
|
||||
return str + string(max((int)(x - str.size()), 0), ' ');
|
||||
}
|
||||
}
|
||||
|
||||
string rjust(string str, const size_t x, bool utf, bool wide, bool limit) {
|
||||
string rjust(string str, const size_t x, bool utf, bool wide, bool limit) {
|
||||
if (utf) {
|
||||
if (limit and ulen(str, wide) > x)
|
||||
return uresize(str, x, wide);
|
||||
if (limit and ulen(str, wide) > x)
|
||||
return uresize(str, x, wide);
|
||||
|
||||
return string(max((int)(x - ulen(str)), 0), ' ') + str;
|
||||
}
|
||||
else {
|
||||
if (limit and str.size() > x) {
|
||||
str.resize(x);
|
||||
return str;
|
||||
};
|
||||
if (limit and str.size() > x) {
|
||||
str.resize(x);
|
||||
return str;
|
||||
};
|
||||
return string(max((int)(x - str.size()), 0), ' ') + str;
|
||||
}
|
||||
}
|
||||
|
||||
string cjust(string str, const size_t x, bool utf, bool wide, bool limit) {
|
||||
string cjust(string str, const size_t x, bool utf, bool wide, bool limit) {
|
||||
if (utf) {
|
||||
if (limit and ulen(str, wide) > x)
|
||||
return uresize(str, x, wide);
|
||||
if (limit and ulen(str, wide) > x)
|
||||
return uresize(str, x, wide);
|
||||
|
||||
return string(max((int)ceil((double)(x - ulen(str)) / 2), 0), ' ') + str + string(max((int)floor((double)(x - ulen(str)) / 2), 0), ' ');
|
||||
}
|
||||
else {
|
||||
if (limit and str.size() > x) {
|
||||
str.resize(x);
|
||||
return str;
|
||||
}
|
||||
if (limit and str.size() > x) {
|
||||
str.resize(x);
|
||||
return str;
|
||||
}
|
||||
return string(max((int)ceil((double)(x - str.size()) / 2), 0), ' ') + str + string(max((int)floor((double)(x - str.size()) / 2), 0), ' ');
|
||||
}
|
||||
}
|
||||
|
||||
string trans(const string& str) {
|
||||
std::string_view oldstr{str};
|
||||
std::string_view oldstr{str};
|
||||
string newstr;
|
||||
newstr.reserve(str.size());
|
||||
for (size_t pos; (pos = oldstr.find(' ')) != string::npos;) {
|
||||
|
@ -358,7 +358,7 @@ namespace Tools {
|
|||
newstr.append(Mv::r(x));
|
||||
oldstr.remove_prefix(pos + x);
|
||||
}
|
||||
return (newstr.empty()) ? str : newstr + string{oldstr};
|
||||
return (newstr.empty()) ? str : newstr + string{oldstr};
|
||||
}
|
||||
|
||||
string sec_to_dhms(size_t seconds, bool no_days, bool no_seconds) {
|
||||
|
@ -372,37 +372,37 @@ namespace Tools {
|
|||
return out;
|
||||
}
|
||||
|
||||
string floating_humanizer(uint64_t value, bool shorten, size_t start, bool bit, bool per_second) {
|
||||
string floating_humanizer(uint64_t value, bool shorten, size_t start, bool bit, bool per_second) {
|
||||
string out;
|
||||
const size_t mult = (bit) ? 8 : 1;
|
||||
bool mega = Config::getB("base_10_sizes");
|
||||
bool mega = Config::getB("base_10_sizes");
|
||||
|
||||
// taking advantage of type deduction for array creation (since C++17)
|
||||
// combined with string literals (operator""s)
|
||||
static const array mebiUnits_bit {
|
||||
"bit"s, "Kib"s, "Mib"s,
|
||||
"Gib"s, "Tib"s, "Pib"s,
|
||||
"Eib"s, "Zib"s, "Yib"s,
|
||||
"Bib"s, "GEb"s
|
||||
};
|
||||
static const array mebiUnits_byte {
|
||||
"Byte"s, "KiB"s, "MiB"s,
|
||||
"GiB"s, "TiB"s, "PiB"s,
|
||||
"EiB"s, "ZiB"s, "YiB"s,
|
||||
"BiB"s, "GEB"s
|
||||
};
|
||||
static const array megaUnits_bit {
|
||||
"bit"s, "Kb"s, "Mb"s,
|
||||
"Gb"s, "Tb"s, "Pb"s,
|
||||
"Eb"s, "Zb"s, "Yb"s,
|
||||
"Bb"s, "Gb"s
|
||||
};
|
||||
static const array megaUnits_byte {
|
||||
"Byte"s, "KB"s, "MB"s,
|
||||
"GB"s, "TB"s, "PB"s,
|
||||
"EB"s, "ZB"s, "YB"s,
|
||||
"BB"s, "GB"s
|
||||
};
|
||||
// taking advantage of type deduction for array creation (since C++17)
|
||||
// combined with string literals (operator""s)
|
||||
static const array mebiUnits_bit {
|
||||
"bit"s, "Kib"s, "Mib"s,
|
||||
"Gib"s, "Tib"s, "Pib"s,
|
||||
"Eib"s, "Zib"s, "Yib"s,
|
||||
"Bib"s, "GEb"s
|
||||
};
|
||||
static const array mebiUnits_byte {
|
||||
"Byte"s, "KiB"s, "MiB"s,
|
||||
"GiB"s, "TiB"s, "PiB"s,
|
||||
"EiB"s, "ZiB"s, "YiB"s,
|
||||
"BiB"s, "GEB"s
|
||||
};
|
||||
static const array megaUnits_bit {
|
||||
"bit"s, "Kb"s, "Mb"s,
|
||||
"Gb"s, "Tb"s, "Pb"s,
|
||||
"Eb"s, "Zb"s, "Yb"s,
|
||||
"Bb"s, "Gb"s
|
||||
};
|
||||
static const array megaUnits_byte {
|
||||
"Byte"s, "KB"s, "MB"s,
|
||||
"GB"s, "TB"s, "PB"s,
|
||||
"EB"s, "ZB"s, "YB"s,
|
||||
"BB"s, "GB"s
|
||||
};
|
||||
const auto& units = (bit) ? ( mega ? megaUnits_bit : mebiUnits_bit) : ( mega ? megaUnits_byte : mebiUnits_byte);
|
||||
|
||||
value *= 100 * mult;
|
||||
|
@ -429,29 +429,29 @@ namespace Tools {
|
|||
}
|
||||
if (out.empty()) {
|
||||
out = to_string(value);
|
||||
if (not mega and out.size() == 4 and start > 0) {
|
||||
out.pop_back();
|
||||
out.insert(2, ".");
|
||||
}
|
||||
else if (out.size() == 3 and start > 0) {
|
||||
out.insert(1, ".");
|
||||
}
|
||||
else if (out.size() >= 2) {
|
||||
out.resize(out.size() - 2);
|
||||
}
|
||||
if (not mega and out.size() == 4 and start > 0) {
|
||||
out.pop_back();
|
||||
out.insert(2, ".");
|
||||
}
|
||||
else if (out.size() == 3 and start > 0) {
|
||||
out.insert(1, ".");
|
||||
}
|
||||
else if (out.size() >= 2) {
|
||||
out.resize(out.size() - 2);
|
||||
}
|
||||
}
|
||||
if (shorten) {
|
||||
auto f_pos = out.find('.');
|
||||
if (f_pos == 1 and out.size() > 3) {
|
||||
out = to_string(round(stof(out) * 10) / 10).substr(0,3);
|
||||
}
|
||||
else if (f_pos != string::npos) {
|
||||
out = to_string((int)round(stof(out)));
|
||||
}
|
||||
if (out.size() > 3) {
|
||||
out = to_string((int)(out[0] - '0') + 1);
|
||||
start++;
|
||||
}
|
||||
if (f_pos == 1 and out.size() > 3) {
|
||||
out = to_string(round(stod(out) * 10) / 10).substr(0,3);
|
||||
}
|
||||
else if (f_pos != string::npos) {
|
||||
out = to_string((int)round(stod(out)));
|
||||
}
|
||||
if (out.size() > 3) {
|
||||
out = to_string((int)(out[0] - '0') + 1);
|
||||
start++;
|
||||
}
|
||||
out.push_back(units[start][0]);
|
||||
}
|
||||
else out += " " + units[start];
|
||||
|
@ -461,18 +461,18 @@ namespace Tools {
|
|||
}
|
||||
|
||||
std::string operator*(const string& str, int64_t n) {
|
||||
if (n < 1 or str.empty()) {
|
||||
return "";
|
||||
}
|
||||
else if (n == 1) {
|
||||
return str;
|
||||
}
|
||||
if (n < 1 or str.empty()) {
|
||||
return "";
|
||||
}
|
||||
else if (n == 1) {
|
||||
return str;
|
||||
}
|
||||
|
||||
string new_str;
|
||||
new_str.reserve(str.size() * n);
|
||||
|
||||
for (; n > 0; n--)
|
||||
new_str.append(str);
|
||||
for (; n > 0; n--)
|
||||
new_str.append(str);
|
||||
|
||||
return new_str;
|
||||
}
|
||||
|
@ -485,17 +485,17 @@ namespace Tools {
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
void atomic_wait(const atomic<bool>& atom, bool old) noexcept {
|
||||
void atomic_wait(const atomic<bool>& atom, bool old) noexcept {
|
||||
while (atom.load(std::memory_order_relaxed) == old ) busy_wait();
|
||||
}
|
||||
|
||||
void atomic_wait_for(const atomic<bool>& atom, bool old, const uint64_t wait_ms) noexcept {
|
||||
void atomic_wait_for(const atomic<bool>& atom, bool old, const uint64_t wait_ms) noexcept {
|
||||
const uint64_t start_time = time_ms();
|
||||
while (atom.load(std::memory_order_relaxed) == old and (time_ms() - start_time < wait_ms)) sleep_ms(1);
|
||||
}
|
||||
|
||||
atomic_lock::atomic_lock(atomic<bool>& atom, bool wait) : atom(atom) {
|
||||
if (wait) while (not this->atom.compare_exchange_strong(this->not_true, true));
|
||||
if (wait) while (not this->atom.compare_exchange_strong(this->not_true, true));
|
||||
else this->atom.store(true);
|
||||
}
|
||||
|
||||
|
@ -511,7 +511,7 @@ namespace Tools {
|
|||
for (string readstr; getline(file, readstr); out += readstr);
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Logger::error("readfile() : Exception when reading " + string{path} + " : " + e.what());
|
||||
Logger::error("readfile() : Exception when reading " + string{path} + " : " + e.what());
|
||||
return fallback;
|
||||
}
|
||||
return (out.empty() ? fallback : out);
|
||||
|
@ -532,13 +532,13 @@ namespace Tools {
|
|||
string hostname() {
|
||||
char host[HOST_NAME_MAX];
|
||||
gethostname(host, HOST_NAME_MAX);
|
||||
return string{host};
|
||||
return string{host};
|
||||
}
|
||||
|
||||
string username() {
|
||||
auto user = getenv("LOGNAME");
|
||||
if (user == NULL or strlen(user) == 0) user = getenv("USER");
|
||||
return (user != NULL ? user : "");
|
||||
if (user == nullptr or strlen(user) == 0) user = getenv("USER");
|
||||
return (user != nullptr ? user : "");
|
||||
}
|
||||
|
||||
DebugTimer::DebugTimer(const string name, bool start, bool delayed_report) : name(name), delayed_report(delayed_report) {
|
||||
|
@ -625,14 +625,14 @@ namespace Logger {
|
|||
int status = -1;
|
||||
public:
|
||||
lose_priv() {
|
||||
if (geteuid() != Global::real_uid) {
|
||||
this->status = seteuid(Global::real_uid);
|
||||
}
|
||||
if (geteuid() != Global::real_uid) {
|
||||
this->status = seteuid(Global::real_uid);
|
||||
}
|
||||
}
|
||||
~lose_priv() {
|
||||
if (status == 0) {
|
||||
status = seteuid(Global::set_uid);
|
||||
}
|
||||
if (status == 0) {
|
||||
status = seteuid(Global::set_uid);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -650,25 +650,25 @@ namespace Logger {
|
|||
auto old_log = logfile;
|
||||
old_log += ".1";
|
||||
|
||||
if (fs::exists(old_log))
|
||||
fs::remove(old_log, ec);
|
||||
if (fs::exists(old_log))
|
||||
fs::remove(old_log, ec);
|
||||
|
||||
if (not ec)
|
||||
fs::rename(logfile, old_log, ec);
|
||||
if (not ec)
|
||||
fs::rename(logfile, old_log, ec);
|
||||
}
|
||||
if (not ec) {
|
||||
std::ofstream lwrite(logfile, std::ios::app);
|
||||
if (first) {
|
||||
first = false;
|
||||
lwrite << "\n" << strf_time(tdf) << "===> btop++ v." << Global::Version << "\n";
|
||||
}
|
||||
if (first) {
|
||||
first = false;
|
||||
lwrite << "\n" << strf_time(tdf) << "===> btop++ v." << Global::Version << "\n";
|
||||
}
|
||||
lwrite << strf_time(tdf) << log_levels.at(level) << ": " << msg << "\n";
|
||||
}
|
||||
else logfile.clear();
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
logfile.clear();
|
||||
throw std::runtime_error("Exception in Logger::log_write() : " + string{e.what()});
|
||||
throw std::runtime_error("Exception in Logger::log_write() : " + string{e.what()});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,10 +39,10 @@ tab-size = 4
|
|||
#endif
|
||||
#endif
|
||||
#define FMT_HEADER_ONLY
|
||||
#include <fmt/core.h>
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/ostream.h>
|
||||
#include <fmt/ranges.h>
|
||||
#include "fmt/core.h"
|
||||
#include "fmt/format.h"
|
||||
#include "fmt/ostream.h"
|
||||
#include "fmt/ranges.h"
|
||||
|
||||
using std::array;
|
||||
using std::atomic;
|
||||
|
@ -50,7 +50,6 @@ using std::string;
|
|||
using std::to_string;
|
||||
using std::tuple;
|
||||
using std::vector;
|
||||
using namespace fmt;
|
||||
using namespace fmt::literals;
|
||||
|
||||
//? ------------------------------------------------- NAMESPACES ------------------------------------------------------
|
||||
|
@ -92,19 +91,19 @@ namespace Fx {
|
|||
//* Collection of escape codes and functions for cursor manipulation
|
||||
namespace Mv {
|
||||
//* Move cursor to <line>, <column>
|
||||
inline string to(int line, int col) { return Fx::e + to_string(line) + ';' + to_string(col) + 'f'; }
|
||||
inline string to(int line, int col) { return Fx::e + to_string(line) + ';' + to_string(col) + 'f'; }
|
||||
|
||||
//* Move cursor right <x> columns
|
||||
inline string r(int x) { return Fx::e + to_string(x) + 'C'; }
|
||||
inline string r(int x) { return Fx::e + to_string(x) + 'C'; }
|
||||
|
||||
//* Move cursor left <x> columns
|
||||
inline string l(int x) { return Fx::e + to_string(x) + 'D'; }
|
||||
inline string l(int x) { return Fx::e + to_string(x) + 'D'; }
|
||||
|
||||
//* Move cursor up x lines
|
||||
inline string u(int x) { return Fx::e + to_string(x) + 'A'; }
|
||||
inline string u(int x) { return Fx::e + to_string(x) + 'A'; }
|
||||
|
||||
//* Move cursor down x lines
|
||||
inline string d(int x) { return Fx::e + to_string(x) + 'B'; }
|
||||
inline string d(int x) { return Fx::e + to_string(x) + 'B'; }
|
||||
|
||||
//* Save cursor position
|
||||
const string save = Fx::e + "s";
|
||||
|
@ -162,15 +161,15 @@ namespace Tools {
|
|||
size_t wide_ulen(const std::wstring& w_str);
|
||||
|
||||
//* Return number of UTF8 characters in a string (wide=true for column size needed on terminal)
|
||||
inline size_t ulen(const string& str, bool wide = false) {
|
||||
inline size_t ulen(const string& str, bool wide = false) {
|
||||
return (wide ? wide_ulen(str) : std::ranges::count_if(str, [](char c) { return (static_cast<unsigned char>(c) & 0xC0) != 0x80; }));
|
||||
}
|
||||
|
||||
//* Resize a string consisting of UTF8 characters (only reduces size)
|
||||
string uresize(const string str, const size_t len, bool wide = false);
|
||||
string uresize(const string str, const size_t len, bool wide = false);
|
||||
|
||||
//* Resize a string consisting of UTF8 characters from left (only reduces size)
|
||||
string luresize(const string str, const size_t len, bool wide = false);
|
||||
string luresize(const string str, const size_t len, bool wide = false);
|
||||
|
||||
//* Replace <from> in <str> with <to> and return new string
|
||||
string s_replace(const string& str, const string& from, const string& to);
|
||||
|
@ -207,11 +206,11 @@ namespace Tools {
|
|||
|
||||
//* Check if string <str> contains string <find_val>, while ignoring case
|
||||
inline bool s_contains_ic(const string& str, const string& find_val) {
|
||||
auto it = std::search(
|
||||
str.begin(), str.end(),
|
||||
find_val.begin(), find_val.end(),
|
||||
[](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); }
|
||||
);
|
||||
auto it = std::search(
|
||||
str.begin(), str.end(),
|
||||
find_val.begin(), find_val.end(),
|
||||
[](char ch1, char ch2) { return std::toupper(ch1) == std::toupper(ch2); }
|
||||
);
|
||||
return it != str.end();
|
||||
}
|
||||
|
||||
|
@ -272,35 +271,35 @@ namespace Tools {
|
|||
auto ssplit(const string& str, const char& delim = ' ') -> vector<string>;
|
||||
|
||||
//* Put current thread to sleep for <ms> milliseconds
|
||||
inline void sleep_ms(const size_t& ms) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
|
||||
}
|
||||
inline void sleep_ms(const size_t& ms) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
|
||||
}
|
||||
|
||||
//* Put current thread to sleep for <micros> microseconds
|
||||
inline void sleep_micros(const size_t& micros) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(micros));
|
||||
}
|
||||
inline void sleep_micros(const size_t& micros) {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(micros));
|
||||
}
|
||||
|
||||
//* Left justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
|
||||
string ljust(string str, const size_t x, bool utf = false, bool wide = false, bool limit = true);
|
||||
string ljust(string str, const size_t x, bool utf = false, bool wide = false, bool limit = true);
|
||||
|
||||
//* Right justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
|
||||
string rjust(string str, const size_t x, bool utf = false, bool wide = false, bool limit = true);
|
||||
string rjust(string str, const size_t x, bool utf = false, bool wide = false, bool limit = true);
|
||||
|
||||
//* Center justify string <str> if <x> is greater than <str> length, limit return size to <x> by default
|
||||
string cjust(string str, const size_t x, bool utf = false, bool wide = false, bool limit = true);
|
||||
string cjust(string str, const size_t x, bool utf = false, bool wide = false, bool limit = true);
|
||||
|
||||
//* Replace whitespaces " " with escape code for move right
|
||||
string trans(const string& str);
|
||||
|
||||
//* Convert seconds to format "<days>d <hours>:<minutes>:<seconds>" and return string
|
||||
string sec_to_dhms(size_t seconds, bool no_days = false, bool no_seconds = false);
|
||||
string sec_to_dhms(size_t seconds, bool no_days = false, bool no_seconds = false);
|
||||
|
||||
//* Scales up in steps of 1024 to highest positive value unit and returns string with unit suffixed
|
||||
//* bit=True or defaults to bytes
|
||||
//* start=int to set 1024 multiplier starting unit
|
||||
//* short=True always returns 0 decimals and shortens unit to 1 character
|
||||
string floating_humanizer(uint64_t value, bool shorten = false, size_t start = 0, bool bit = false, bool per_second = false);
|
||||
string floating_humanizer(uint64_t value, bool shorten = false, size_t start = 0, bool bit = false, bool per_second = false);
|
||||
|
||||
//* Add std::string operator * : Repeat string <str> <n> number of times
|
||||
std::string operator*(const string& str, int64_t n);
|
||||
|
@ -323,21 +322,21 @@ namespace Tools {
|
|||
#endif
|
||||
}
|
||||
|
||||
void atomic_wait(const atomic<bool>& atom, bool old = true) noexcept;
|
||||
void atomic_wait(const atomic<bool>& atom, bool old = true) noexcept;
|
||||
|
||||
void atomic_wait_for(const atomic<bool>& atom, bool old = true, const uint64_t wait_ms = 0) noexcept;
|
||||
void atomic_wait_for(const atomic<bool>& atom, bool old = true, const uint64_t wait_ms = 0) noexcept;
|
||||
|
||||
//* Sets atomic<bool> to true on construct, sets to false on destruct
|
||||
class atomic_lock {
|
||||
atomic<bool>& atom;
|
||||
bool not_true{}; // defaults to false
|
||||
bool not_true{}; // defaults to false
|
||||
public:
|
||||
atomic_lock(atomic<bool>& atom, bool wait = false);
|
||||
atomic_lock(atomic<bool>& atom, bool wait = false);
|
||||
~atomic_lock();
|
||||
};
|
||||
|
||||
//* Read a complete file and return as a string
|
||||
string readfile(const std::filesystem::path& path, const string& fallback = "");
|
||||
string readfile(const std::filesystem::path& path, const string& fallback = "");
|
||||
|
||||
//* Convert a celsius value to celsius, fahrenheit, kelvin or rankin and return tuple with new value and unit.
|
||||
auto celsius_to(const long long& celsius, const string& scale) -> tuple<long long, string>;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -59,9 +59,9 @@ tab-size = 4
|
|||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include <btop_config.hpp>
|
||||
#include <btop_shared.hpp>
|
||||
#include <btop_tools.hpp>
|
||||
#include "../btop_config.hpp"
|
||||
#include "../btop_shared.hpp"
|
||||
#include "../btop_tools.hpp"
|
||||
|
||||
using std::clamp, std::string_literals::operator""s, std::cmp_equal, std::cmp_less, std::cmp_greater;
|
||||
using std::ifstream, std::numeric_limits, std::streamsize, std::round, std::max, std::min;
|
||||
|
@ -120,12 +120,12 @@ namespace Shared {
|
|||
//? Shared global variables init
|
||||
int mib[2];
|
||||
mib[0] = CTL_HW;
|
||||
mib[1] = HW_NCPU;
|
||||
mib[1] = HW_NCPU;
|
||||
int ncpu;
|
||||
size_t len = sizeof(ncpu);
|
||||
if (sysctl(mib, 2, &ncpu, &len, NULL, 0) == -1) {
|
||||
size_t len = sizeof(ncpu);
|
||||
if (sysctl(mib, 2, &ncpu, &len, nullptr, 0) == -1) {
|
||||
Logger::warning("Could not determine number of cores, defaulting to 1.");
|
||||
} else {
|
||||
} else {
|
||||
coreCount = ncpu;
|
||||
}
|
||||
|
||||
|
@ -143,21 +143,21 @@ namespace Shared {
|
|||
|
||||
int64_t memsize = 0;
|
||||
size_t size = sizeof(memsize);
|
||||
if (sysctlbyname("hw.physmem", &memsize, &size, NULL, 0) < 0) {
|
||||
if (sysctlbyname("hw.physmem", &memsize, &size, nullptr, 0) < 0) {
|
||||
Logger::warning("Could not get memory size");
|
||||
}
|
||||
totalMem = memsize;
|
||||
|
||||
struct timeval result;
|
||||
size = sizeof(result);
|
||||
if (sysctlbyname("kern.boottime", &result, &size, NULL, 0) < 0) {
|
||||
if (sysctlbyname("kern.boottime", &result, &size, nullptr, 0) < 0) {
|
||||
Logger::warning("Could not get boot time");
|
||||
} else {
|
||||
bootTime = result.tv_sec;
|
||||
}
|
||||
|
||||
size = sizeof(kfscale);
|
||||
if (sysctlbyname("kern.fscale", &kfscale, &size, NULL, 0) == -1) {
|
||||
if (sysctlbyname("kern.fscale", &kfscale, &size, nullptr, 0) == -1) {
|
||||
kfscale = 2048;
|
||||
}
|
||||
|
||||
|
@ -185,7 +185,7 @@ namespace Shared {
|
|||
|
||||
//* RAII wrapper for kvm_openfiles
|
||||
class kvm_openfiles_wrapper {
|
||||
kvm_t* kd = NULL;
|
||||
kvm_t* kd = nullptr;
|
||||
public:
|
||||
kvm_openfiles_wrapper(const char* execf, const char* coref, const char* swapf, int flags, char* err) {
|
||||
this->kd = kvm_openfiles(execf, coref, swapf, flags, err);
|
||||
|
@ -205,19 +205,19 @@ namespace Cpu {
|
|||
const array<string, 10> time_names = {"user", "nice", "system", "idle"};
|
||||
|
||||
unordered_flat_map<string, long long> cpu_old = {
|
||||
{"totals", 0},
|
||||
{"idles", 0},
|
||||
{"user", 0},
|
||||
{"nice", 0},
|
||||
{"system", 0},
|
||||
{"idle", 0}
|
||||
{"totals", 0},
|
||||
{"idles", 0},
|
||||
{"user", 0},
|
||||
{"nice", 0},
|
||||
{"system", 0},
|
||||
{"idle", 0}
|
||||
};
|
||||
|
||||
string get_cpuName() {
|
||||
string name;
|
||||
char buffer[1024];
|
||||
size_t size = sizeof(buffer);
|
||||
if (sysctlbyname("hw.model", &buffer, &size, NULL, 0) < 0) {
|
||||
if (sysctlbyname("hw.model", &buffer, &size, nullptr, 0) < 0) {
|
||||
Logger::error("Failed to get CPU name");
|
||||
return name;
|
||||
}
|
||||
|
@ -264,13 +264,13 @@ namespace Cpu {
|
|||
if (Config::getB("show_coretemp") and Config::getB("check_temp")) {
|
||||
int32_t temp;
|
||||
size_t size = sizeof(temp);
|
||||
if (sysctlbyname("dev.cpu.0.temperature", &temp, &size, NULL, 0) < 0) {
|
||||
if (sysctlbyname("dev.cpu.0.temperature", &temp, &size, nullptr, 0) < 0) {
|
||||
Logger::warning("Could not get temp sensor - maybe you need to load the coretemp module");
|
||||
} else {
|
||||
got_sensors = true;
|
||||
int temp;
|
||||
size_t size = sizeof(temp);
|
||||
sysctlbyname("dev.cpu.0.coretemp.tjmax", &temp, &size, NULL, 0); //asuming the max temp is same for all cores
|
||||
sysctlbyname("dev.cpu.0.coretemp.tjmax", &temp, &size, nullptr, 0); //asuming the max temp is same for all cores
|
||||
temp = (temp - 2732) / 10; // since it's an int, it's multiplied by 10, and offset to absolute zero...
|
||||
current_cpu.temp_max = temp;
|
||||
}
|
||||
|
@ -284,7 +284,7 @@ namespace Cpu {
|
|||
int found = 0;
|
||||
bool got_package = false;
|
||||
size_t size = sizeof(p_temp);
|
||||
if (sysctlbyname("hw.acpi.thermal.tz0.temperature", &p_temp, &size, NULL, 0) >= 0) {
|
||||
if (sysctlbyname("hw.acpi.thermal.tz0.temperature", &p_temp, &size, nullptr, 0) >= 0) {
|
||||
got_package = true;
|
||||
p_temp = (p_temp - 2732) / 10; // since it's an int, it's multiplied by 10, and offset to absolute zero...
|
||||
}
|
||||
|
@ -292,7 +292,7 @@ namespace Cpu {
|
|||
size = sizeof(temp);
|
||||
for (int i = 0; i < Shared::coreCount; i++) {
|
||||
string s = "dev.cpu." + std::to_string(i) + ".temperature";
|
||||
if (sysctlbyname(s.c_str(), &temp, &size, NULL, 0) >= 0) {
|
||||
if (sysctlbyname(s.c_str(), &temp, &size, nullptr, 0) >= 0) {
|
||||
temp = (temp - 2732) / 10;
|
||||
if (not got_package) {
|
||||
p_temp += temp;
|
||||
|
@ -317,7 +317,7 @@ namespace Cpu {
|
|||
unsigned int freq = 1;
|
||||
size_t size = sizeof(freq);
|
||||
|
||||
if (sysctlbyname("dev.cpu.0.freq", &freq, &size, NULL, 0) < 0) {
|
||||
if (sysctlbyname("dev.cpu.0.freq", &freq, &size, nullptr, 0) < 0) {
|
||||
return "";
|
||||
}
|
||||
return std::to_string(freq / 1000.0 ).substr(0, 3); // seems to be in MHz
|
||||
|
@ -373,17 +373,17 @@ namespace Cpu {
|
|||
uint32_t percent = -1;
|
||||
size_t size = sizeof(percent);
|
||||
string status = "discharging";
|
||||
if (sysctlbyname("hw.acpi.battery.life", &percent, &size, NULL, 0) < 0) {
|
||||
if (sysctlbyname("hw.acpi.battery.life", &percent, &size, nullptr, 0) < 0) {
|
||||
has_battery = false;
|
||||
} else {
|
||||
has_battery = true;
|
||||
size_t size = sizeof(seconds);
|
||||
if (sysctlbyname("hw.acpi.battery.time", &seconds, &size, NULL, 0) < 0) {
|
||||
if (sysctlbyname("hw.acpi.battery.time", &seconds, &size, nullptr, 0) < 0) {
|
||||
seconds = 0;
|
||||
}
|
||||
int state;
|
||||
size = sizeof(state);
|
||||
if (sysctlbyname("hw.acpi.battery.state", &state, &size, NULL, 0) < 0) {
|
||||
if (sysctlbyname("hw.acpi.battery.state", &state, &size, nullptr, 0) < 0) {
|
||||
status = "unknown";
|
||||
} else {
|
||||
if (state == 2) {
|
||||
|
@ -398,7 +398,7 @@ namespace Cpu {
|
|||
return {percent, seconds, status};
|
||||
}
|
||||
|
||||
auto collect(bool no_update) -> cpu_info & {
|
||||
auto collect(bool no_update) -> cpu_info & {
|
||||
if (Runner::stopping or (no_update and not current_cpu.cpu_percent.at("total").empty()))
|
||||
return current_cpu;
|
||||
auto &cpu = current_cpu;
|
||||
|
@ -409,7 +409,7 @@ namespace Cpu {
|
|||
|
||||
vector<array<long, CPUSTATES>> cpu_time(Shared::coreCount);
|
||||
size_t size = sizeof(long) * CPUSTATES * Shared::coreCount;
|
||||
if (sysctlbyname("kern.cp_times", &cpu_time[0], &size, NULL, 0) == -1) {
|
||||
if (sysctlbyname("kern.cp_times", &cpu_time[0], &size, nullptr, 0) == -1) {
|
||||
Logger::error("failed to get CPU times");
|
||||
}
|
||||
long long global_totals = 0;
|
||||
|
@ -543,35 +543,37 @@ namespace Mem {
|
|||
|
||||
// find all zpools in the system. Do this only at startup.
|
||||
void get_zpools() {
|
||||
std::regex toReplace("\\.");
|
||||
PipeWrapper poolPipe = PipeWrapper("zpool list -H -o name", "r");
|
||||
|
||||
while (not std::feof(poolPipe())) {
|
||||
char poolName[512];
|
||||
size_t len = 512;
|
||||
if (fgets(poolName, len, poolPipe())) {
|
||||
poolName[strcspn(poolName, "\n")] = 0;
|
||||
Logger::debug("zpool found: " + string(poolName));
|
||||
Mem::zpools.push_back(poolName);
|
||||
Mem::zpools.push_back(std::regex_replace(poolName, toReplace, "%25"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void collect_disk(unordered_flat_map<string, disk_info> &disks, unordered_flat_map<string, string> &mapping) {
|
||||
void collect_disk(unordered_flat_map<string, disk_info> &disks, unordered_flat_map<string, string> &mapping) {
|
||||
// this bit is for 'regular' mounts
|
||||
static struct statinfo cur;
|
||||
long double etime = 0;
|
||||
uint64_t total_bytes_read;
|
||||
static struct statinfo cur;
|
||||
long double etime = 0;
|
||||
uint64_t total_bytes_read;
|
||||
uint64_t total_bytes_write;
|
||||
|
||||
static std::unique_ptr<struct devinfo, decltype(std::free)*> curDevInfo (reinterpret_cast<struct devinfo*>(std::calloc(1, sizeof(struct devinfo))), std::free);
|
||||
cur.dinfo = curDevInfo.get();
|
||||
|
||||
if (devstat_getdevs(NULL, &cur) != -1) {
|
||||
if (devstat_getdevs(nullptr, &cur) != -1) {
|
||||
for (int i = 0; i < cur.dinfo->numdevs; i++) {
|
||||
auto d = cur.dinfo->devices[i];
|
||||
string devStatName = "/dev/" + string(d.device_name) + std::to_string(d.unit_number);
|
||||
for (auto& [ignored, disk] : disks) { // find matching mountpoints - could be multiple as d.device_name is only ada (and d.unit_number is the device number), while the disk.dev is like /dev/ada0s1
|
||||
if (disk.dev.string().rfind(devStatName, 0) == 0) {
|
||||
devstat_compute_statistics(&d, NULL, etime, DSM_TOTAL_BYTES_READ, &total_bytes_read, DSM_TOTAL_BYTES_WRITE, &total_bytes_write, DSM_NONE);
|
||||
devstat_compute_statistics(&d, nullptr, etime, DSM_TOTAL_BYTES_READ, &total_bytes_read, DSM_TOTAL_BYTES_WRITE, &total_bytes_write, DSM_NONE);
|
||||
assign_values(disk, total_bytes_read, total_bytes_write);
|
||||
string mountpoint = mapping.at(disk.dev);
|
||||
Logger::debug("dev " + devStatName + " -> " + mountpoint + " read=" + std::to_string(total_bytes_read) + " write=" + std::to_string(total_bytes_write));
|
||||
|
@ -583,7 +585,7 @@ namespace Mem {
|
|||
}
|
||||
|
||||
// this code is for ZFS mounts
|
||||
for (string poolName : Mem::zpools) {
|
||||
for (const auto &poolName : Mem::zpools) {
|
||||
char sysCtl[1024];
|
||||
snprintf(sysCtl, sizeof(sysCtl), "sysctl kstat.zfs.%s.dataset | egrep \'dataset_name|nread|nwritten\'", poolName.c_str());
|
||||
PipeWrapper f = PipeWrapper(sysCtl, "r");
|
||||
|
@ -594,7 +596,7 @@ namespace Mem {
|
|||
while (not std::feof(f())) {
|
||||
if (fgets(buf, len, f())) {
|
||||
char *name = std::strtok(buf, ": \n");
|
||||
char *value = std::strtok(NULL, ": \n");
|
||||
char *value = std::strtok(nullptr, ": \n");
|
||||
if (string(name).find("dataset_name") != string::npos) {
|
||||
// create entry if datasetname matches with anything in mapping
|
||||
// relies on the fact that the dataset name is last value in the list
|
||||
|
@ -617,17 +619,17 @@ namespace Mem {
|
|||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
auto collect(bool no_update) -> mem_info & {
|
||||
auto collect(bool no_update) -> mem_info & {
|
||||
if (Runner::stopping or (no_update and not current_mem.percent.at("used").empty()))
|
||||
return current_mem;
|
||||
|
||||
auto show_swap = Config::getB("show_swap");
|
||||
auto show_disks = Config::getB("show_disks");
|
||||
auto swap_disk = Config::getB("swap_disk");
|
||||
auto show_swap = Config::getB("show_swap");
|
||||
auto show_disks = Config::getB("show_disks");
|
||||
auto swap_disk = Config::getB("swap_disk");
|
||||
auto &mem = current_mem;
|
||||
static bool snapped = (getenv("BTOP_SNAPPED") != NULL);
|
||||
static bool snapped = (getenv("BTOP_SNAPPED") != nullptr);
|
||||
|
||||
int mib[4];
|
||||
u_int memActive, memWire, cachedMem, freeMem;
|
||||
|
@ -635,12 +637,12 @@ namespace Mem {
|
|||
|
||||
len = 4; sysctlnametomib("vm.stats.vm.v_active_count", mib, &len);
|
||||
len = sizeof(memActive);
|
||||
sysctl(mib, 4, &(memActive), &len, NULL, 0);
|
||||
sysctl(mib, 4, &(memActive), &len, nullptr, 0);
|
||||
memActive *= Shared::pageSize;
|
||||
|
||||
len = 4; sysctlnametomib("vm.stats.vm.v_wire_count", mib, &len);
|
||||
len = sizeof(memWire);
|
||||
sysctl(mib, 4, &(memWire), &len, NULL, 0);
|
||||
sysctl(mib, 4, &(memWire), &len, nullptr, 0);
|
||||
memWire *= Shared::pageSize;
|
||||
|
||||
mem.stats.at("used") = memWire + memActive;
|
||||
|
@ -648,19 +650,19 @@ namespace Mem {
|
|||
|
||||
len = sizeof(cachedMem);
|
||||
len = 4; sysctlnametomib("vm.stats.vm.v_cache_count", mib, &len);
|
||||
sysctl(mib, 4, &(cachedMem), &len, NULL, 0);
|
||||
sysctl(mib, 4, &(cachedMem), &len, nullptr, 0);
|
||||
cachedMem *= Shared::pageSize;
|
||||
mem.stats.at("cached") = cachedMem;
|
||||
|
||||
len = sizeof(freeMem);
|
||||
len = 4; sysctlnametomib("vm.stats.vm.v_free_count", mib, &len);
|
||||
sysctl(mib, 4, &(freeMem), &len, NULL, 0);
|
||||
sysctl(mib, 4, &(freeMem), &len, nullptr, 0);
|
||||
freeMem *= Shared::pageSize;
|
||||
mem.stats.at("free") = freeMem;
|
||||
|
||||
if (show_swap) {
|
||||
char buf[_POSIX2_LINE_MAX];
|
||||
Shared::kvm_openfiles_wrapper kd(NULL, _PATH_DEVNULL, NULL, O_RDONLY, buf);
|
||||
Shared::kvm_openfiles_wrapper kd(nullptr, _PATH_DEVNULL, nullptr, O_RDONLY, buf);
|
||||
struct kvm_swap swap[16];
|
||||
int nswap = kvm_getswapinfo(kd(), swap, 16, 0);
|
||||
int totalSwap = 0, usedSwap = 0;
|
||||
|
@ -693,7 +695,7 @@ namespace Mem {
|
|||
double uptime = system_uptime();
|
||||
auto &disks_filter = Config::getS("disks_filter");
|
||||
bool filter_exclude = false;
|
||||
// auto only_physical = Config::getB("only_physical");
|
||||
// auto only_physical = Config::getB("only_physical");
|
||||
auto &disks = mem.disks;
|
||||
vector<string> filter;
|
||||
if (not disks_filter.empty()) {
|
||||
|
@ -711,7 +713,7 @@ namespace Mem {
|
|||
for (int i = 0; i < count; i++) {
|
||||
auto fstype = string(stfs[i].f_fstypename);
|
||||
if (fstype == "autofs" || fstype == "devfs" || fstype == "linprocfs" || fstype == "procfs" || fstype == "tmpfs" || fstype == "linsysfs" ||
|
||||
fstype == "fdesckfs") {
|
||||
fstype == "fdesckfs") {
|
||||
// in memory filesystems -> not useful to show
|
||||
continue;
|
||||
}
|
||||
|
@ -826,11 +828,11 @@ namespace Net {
|
|||
auto operator()() -> struct ifaddrs * { return ifaddr; }
|
||||
};
|
||||
|
||||
auto collect(bool no_update) -> net_info & {
|
||||
auto collect(bool no_update) -> net_info & {
|
||||
auto &net = current_net;
|
||||
auto &config_iface = Config::getS("net_iface");
|
||||
auto net_sync = Config::getB("net_sync");
|
||||
auto net_auto = Config::getB("net_auto");
|
||||
auto net_sync = Config::getB("net_sync");
|
||||
auto net_auto = Config::getB("net_auto");
|
||||
auto new_timestamp = time_ms();
|
||||
|
||||
if (not no_update and errors < 3) {
|
||||
|
@ -850,8 +852,8 @@ namespace Net {
|
|||
string ipv4, ipv6;
|
||||
|
||||
//? Iteration over all items in getifaddrs() list
|
||||
for (auto *ifa = if_wrap(); ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == NULL) continue;
|
||||
for (auto *ifa = if_wrap(); ifa != nullptr; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == nullptr) continue;
|
||||
family = ifa->ifa_addr->sa_family;
|
||||
const auto &iface = ifa->ifa_name;
|
||||
//? Update available interfaces vector and get status of interface
|
||||
|
@ -868,7 +870,7 @@ namespace Net {
|
|||
//? Get IPv4 address
|
||||
if (family == AF_INET) {
|
||||
if (net[iface].ipv4.empty()) {
|
||||
if (NULL != inet_ntop(family, &(reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr)->sin_addr), ip, IPBUFFER_MAXSIZE)) {
|
||||
if (nullptr != inet_ntop(family, &(reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr)->sin_addr), ip, IPBUFFER_MAXSIZE)) {
|
||||
|
||||
net[iface].ipv4 = ip;
|
||||
} else {
|
||||
|
@ -880,7 +882,7 @@ namespace Net {
|
|||
//? Get IPv6 address
|
||||
else if (family == AF_INET6) {
|
||||
if (net[iface].ipv6.empty()) {
|
||||
if (NULL != inet_ntop(family, &(reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr)->sin6_addr), ip, IPBUFFER_MAXSIZE)) {
|
||||
if (nullptr != inet_ntop(family, &(reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr)->sin6_addr), ip, IPBUFFER_MAXSIZE)) {
|
||||
net[iface].ipv6 = ip;
|
||||
} else {
|
||||
int errsv = errno;
|
||||
|
@ -893,15 +895,15 @@ namespace Net {
|
|||
unordered_flat_map<string, std::tuple<uint64_t, uint64_t>> ifstats;
|
||||
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST, 0};
|
||||
size_t len;
|
||||
if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
|
||||
if (sysctl(mib, 6, nullptr, &len, nullptr, 0) < 0) {
|
||||
Logger::error("failed getting network interfaces");
|
||||
} else {
|
||||
std::unique_ptr<char[]> buf(new char[len]);
|
||||
if (sysctl(mib, 6, buf.get(), &len, NULL, 0) < 0) {
|
||||
if (sysctl(mib, 6, buf.get(), &len, nullptr, 0) < 0) {
|
||||
Logger::error("failed getting network interfaces");
|
||||
} else {
|
||||
char *lim = buf.get() + len;
|
||||
char *next = NULL;
|
||||
char *next = nullptr;
|
||||
for (next = buf.get(); next < lim;) {
|
||||
struct if_msghdr *ifm = (struct if_msghdr *)next;
|
||||
next += ifm->ifm_msglen;
|
||||
|
@ -985,7 +987,7 @@ namespace Net {
|
|||
auto sorted_interfaces = interfaces;
|
||||
rng::sort(sorted_interfaces, [&](const auto &a, const auto &b) {
|
||||
return cmp_greater(net.at(a).stat["download"].total + net.at(a).stat["upload"].total,
|
||||
net.at(b).stat["download"].total + net.at(b).stat["upload"].total);
|
||||
net.at(b).stat["download"].total + net.at(b).stat["upload"].total);
|
||||
});
|
||||
selected_iface.clear();
|
||||
//? Try to set to a connected interface
|
||||
|
@ -1008,8 +1010,8 @@ namespace Net {
|
|||
for (const auto &sel : {0, 1}) {
|
||||
if (rescale or max_count[dir][sel] >= 5) {
|
||||
const long long avg_speed = (net[selected_iface].bandwidth[dir].size() > 5
|
||||
? std::accumulate(net.at(selected_iface).bandwidth.at(dir).rbegin(), net.at(selected_iface).bandwidth.at(dir).rbegin() + 5, 0ll) / 5
|
||||
: net[selected_iface].stat[dir].speed);
|
||||
? std::accumulate(net.at(selected_iface).bandwidth.at(dir).rbegin(), net.at(selected_iface).bandwidth.at(dir).rbegin() + 5, 0ll) / 5
|
||||
: net[selected_iface].stat[dir].speed);
|
||||
graph_max[dir] = max(uint64_t(avg_speed * (sel == 0 ? 1.3 : 3.0)), (uint64_t)10 << 10);
|
||||
max_count[dir][0] = max_count[dir][1] = 0;
|
||||
redraw = true;
|
||||
|
@ -1078,7 +1080,7 @@ namespace Proc {
|
|||
|
||||
//? Process runtime : current time - start time (both in unix time - seconds since epoch)
|
||||
struct timeval currentTime;
|
||||
gettimeofday(¤tTime, NULL);
|
||||
gettimeofday(¤tTime, nullptr);
|
||||
detailed.elapsed = sec_to_dhms(currentTime.tv_sec - detailed.entry.cpu_s); // only interested in second granularity, so ignoring tc_usec
|
||||
if (detailed.elapsed.size() > 8) detailed.elapsed.resize(detailed.elapsed.size() - 3);
|
||||
|
||||
|
@ -1110,17 +1112,17 @@ namespace Proc {
|
|||
}
|
||||
|
||||
//* Collects and sorts process information from /proc
|
||||
auto collect(bool no_update) -> vector<proc_info> & {
|
||||
auto collect(bool no_update) -> vector<proc_info> & {
|
||||
const auto &sorting = Config::getS("proc_sorting");
|
||||
auto reverse = Config::getB("proc_reversed");
|
||||
auto reverse = Config::getB("proc_reversed");
|
||||
const auto &filter = Config::getS("proc_filter");
|
||||
auto per_core = Config::getB("proc_per_core");
|
||||
auto tree = Config::getB("proc_tree");
|
||||
auto show_detailed = Config::getB("show_detailed");
|
||||
auto per_core = Config::getB("proc_per_core");
|
||||
auto tree = Config::getB("proc_tree");
|
||||
auto show_detailed = Config::getB("show_detailed");
|
||||
const size_t detailed_pid = Config::getI("detailed_pid");
|
||||
bool should_filter = current_filter != filter;
|
||||
if (should_filter) current_filter = filter;
|
||||
bool sorted_change = (sorting != current_sort or reverse != current_rev or should_filter);
|
||||
bool sorted_change = (sorting != current_sort or reverse != current_rev or should_filter);
|
||||
if (sorted_change) {
|
||||
current_sort = sorting;
|
||||
current_rev = reverse;
|
||||
|
@ -1133,7 +1135,7 @@ namespace Proc {
|
|||
|
||||
vector<array<long, CPUSTATES>> cpu_time(Shared::coreCount);
|
||||
size_t size = sizeof(long) * CPUSTATES * Shared::coreCount;
|
||||
if (sysctlbyname("kern.cp_times", &cpu_time[0], &size, NULL, 0) == -1) {
|
||||
if (sysctlbyname("kern.cp_times", &cpu_time[0], &size, nullptr, 0) == -1) {
|
||||
Logger::error("failed to get CPU times");
|
||||
}
|
||||
cputimes = 0;
|
||||
|
@ -1152,16 +1154,16 @@ namespace Proc {
|
|||
should_filter = true;
|
||||
found.clear();
|
||||
struct timeval currentTime;
|
||||
gettimeofday(¤tTime, NULL);
|
||||
gettimeofday(¤tTime, nullptr);
|
||||
const double timeNow = currentTime.tv_sec + (currentTime.tv_usec / 1'000'000);
|
||||
|
||||
int count = 0;
|
||||
char buf[_POSIX2_LINE_MAX];
|
||||
Shared::kvm_openfiles_wrapper kd(NULL, _PATH_DEVNULL, NULL, O_RDONLY, buf);
|
||||
char buf[_POSIX2_LINE_MAX];
|
||||
Shared::kvm_openfiles_wrapper kd(nullptr, _PATH_DEVNULL, nullptr, O_RDONLY, buf);
|
||||
const struct kinfo_proc* kprocs = kvm_getprocs(kd(), KERN_PROC_PROC, 0, &count);
|
||||
|
||||
for (int i = 0; i < count; i++) {
|
||||
const struct kinfo_proc* kproc = &kprocs[i];
|
||||
const struct kinfo_proc* kproc = &kprocs[i];
|
||||
const size_t pid = (size_t)kproc->ki_pid;
|
||||
if (pid < 1) continue;
|
||||
found.push_back(pid);
|
||||
|
@ -1179,7 +1181,7 @@ namespace Proc {
|
|||
|
||||
//? Get program name, command, username, parent pid, nice and status
|
||||
if (no_cache) {
|
||||
if (kproc->ki_comm == NULL or kproc->ki_comm == "idle"s) {
|
||||
if (string(kproc->ki_comm) == "idle"s) {
|
||||
current_procs.pop_back();
|
||||
found.pop_back();
|
||||
continue;
|
||||
|
@ -1342,8 +1344,8 @@ namespace Tools {
|
|||
struct timeval ts, currTime;
|
||||
std::size_t len = sizeof(ts);
|
||||
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
|
||||
if (sysctl(mib, 2, &ts, &len, NULL, 0) != -1) {
|
||||
gettimeofday(&currTime, NULL);
|
||||
if (sysctl(mib, 2, &ts, &len, nullptr, 0) != -1) {
|
||||
gettimeofday(&currTime, nullptr);
|
||||
return currTime.tv_sec - ts.tv_sec;
|
||||
}
|
||||
return 0.0;
|
||||
|
|
|
@ -28,6 +28,7 @@ tab-size = 4
|
|||
#include <ifaddrs.h>
|
||||
#include <net/if.h>
|
||||
#include <arpa/inet.h> // for inet_ntop()
|
||||
#include <filesystem>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#if defined(RSMI_STATIC)
|
||||
|
@ -38,12 +39,11 @@ tab-size = 4
|
|||
#include <pwd.h>
|
||||
#endif
|
||||
|
||||
#include <btop_shared.hpp>
|
||||
#include <btop_config.hpp>
|
||||
#include <btop_tools.hpp>
|
||||
#include "../btop_shared.hpp"
|
||||
#include "../btop_config.hpp"
|
||||
#include "../btop_tools.hpp"
|
||||
|
||||
using std::clamp;
|
||||
using std::cmp_equal;
|
||||
using std::cmp_greater;
|
||||
using std::cmp_less;
|
||||
using std::ifstream;
|
||||
|
@ -52,6 +52,7 @@ using std::min;
|
|||
using std::numeric_limits;
|
||||
using std::round;
|
||||
using std::streamsize;
|
||||
using std::vector;
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
namespace rng = std::ranges;
|
||||
|
@ -67,8 +68,8 @@ namespace Cpu {
|
|||
vector<string> available_sensors = {"Auto"};
|
||||
cpu_info current_cpu;
|
||||
fs::path freq_path = "/sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq";
|
||||
bool got_sensors{}; // defaults to false
|
||||
bool cpu_temp_only{}; // defaults to false
|
||||
bool got_sensors{}; // defaults to false
|
||||
bool cpu_temp_only{}; // defaults to false
|
||||
|
||||
//* Populate found_sensors map
|
||||
bool get_sensors();
|
||||
|
@ -82,9 +83,9 @@ namespace Cpu {
|
|||
struct Sensor {
|
||||
fs::path path;
|
||||
string label;
|
||||
int64_t temp{}; // defaults to 0
|
||||
int64_t high{}; // defaults to 0
|
||||
int64_t crit{}; // defaults to 0
|
||||
int64_t temp{}; // defaults to 0
|
||||
int64_t high{}; // defaults to 0
|
||||
int64_t crit{}; // defaults to 0
|
||||
};
|
||||
|
||||
unordered_flat_map<string, Sensor> found_sensors;
|
||||
|
@ -299,10 +300,10 @@ namespace Cpu {
|
|||
bool has_battery = true;
|
||||
tuple<int, long, string> current_bat;
|
||||
|
||||
const array time_names {
|
||||
"user"s, "nice"s, "system"s, "idle"s, "iowait"s,
|
||||
"irq"s, "softirq"s, "steal"s, "guest"s, "guest_nice"s
|
||||
};
|
||||
const array time_names {
|
||||
"user"s, "nice"s, "system"s, "idle"s, "iowait"s,
|
||||
"irq"s, "softirq"s, "steal"s, "guest"s, "guest_nice"s
|
||||
};
|
||||
|
||||
unordered_flat_map<string, long long> cpu_old = {
|
||||
{"totals", 0},
|
||||
|
@ -545,14 +546,14 @@ namespace Cpu {
|
|||
}
|
||||
|
||||
string get_cpuHz() {
|
||||
static int failed{}; // defaults to 0
|
||||
static int failed{}; // defaults to 0
|
||||
|
||||
if (failed > 4)
|
||||
return ""s;
|
||||
if (failed > 4)
|
||||
return ""s;
|
||||
|
||||
string cpuhz;
|
||||
try {
|
||||
double hz{}; // defaults to 0.0
|
||||
double hz{}; // defaults to 0.0
|
||||
//? Try to get freq from /sys/devices/system/cpu/cpufreq/policy first (faster)
|
||||
if (not freq_path.empty()) {
|
||||
hz = stod(readfile(freq_path, "0.0")) / 1000;
|
||||
|
@ -577,8 +578,8 @@ namespace Cpu {
|
|||
}
|
||||
}
|
||||
|
||||
if (hz <= 1 or hz >= 1000000)
|
||||
throw std::runtime_error("Failed to read /sys/devices/system/cpu/cpufreq/policy and /proc/cpuinfo.");
|
||||
if (hz <= 1 or hz >= 1000000)
|
||||
throw std::runtime_error("Failed to read /sys/devices/system/cpu/cpufreq/policy and /proc/cpuinfo.");
|
||||
|
||||
if (hz >= 1000) {
|
||||
if (hz >= 10000) cpuhz = to_string((int)round(hz / 1000)); // Future proof until we reach THz speeds :)
|
||||
|
@ -590,10 +591,10 @@ namespace Cpu {
|
|||
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
if (++failed < 5)
|
||||
return ""s;
|
||||
if (++failed < 5)
|
||||
return ""s;
|
||||
else {
|
||||
Logger::warning("get_cpuHZ() : " + string{e.what()});
|
||||
Logger::warning("get_cpuHZ() : " + string{e.what()});
|
||||
return ""s;
|
||||
}
|
||||
}
|
||||
|
@ -608,9 +609,9 @@ namespace Cpu {
|
|||
//? Try to get core mapping from /proc/cpuinfo
|
||||
ifstream cpuinfo(Shared::procPath / "cpuinfo");
|
||||
if (cpuinfo.good()) {
|
||||
int cpu{}; // defaults to 0
|
||||
int core{}; // defaults to 0
|
||||
int n{}; // defaults to 0
|
||||
int cpu{}; // defaults to 0
|
||||
int core{}; // defaults to 0
|
||||
int n{}; // defaults to 0
|
||||
for (string instr; cpuinfo >> instr;) {
|
||||
if (instr == "processor") {
|
||||
cpuinfo.ignore(SSmax, ':');
|
||||
|
@ -803,7 +804,7 @@ namespace Cpu {
|
|||
return {percent, seconds, status};
|
||||
}
|
||||
|
||||
auto collect(bool no_update) -> cpu_info& {
|
||||
auto collect(bool no_update) -> cpu_info& {
|
||||
if (Runner::stopping or (no_update and not current_cpu.cpu_percent.at("total").empty())) return current_cpu;
|
||||
auto& cpu = current_cpu;
|
||||
|
||||
|
@ -929,9 +930,9 @@ namespace Cpu {
|
|||
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Logger::debug("Cpu::collect() : " + string{e.what()});
|
||||
Logger::debug("Cpu::collect() : " + string{e.what()});
|
||||
if (cread.bad()) throw std::runtime_error("Failed to read /proc/stat");
|
||||
else throw std::runtime_error("Cpu::collect() : " + string{e.what()});
|
||||
else throw std::runtime_error("Cpu::collect() : " + string{e.what()});
|
||||
}
|
||||
|
||||
if (Config::getB("check_temp") and got_sensors)
|
||||
|
@ -1486,10 +1487,10 @@ namespace Gpu {
|
|||
}
|
||||
|
||||
namespace Mem {
|
||||
bool has_swap{}; // defaults to false
|
||||
bool has_swap{}; // defaults to false
|
||||
vector<string> fstab;
|
||||
fs::file_time_type fstab_time;
|
||||
int disk_ios{}; // defaults to 0
|
||||
int disk_ios{}; // defaults to 0
|
||||
vector<string> last_found;
|
||||
|
||||
//?* Find the filepath to the specified ZFS object's stat file
|
||||
|
@ -1514,12 +1515,12 @@ namespace Mem {
|
|||
return totalMem;
|
||||
}
|
||||
|
||||
auto collect(bool no_update) -> mem_info& {
|
||||
auto collect(bool no_update) -> mem_info& {
|
||||
if (Runner::stopping or (no_update and not current_mem.percent.at("used").empty())) return current_mem;
|
||||
auto show_swap = Config::getB("show_swap");
|
||||
auto swap_disk = Config::getB("swap_disk");
|
||||
auto show_disks = Config::getB("show_disks");
|
||||
auto zfs_arc_cached = Config::getB("zfs_arc_cached");
|
||||
auto show_swap = Config::getB("show_swap");
|
||||
auto swap_disk = Config::getB("swap_disk");
|
||||
auto show_disks = Config::getB("show_disks");
|
||||
auto zfs_arc_cached = Config::getB("zfs_arc_cached");
|
||||
auto totalMem = get_totalMem();
|
||||
auto& mem = current_mem;
|
||||
|
||||
|
@ -1613,9 +1614,9 @@ namespace Mem {
|
|||
try {
|
||||
auto& disks_filter = Config::getS("disks_filter");
|
||||
bool filter_exclude = false;
|
||||
auto use_fstab = Config::getB("use_fstab");
|
||||
auto only_physical = Config::getB("only_physical");
|
||||
auto zfs_hide_datasets = Config::getB("zfs_hide_datasets");
|
||||
auto use_fstab = Config::getB("use_fstab");
|
||||
auto only_physical = Config::getB("only_physical");
|
||||
auto zfs_hide_datasets = Config::getB("zfs_hide_datasets");
|
||||
auto& disks = mem.disks;
|
||||
ifstream diskread;
|
||||
|
||||
|
@ -1886,14 +1887,14 @@ namespace Mem {
|
|||
while (cmp_greater(disk.io_activity.size(), width * 2)) disk.io_activity.pop_front();
|
||||
}
|
||||
} else {
|
||||
Logger::debug("Error in Mem::collect() : when opening " + string{disk.stat});
|
||||
Logger::debug("Error in Mem::collect() : when opening " + string{disk.stat});
|
||||
}
|
||||
diskread.close();
|
||||
}
|
||||
old_uptime = uptime;
|
||||
}
|
||||
catch (const std::exception& e) {
|
||||
Logger::warning("Error in Mem::collect() : " + string{e.what()});
|
||||
Logger::warning("Error in Mem::collect() : " + string{e.what()});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1954,13 +1955,13 @@ namespace Mem {
|
|||
bool zfs_collect_pool_total_stats(struct disk_info &disk) {
|
||||
ifstream diskread;
|
||||
|
||||
int64_t bytes_read;
|
||||
int64_t bytes_write;
|
||||
int64_t io_ticks;
|
||||
int64_t bytes_read_total{}; // defaults to 0
|
||||
int64_t bytes_write_total{}; // defaults to 0
|
||||
int64_t io_ticks_total{}; // defaults to 0
|
||||
int64_t objects_read{}; // defaults to 0
|
||||
int64_t bytes_read;
|
||||
int64_t bytes_write;
|
||||
int64_t io_ticks;
|
||||
int64_t bytes_read_total{}; // defaults to 0
|
||||
int64_t bytes_write_total{}; // defaults to 0
|
||||
int64_t io_ticks_total{}; // defaults to 0
|
||||
int64_t objects_read{}; // defaults to 0
|
||||
|
||||
// looking through all files that start with 'objset'
|
||||
for (const auto& file: fs::directory_iterator(disk.stat)) {
|
||||
|
@ -2036,11 +2037,11 @@ namespace Net {
|
|||
net_info empty_net = {};
|
||||
vector<string> interfaces;
|
||||
string selected_iface;
|
||||
int errors{}; // defaults to 0
|
||||
int errors{}; // defaults to 0
|
||||
unordered_flat_map<string, uint64_t> graph_max = { {"download", {}}, {"upload", {}} };
|
||||
unordered_flat_map<string, array<int, 2>> max_count = { {"download", {}}, {"upload", {}} };
|
||||
bool rescale{true};
|
||||
uint64_t timestamp{}; // defaults to 0
|
||||
bool rescale{true};
|
||||
uint64_t timestamp{}; // defaults to 0
|
||||
|
||||
//* RAII wrapper for getifaddrs
|
||||
class getifaddr_wrapper {
|
||||
|
@ -2052,12 +2053,12 @@ namespace Net {
|
|||
auto operator()() -> struct ifaddrs* { return ifaddr; }
|
||||
};
|
||||
|
||||
auto collect(bool no_update) -> net_info& {
|
||||
auto collect(bool no_update) -> net_info& {
|
||||
if (Runner::stopping) return empty_net;
|
||||
auto& net = current_net;
|
||||
auto& config_iface = Config::getS("net_iface");
|
||||
auto net_sync = Config::getB("net_sync");
|
||||
auto net_auto = Config::getB("net_auto");
|
||||
auto net_sync = Config::getB("net_sync");
|
||||
auto net_auto = Config::getB("net_auto");
|
||||
auto new_timestamp = time_ms();
|
||||
|
||||
if (not no_update and errors < 3) {
|
||||
|
@ -2077,8 +2078,8 @@ namespace Net {
|
|||
string ipv4, ipv6;
|
||||
|
||||
//? Iteration over all items in getifaddrs() list
|
||||
for (auto* ifa = if_wrap(); ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == NULL) continue;
|
||||
for (auto* ifa = if_wrap(); ifa != nullptr; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == nullptr) continue;
|
||||
family = ifa->ifa_addr->sa_family;
|
||||
const auto& iface = ifa->ifa_name;
|
||||
|
||||
|
@ -2098,7 +2099,7 @@ namespace Net {
|
|||
//? Get IPv4 address
|
||||
if (family == AF_INET) {
|
||||
if (net[iface].ipv4.empty()) {
|
||||
if (NULL != inet_ntop(family, &(reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr)->sin_addr), ip, IPBUFFER_MAXSIZE)) {
|
||||
if (nullptr != inet_ntop(family, &(reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr)->sin_addr), ip, IPBUFFER_MAXSIZE)) {
|
||||
net[iface].ipv4 = ip;
|
||||
} else {
|
||||
int errsv = errno;
|
||||
|
@ -2109,7 +2110,7 @@ namespace Net {
|
|||
//? Get IPv6 address
|
||||
else if (family == AF_INET6) {
|
||||
if (net[iface].ipv6.empty()) {
|
||||
if (NULL != inet_ntop(family, &(reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr)->sin6_addr), ip, IPBUFFER_MAXSIZE)) {
|
||||
if (nullptr != inet_ntop(family, &(reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr)->sin6_addr), ip, IPBUFFER_MAXSIZE)) {
|
||||
net[iface].ipv6 = ip;
|
||||
} else {
|
||||
int errsv = errno;
|
||||
|
@ -2129,7 +2130,7 @@ namespace Net {
|
|||
auto& saved_stat = net.at(iface).stat.at(dir);
|
||||
auto& bandwidth = net.at(iface).bandwidth.at(dir);
|
||||
|
||||
uint64_t val{}; // defaults to 0
|
||||
uint64_t val{}; // defaults to 0
|
||||
try { val = (uint64_t)stoull(readfile(sys_file, "0")); }
|
||||
catch (const std::invalid_argument&) {}
|
||||
catch (const std::out_of_range&) {}
|
||||
|
@ -2250,15 +2251,15 @@ namespace Proc {
|
|||
unordered_flat_map<string, string> uid_user;
|
||||
string current_sort;
|
||||
string current_filter;
|
||||
bool current_rev{}; // defaults to false
|
||||
bool current_rev{}; // defaults to false
|
||||
|
||||
fs::file_time_type passwd_time;
|
||||
|
||||
uint64_t cputimes;
|
||||
int collapse = -1, expand = -1;
|
||||
uint64_t old_cputimes{}; // defaults to 0
|
||||
atomic<int> numpids{}; // defaults to 0
|
||||
int filter_found{}; // defaults to 0
|
||||
uint64_t old_cputimes{}; // defaults to 0
|
||||
atomic<int> numpids{}; // defaults to 0
|
||||
int filter_found{}; // defaults to 0
|
||||
|
||||
detail_container detailed;
|
||||
constexpr size_t KTHREADD = 2;
|
||||
|
@ -2362,19 +2363,19 @@ namespace Proc {
|
|||
}
|
||||
|
||||
//* Collects and sorts process information from /proc
|
||||
auto collect(bool no_update) -> vector<proc_info>& {
|
||||
auto collect(bool no_update) -> vector<proc_info>& {
|
||||
if (Runner::stopping) return current_procs;
|
||||
const auto& sorting = Config::getS("proc_sorting");
|
||||
auto reverse = Config::getB("proc_reversed");
|
||||
auto reverse = Config::getB("proc_reversed");
|
||||
const auto& filter = Config::getS("proc_filter");
|
||||
auto per_core = Config::getB("proc_per_core");
|
||||
auto should_filter_kernel = Config::getB("proc_filter_kernel");
|
||||
auto tree = Config::getB("proc_tree");
|
||||
auto show_detailed = Config::getB("show_detailed");
|
||||
auto per_core = Config::getB("proc_per_core");
|
||||
auto should_filter_kernel = Config::getB("proc_filter_kernel");
|
||||
auto tree = Config::getB("proc_tree");
|
||||
auto show_detailed = Config::getB("show_detailed");
|
||||
const size_t detailed_pid = Config::getI("detailed_pid");
|
||||
bool should_filter = current_filter != filter;
|
||||
if (should_filter) current_filter = filter;
|
||||
bool sorted_change = (sorting != current_sort or reverse != current_rev or should_filter);
|
||||
bool sorted_change = (sorting != current_sort or reverse != current_rev or should_filter);
|
||||
if (sorted_change) {
|
||||
current_sort = sorting;
|
||||
current_rev = reverse;
|
||||
|
@ -2390,7 +2391,7 @@ namespace Proc {
|
|||
const int cmult = (per_core) ? Shared::coreCount : 1;
|
||||
bool got_detailed = false;
|
||||
|
||||
static size_t proc_clear_count{}; // defaults to 0
|
||||
static size_t proc_clear_count{}; // defaults to 0
|
||||
|
||||
//* Use pids from last update if only changing filter, sorting or tree options
|
||||
if (no_update and not current_procs.empty()) {
|
||||
|
@ -2465,7 +2466,7 @@ namespace Proc {
|
|||
|
||||
//? Check if pid already exists in current_procs
|
||||
auto find_old = rng::find(current_procs, pid, &proc_info::pid);
|
||||
bool no_cache{}; // defaults to false
|
||||
bool no_cache{}; // defaults to false
|
||||
if (find_old == current_procs.end()) {
|
||||
current_procs.push_back({pid});
|
||||
find_old = current_procs.end() - 1;
|
||||
|
@ -2519,7 +2520,7 @@ namespace Proc {
|
|||
try {
|
||||
struct passwd* udet;
|
||||
udet = getpwuid(stoi(uid));
|
||||
if (udet != NULL and udet->pw_name != NULL) {
|
||||
if (udet != nullptr and udet->pw_name != nullptr) {
|
||||
new_proc.user = string(udet->pw_name);
|
||||
}
|
||||
else {
|
||||
|
@ -2748,6 +2749,6 @@ namespace Tools {
|
|||
catch (const std::invalid_argument&) {}
|
||||
catch (const std::out_of_range&) {}
|
||||
}
|
||||
throw std::runtime_error("Failed get uptime from " + string{Shared::procPath} + "/uptime");
|
||||
throw std::runtime_error("Failed to get uptime from " + string{Shared::procPath} + "/uptime");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -45,9 +45,6 @@ tab-size = 4
|
|||
#include <unistd.h>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <btop_config.hpp>
|
||||
#include <btop_shared.hpp>
|
||||
#include <btop_tools.hpp>
|
||||
#include <cmath>
|
||||
#include <fstream>
|
||||
#include <numeric>
|
||||
|
@ -55,6 +52,10 @@ tab-size = 4
|
|||
#include <regex>
|
||||
#include <string>
|
||||
|
||||
#include "../btop_config.hpp"
|
||||
#include "../btop_shared.hpp"
|
||||
#include "../btop_tools.hpp"
|
||||
|
||||
#include "sensors.hpp"
|
||||
#include "smc.hpp"
|
||||
|
||||
|
@ -72,7 +73,6 @@ namespace Cpu {
|
|||
vector<string> available_fields = {"total"};
|
||||
vector<string> available_sensors = {"Auto"};
|
||||
cpu_info current_cpu;
|
||||
fs::path freq_path = "/sys/devices/system/cpu/cpufreq/policy0/scaling_cur_freq";
|
||||
bool got_sensors = false, cpu_temp_only = false;
|
||||
int core_offset = 0;
|
||||
|
||||
|
@ -128,7 +128,7 @@ namespace Shared {
|
|||
}
|
||||
|
||||
size_t physicalCoreCountSize = sizeof(physicalCoreCount);
|
||||
if (sysctlbyname("hw.physicalcpu", &physicalCoreCount, &physicalCoreCountSize, NULL, 0) < 0) {
|
||||
if (sysctlbyname("hw.physicalcpu", &physicalCoreCount, &physicalCoreCountSize, nullptr, 0) < 0) {
|
||||
Logger::error("Could not get physical core count");
|
||||
}
|
||||
|
||||
|
@ -154,7 +154,7 @@ namespace Shared {
|
|||
|
||||
int64_t memsize = 0;
|
||||
size_t size = sizeof(memsize);
|
||||
if (sysctlbyname("hw.memsize", &memsize, &size, NULL, 0) < 0) {
|
||||
if (sysctlbyname("hw.memsize", &memsize, &size, nullptr, 0) < 0) {
|
||||
Logger::warning("Could not get memory size");
|
||||
}
|
||||
totalMem = memsize;
|
||||
|
@ -163,7 +163,6 @@ namespace Shared {
|
|||
arg_max = sysconf(_SC_ARG_MAX);
|
||||
|
||||
//? Init for namespace Cpu
|
||||
if (not fs::exists(Cpu::freq_path) or access(Cpu::freq_path.c_str(), R_OK) == -1) Cpu::freq_path.clear();
|
||||
Cpu::current_cpu.core_percent.insert(Cpu::current_cpu.core_percent.begin(), Shared::coreCount, {});
|
||||
Cpu::current_cpu.temp.insert(Cpu::current_cpu.temp.begin(), Shared::coreCount + 1, {});
|
||||
Cpu::core_old_totals.insert(Cpu::core_old_totals.begin(), Shared::coreCount, 0);
|
||||
|
@ -193,19 +192,19 @@ namespace Cpu {
|
|||
const array<string, 10> time_names = {"user", "nice", "system", "idle"};
|
||||
|
||||
unordered_flat_map<string, long long> cpu_old = {
|
||||
{"totals", 0},
|
||||
{"idles", 0},
|
||||
{"user", 0},
|
||||
{"nice", 0},
|
||||
{"system", 0},
|
||||
{"idle", 0}
|
||||
{"totals", 0},
|
||||
{"idles", 0},
|
||||
{"user", 0},
|
||||
{"nice", 0},
|
||||
{"system", 0},
|
||||
{"idle", 0}
|
||||
};
|
||||
|
||||
string get_cpuName() {
|
||||
string name;
|
||||
char buffer[1024];
|
||||
size_t size = sizeof(buffer);
|
||||
if (sysctlbyname("machdep.cpu.brand_string", &buffer, &size, NULL, 0) < 0) {
|
||||
if (sysctlbyname("machdep.cpu.brand_string", &buffer, &size, nullptr, 0) < 0) {
|
||||
Logger::error("Failed to get CPU name");
|
||||
return name;
|
||||
}
|
||||
|
@ -323,7 +322,7 @@ namespace Cpu {
|
|||
|
||||
int mib[] = {CTL_HW, HW_CPU_FREQ};
|
||||
|
||||
if (sysctl(mib, 2, &freq, &size, NULL, 0) < 0) {
|
||||
if (sysctl(mib, 2, &freq, &size, nullptr, 0) < 0) {
|
||||
// this fails on Apple Silicon macs. Apparently you're not allowed to know
|
||||
return "";
|
||||
}
|
||||
|
@ -442,7 +441,7 @@ namespace Cpu {
|
|||
return {percent, seconds, status};
|
||||
}
|
||||
|
||||
auto collect(bool no_update) -> cpu_info & {
|
||||
auto collect(bool no_update) -> cpu_info & {
|
||||
if (Runner::stopping or (no_update and not current_cpu.cpu_percent.at("total").empty()))
|
||||
return current_cpu;
|
||||
auto &cpu = current_cpu;
|
||||
|
@ -454,7 +453,7 @@ namespace Cpu {
|
|||
natural_t cpu_count;
|
||||
natural_t i;
|
||||
kern_return_t error;
|
||||
processor_cpu_load_info_data_t *cpu_load_info = NULL;
|
||||
processor_cpu_load_info_data_t *cpu_load_info = nullptr;
|
||||
|
||||
MachProcessorInfo info{};
|
||||
error = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, &info.info_array, &info.info_count);
|
||||
|
@ -603,7 +602,7 @@ namespace Mem {
|
|||
}
|
||||
/* Get the list of all drive objects. */
|
||||
if (IOServiceGetMatchingServices(libtop_master_port,
|
||||
IOServiceMatching("IOMediaBSDClient"), &drive_list)) {
|
||||
IOServiceMatching("IOMediaBSDClient"), &drive_list)) {
|
||||
Logger::error("Error in IOServiceGetMatchingServices()");
|
||||
return;
|
||||
}
|
||||
|
@ -663,15 +662,15 @@ namespace Mem {
|
|||
}
|
||||
}
|
||||
|
||||
auto collect(bool no_update) -> mem_info & {
|
||||
auto collect(bool no_update) -> mem_info & {
|
||||
if (Runner::stopping or (no_update and not current_mem.percent.at("used").empty()))
|
||||
return current_mem;
|
||||
|
||||
auto show_swap = Config::getB("show_swap");
|
||||
auto show_disks = Config::getB("show_disks");
|
||||
auto swap_disk = Config::getB("swap_disk");
|
||||
auto show_swap = Config::getB("show_swap");
|
||||
auto show_disks = Config::getB("show_disks");
|
||||
auto swap_disk = Config::getB("swap_disk");
|
||||
auto &mem = current_mem;
|
||||
static bool snapped = (getenv("BTOP_SNAPPED") != NULL);
|
||||
static bool snapped = (getenv("BTOP_SNAPPED") != nullptr);
|
||||
|
||||
vm_statistics64 p;
|
||||
mach_msg_type_number_t info_size = HOST_VM_INFO64_COUNT;
|
||||
|
@ -686,7 +685,7 @@ namespace Mem {
|
|||
|
||||
struct xsw_usage swap;
|
||||
size_t len = sizeof(struct xsw_usage);
|
||||
if (sysctl(mib, 2, &swap, &len, NULL, 0) == 0) {
|
||||
if (sysctl(mib, 2, &swap, &len, nullptr, 0) == 0) {
|
||||
mem.stats.at("swap_total") = swap.xsu_total;
|
||||
mem.stats.at("swap_free") = swap.xsu_avail;
|
||||
mem.stats.at("swap_used") = swap.xsu_used;
|
||||
|
@ -713,7 +712,7 @@ namespace Mem {
|
|||
double uptime = system_uptime();
|
||||
auto &disks_filter = Config::getS("disks_filter");
|
||||
bool filter_exclude = false;
|
||||
// auto only_physical = Config::getB("only_physical");
|
||||
// auto only_physical = Config::getB("only_physical");
|
||||
auto &disks = mem.disks;
|
||||
vector<string> filter;
|
||||
if (not disks_filter.empty()) {
|
||||
|
@ -843,11 +842,11 @@ namespace Net {
|
|||
auto operator()() -> struct ifaddrs * { return ifaddr; }
|
||||
};
|
||||
|
||||
auto collect(bool no_update) -> net_info & {
|
||||
auto collect(bool no_update) -> net_info & {
|
||||
auto &net = current_net;
|
||||
auto &config_iface = Config::getS("net_iface");
|
||||
auto net_sync = Config::getB("net_sync");
|
||||
auto net_auto = Config::getB("net_auto");
|
||||
auto net_sync = Config::getB("net_sync");
|
||||
auto net_auto = Config::getB("net_auto");
|
||||
auto new_timestamp = time_ms();
|
||||
|
||||
if (not no_update and errors < 3) {
|
||||
|
@ -867,8 +866,8 @@ namespace Net {
|
|||
string ipv4, ipv6;
|
||||
|
||||
//? Iteration over all items in getifaddrs() list
|
||||
for (auto *ifa = if_wrap(); ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == NULL) continue;
|
||||
for (auto *ifa = if_wrap(); ifa != nullptr; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == nullptr) continue;
|
||||
family = ifa->ifa_addr->sa_family;
|
||||
const auto &iface = ifa->ifa_name;
|
||||
//? Update available interfaces vector and get status of interface
|
||||
|
@ -884,7 +883,7 @@ namespace Net {
|
|||
//? Get IPv4 address
|
||||
if (family == AF_INET) {
|
||||
if (net[iface].ipv4.empty()) {
|
||||
if (NULL != inet_ntop(family, &(reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr)->sin_addr), ip, IPBUFFER_MAXSIZE)) {
|
||||
if (nullptr != inet_ntop(family, &(reinterpret_cast<struct sockaddr_in*>(ifa->ifa_addr)->sin_addr), ip, IPBUFFER_MAXSIZE)) {
|
||||
net[iface].ipv4 = ip;
|
||||
} else {
|
||||
int errsv = errno;
|
||||
|
@ -895,7 +894,7 @@ namespace Net {
|
|||
//? Get IPv6 address
|
||||
else if (family == AF_INET6) {
|
||||
if (net[iface].ipv6.empty()) {
|
||||
if (NULL != inet_ntop(family, &(reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr)->sin6_addr), ip, IPBUFFER_MAXSIZE)) {
|
||||
if (nullptr != inet_ntop(family, &(reinterpret_cast<struct sockaddr_in6*>(ifa->ifa_addr)->sin6_addr), ip, IPBUFFER_MAXSIZE)) {
|
||||
net[iface].ipv6 = ip;
|
||||
} else {
|
||||
int errsv = errno;
|
||||
|
@ -908,15 +907,15 @@ namespace Net {
|
|||
unordered_flat_map<string, std::tuple<uint64_t, uint64_t>> ifstats;
|
||||
int mib[] = {CTL_NET, PF_ROUTE, 0, 0, NET_RT_IFLIST2, 0};
|
||||
size_t len;
|
||||
if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
|
||||
if (sysctl(mib, 6, nullptr, &len, nullptr, 0) < 0) {
|
||||
Logger::error("failed getting network interfaces");
|
||||
} else {
|
||||
std::unique_ptr<char[]> buf(new char[len]);
|
||||
if (sysctl(mib, 6, buf.get(), &len, NULL, 0) < 0) {
|
||||
if (sysctl(mib, 6, buf.get(), &len, nullptr, 0) < 0) {
|
||||
Logger::error("failed getting network interfaces");
|
||||
} else {
|
||||
char *lim = buf.get() + len;
|
||||
char *next = NULL;
|
||||
char *next = nullptr;
|
||||
for (next = buf.get(); next < lim;) {
|
||||
struct if_msghdr *ifm = (struct if_msghdr *)next;
|
||||
next += ifm->ifm_msglen;
|
||||
|
@ -1000,7 +999,7 @@ namespace Net {
|
|||
auto sorted_interfaces = interfaces;
|
||||
rng::sort(sorted_interfaces, [&](const auto &a, const auto &b) {
|
||||
return cmp_greater(net.at(a).stat["download"].total + net.at(a).stat["upload"].total,
|
||||
net.at(b).stat["download"].total + net.at(b).stat["upload"].total);
|
||||
net.at(b).stat["download"].total + net.at(b).stat["upload"].total);
|
||||
});
|
||||
selected_iface.clear();
|
||||
//? Try to set to a connected interface
|
||||
|
@ -1023,8 +1022,8 @@ namespace Net {
|
|||
for (const auto &sel : {0, 1}) {
|
||||
if (rescale or max_count[dir][sel] >= 5) {
|
||||
const long long avg_speed = (net[selected_iface].bandwidth[dir].size() > 5
|
||||
? std::accumulate(net.at(selected_iface).bandwidth.at(dir).rbegin(), net.at(selected_iface).bandwidth.at(dir).rbegin() + 5, 0ll) / 5
|
||||
: net[selected_iface].stat[dir].speed);
|
||||
? std::accumulate(net.at(selected_iface).bandwidth.at(dir).rbegin(), net.at(selected_iface).bandwidth.at(dir).rbegin() + 5, 0ll) / 5
|
||||
: net[selected_iface].stat[dir].speed);
|
||||
graph_max[dir] = max(uint64_t(avg_speed * (sel == 0 ? 1.3 : 3.0)), (uint64_t)10 << 10);
|
||||
max_count[dir][0] = max_count[dir][1] = 0;
|
||||
redraw = true;
|
||||
|
@ -1093,7 +1092,7 @@ namespace Proc {
|
|||
|
||||
//? Process runtime : current time - start time (both in unix time - seconds since epoch)
|
||||
struct timeval currentTime;
|
||||
gettimeofday(¤tTime, NULL);
|
||||
gettimeofday(¤tTime, nullptr);
|
||||
detailed.elapsed = sec_to_dhms(currentTime.tv_sec - (detailed.entry.cpu_s / 1'000'000));
|
||||
if (detailed.elapsed.size() > 8) detailed.elapsed.resize(detailed.elapsed.size() - 3);
|
||||
|
||||
|
@ -1125,17 +1124,17 @@ namespace Proc {
|
|||
}
|
||||
|
||||
//* Collects and sorts process information from /proc
|
||||
auto collect(bool no_update) -> vector<proc_info> & {
|
||||
auto collect(bool no_update) -> vector<proc_info> & {
|
||||
const auto &sorting = Config::getS("proc_sorting");
|
||||
auto reverse = Config::getB("proc_reversed");
|
||||
auto reverse = Config::getB("proc_reversed");
|
||||
const auto &filter = Config::getS("proc_filter");
|
||||
auto per_core = Config::getB("proc_per_core");
|
||||
auto tree = Config::getB("proc_tree");
|
||||
auto show_detailed = Config::getB("show_detailed");
|
||||
auto per_core = Config::getB("proc_per_core");
|
||||
auto tree = Config::getB("proc_tree");
|
||||
auto show_detailed = Config::getB("show_detailed");
|
||||
const size_t detailed_pid = Config::getI("detailed_pid");
|
||||
bool should_filter = current_filter != filter;
|
||||
if (should_filter) current_filter = filter;
|
||||
bool sorted_change = (sorting != current_sort or reverse != current_rev or should_filter);
|
||||
bool sorted_change = (sorting != current_sort or reverse != current_rev or should_filter);
|
||||
if (sorted_change) {
|
||||
current_sort = sorting;
|
||||
current_rev = reverse;
|
||||
|
@ -1155,7 +1154,7 @@ namespace Proc {
|
|||
{ //* Get CPU totals
|
||||
natural_t cpu_count;
|
||||
kern_return_t error;
|
||||
processor_cpu_load_info_data_t *cpu_load_info = NULL;
|
||||
processor_cpu_load_info_data_t *cpu_load_info = nullptr;
|
||||
MachProcessorInfo info{};
|
||||
error = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, &info.info_array, &info.info_count);
|
||||
if (error != KERN_SUCCESS) {
|
||||
|
@ -1177,13 +1176,13 @@ namespace Proc {
|
|||
size_t size = 0;
|
||||
const auto timeNow = time_micros();
|
||||
|
||||
if (sysctl(mib, 4, NULL, &size, NULL, 0) < 0 || size == 0) {
|
||||
if (sysctl(mib, 4, nullptr, &size, nullptr, 0) < 0 || size == 0) {
|
||||
Logger::error("Unable to get size of kproc_infos");
|
||||
}
|
||||
uint64_t cpu_t = 0;
|
||||
|
||||
std::unique_ptr<kinfo_proc[]> processes(new kinfo_proc[size / sizeof(kinfo_proc)]);
|
||||
if (sysctl(mib, 4, processes.get(), &size, NULL, 0) == 0) {
|
||||
if (sysctl(mib, 4, processes.get(), &size, nullptr, 0) == 0) {
|
||||
size_t count = size / sizeof(struct kinfo_proc);
|
||||
for (size_t i = 0; i < count; i++) { //* iterate over all processes in kinfo_proc
|
||||
struct kinfo_proc& kproc = processes.get()[i];
|
||||
|
@ -1214,7 +1213,7 @@ namespace Proc {
|
|||
std::unique_ptr<char[]> proc_chars(new char[Shared::arg_max]);
|
||||
int mib[] = {CTL_KERN, KERN_PROCARGS2, (int)pid};
|
||||
size_t argmax = Shared::arg_max;
|
||||
if (sysctl(mib, 3, proc_chars.get(), &argmax, NULL, 0) == 0) {
|
||||
if (sysctl(mib, 3, proc_chars.get(), &argmax, nullptr, 0) == 0) {
|
||||
int argc = 0;
|
||||
memcpy(&argc, &proc_chars.get()[0], sizeof(argc));
|
||||
std::string_view proc_args(proc_chars.get(), argmax);
|
||||
|
@ -1377,8 +1376,8 @@ namespace Tools {
|
|||
struct timeval ts, currTime;
|
||||
std::size_t len = sizeof(ts);
|
||||
int mib[2] = {CTL_KERN, KERN_BOOTTIME};
|
||||
if (sysctl(mib, 2, &ts, &len, NULL, 0) != -1) {
|
||||
gettimeofday(&currTime, NULL);
|
||||
if (sysctl(mib, 2, &ts, &len, nullptr, 0) != -1) {
|
||||
gettimeofday(&currTime, nullptr);
|
||||
return currTime.tv_sec - ts.tv_sec;
|
||||
}
|
||||
return 0.0;
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -74,8 +74,8 @@ double getValue(IOHIDServiceClientRef sc) {
|
|||
|
||||
long long Cpu::ThermalSensors::getSensors() {
|
||||
CFDictionaryRef thermalSensors = matching(0xff00, 5); // 65280_10 = FF00_16
|
||||
// thermalSensors's PrimaryUsagePage should be 0xff00 for M1 chip, instead of 0xff05
|
||||
// can be checked by ioreg -lfx
|
||||
// thermalSensors's PrimaryUsagePage should be 0xff00 for M1 chip, instead of 0xff05
|
||||
// can be checked by ioreg -lfx
|
||||
IOHIDEventSystemClientRef system = IOHIDEventSystemClientCreate(kCFAllocatorDefault);
|
||||
IOHIDEventSystemClientSetMatching(system, thermalSensors);
|
||||
CFArrayRef matchingsrvs = IOHIDEventSystemClientCopyServices(system);
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -18,6 +18,9 @@ tab-size = 4
|
|||
|
||||
#include "smc.hpp"
|
||||
|
||||
static constexpr size_t MaxIndexCount = sizeof("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ") - 1;
|
||||
static constexpr const char *KeyIndexes = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
|
||||
static UInt32 _strtoul(char *str, int size, int base) {
|
||||
UInt32 total = 0;
|
||||
int i;
|
||||
|
@ -34,20 +37,18 @@ static UInt32 _strtoul(char *str, int size, int base) {
|
|||
|
||||
static void _ultostr(char *str, UInt32 val) {
|
||||
str[0] = '\0';
|
||||
sprintf(str, "%c%c%c%c",
|
||||
(unsigned int)val >> 24,
|
||||
(unsigned int)val >> 16,
|
||||
(unsigned int)val >> 8,
|
||||
(unsigned int)val);
|
||||
snprintf(str, 5, "%c%c%c%c",
|
||||
(unsigned int)val >> 24,
|
||||
(unsigned int)val >> 16,
|
||||
(unsigned int)val >> 8,
|
||||
(unsigned int)val);
|
||||
}
|
||||
|
||||
namespace Cpu {
|
||||
|
||||
SMCConnection::SMCConnection() {
|
||||
IOMasterPort(kIOMasterPortDefault, &masterPort);
|
||||
|
||||
CFMutableDictionaryRef matchingDictionary = IOServiceMatching("AppleSMC");
|
||||
result = IOServiceGetMatchingServices(masterPort, matchingDictionary, &iterator);
|
||||
result = IOServiceGetMatchingServices(0, matchingDictionary, &iterator);
|
||||
if (result != kIOReturnSuccess) {
|
||||
throw std::runtime_error("failed to get AppleSMC");
|
||||
}
|
||||
|
@ -65,7 +66,7 @@ namespace Cpu {
|
|||
}
|
||||
}
|
||||
SMCConnection::~SMCConnection() {
|
||||
IOServiceClose(conn);
|
||||
IOServiceClose(conn);
|
||||
}
|
||||
|
||||
long long SMCConnection::getSMCTemp(char *key) {
|
||||
|
@ -92,12 +93,15 @@ namespace Cpu {
|
|||
long long SMCConnection::getTemp(int core) {
|
||||
char key[] = SMC_KEY_CPU_TEMP;
|
||||
if (core >= 0) {
|
||||
snprintf(key, 5, "TC%1dc", core);
|
||||
if ((size_t)core > MaxIndexCount) {
|
||||
return -1;
|
||||
}
|
||||
snprintf(key, 5, "TC%1cc", KeyIndexes[core]);
|
||||
}
|
||||
long long result = getSMCTemp(key);
|
||||
if (result == -1) {
|
||||
// try again with C
|
||||
snprintf(key, 5, "TC%1dC", core);
|
||||
snprintf(key, 5, "TC%1dC", KeyIndexes[core]);
|
||||
result = getSMCTemp(key);
|
||||
}
|
||||
return result;
|
||||
|
@ -132,7 +136,7 @@ namespace Cpu {
|
|||
|
||||
return kIOReturnSuccess;
|
||||
}
|
||||
|
||||
|
||||
kern_return_t SMCConnection::SMCCall(int index, SMCKeyData_t *inputStructure, SMCKeyData_t *outputStructure) {
|
||||
size_t structureInputSize;
|
||||
size_t structureOutputSize;
|
||||
|
@ -141,10 +145,10 @@ namespace Cpu {
|
|||
structureOutputSize = sizeof(SMCKeyData_t);
|
||||
|
||||
return IOConnectCallStructMethod(conn, index,
|
||||
// inputStructure
|
||||
inputStructure, structureInputSize,
|
||||
// ouputStructure
|
||||
outputStructure, &structureOutputSize);
|
||||
// inputStructure
|
||||
inputStructure, structureInputSize,
|
||||
// ouputStructure
|
||||
outputStructure, &structureOutputSize);
|
||||
}
|
||||
|
||||
} // namespace Cpu
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
@ -104,7 +104,7 @@ namespace Cpu {
|
|||
long long getTemp(int core);
|
||||
|
||||
private:
|
||||
kern_return_t SMCReadKey(UInt32Char_t key, SMCVal_t *val);
|
||||
kern_return_t SMCReadKey(UInt32Char_t key, SMCVal_t *val);
|
||||
long long getSMCTemp(char *key);
|
||||
kern_return_t SMCCall(int index, SMCKeyData_t *inputStructure, SMCKeyData_t *outputStructure);
|
||||
|
||||
|
|
Loading…
Reference in a new issue