Squashed commit of the following:

commit c296ac13cd
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

commit 9a1e760a66
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

commit 9c8af4df43
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

commit 8a49d8cf45
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

commit 1556388c83
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

commit d17e1a2dac
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Aug 25 16:18:39 2023 +0200

    fix some warnings

commit 4d8aa6b118
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Fri Aug 25 15:52:58 2023 +0200

    fix core check

commit 22e64caaff
Author: Jonathan Fouquart <jfouquart@hotmail.fr>
Date:   Fri Aug 25 09:37:49 2023 +0200

    Fix getting zfs pool name with '.' char in freebsd

commit 2217cbe143
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Aug 23 16:01:04 2023 +0200

    [macos] don't check /sys on macos

commit 008fcd889e
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Aug 23 16:05:00 2023 +0200

    also add g++13

commit 0fdca5eb03
Author: Jos Dehaes <jos.dehaes@gmail.com>
Date:   Wed Aug 23 15:54:07 2023 +0200

    support gcc13

commit dcbdb7360d
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

commit 1b126f55e3
Author: aristocratos <gnmjpl@gmail.com>
Date:   Fri Aug 4 01:08:27 2023 +0200

    Update Makefile for partial static compilation on freebsd

commit c8ec6bbb00
Author: aristocratos <gnmjpl@gmail.com>
Date:   Thu Aug 3 23:08:33 2023 +0200

    Fix freebsd nullptr changes and makefile for gcc12 and newer

commit 8a33aab588
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.

commit 94e5c02d11
Author: aristocratos <gnmjpl@gmail.com>
Date:   Thu Jul 27 20:51:21 2023 +0200

    Better text editing

commit 091c30ab2b
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

commit e4abcefbf9
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.

commit d53307f14c
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

commit 594f42b9eb
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

commit aca2e4be75
Author: aristocratos <gnmjpl@gmail.com>
Date:   Wed Jul 26 14:38:48 2023 +0200

    Fix whitespace indent -> tab indent

commit 33faa01910
Author: aristocratos <gnmjpl@gmail.com>
Date:   Wed Jul 26 14:34:15 2023 +0200

    Revert fmt submodule to static fmt folder in include

commit 53d6ebabc0
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:
aristocratos 2023-08-26 20:29:43 +02:00
parent 346c9e479b
commit bd5d697830
41 changed files with 16536 additions and 789 deletions

View file

@ -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
View file

@ -1,3 +0,0 @@
[submodule "lib/fmt"]
path = lib/fmt
url = https://github.com/fmtlib/fmt

View file

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

View file

@ -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
View 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
View 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

File diff suppressed because it is too large Load diff

633
include/fmt/color.h Normal file
View 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
View 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

File diff suppressed because it is too large Load diff

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

File diff suppressed because it is too large Load diff

451
include/fmt/os.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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 +0,0 @@
Subproject commit a54cb108d4634e768ad3ce0855313991ccc768de

View file

@ -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);
}

View file

@ -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);
}

View file

@ -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();

View file

@ -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;

View file

@ -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()();

View file

@ -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()});
}
}

View file

@ -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
*/

View file

@ -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;

View file

@ -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 {

View file

@ -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 {

View file

@ -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);
}

View file

@ -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";

View file

@ -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();

View file

@ -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()});
}
}
}

View file

@ -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>;

View file

@ -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(&currentTime, NULL);
gettimeofday(&currentTime, 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(&currentTime, NULL);
gettimeofday(&currentTime, 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;

View file

@ -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");
}
}

View file

@ -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(&currentTime, NULL);
gettimeofday(&currentTime, 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;

View file

@ -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);

View file

@ -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,

View file

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

View file

@ -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);