From 854b760f99c822617d52a55f91cdf642d4a7404e Mon Sep 17 00:00:00 2001 From: BurntRanch <69512353+BurntRanch@users.noreply.github.com> Date: Tue, 20 Aug 2024 21:07:54 +0300 Subject: [PATCH] New version v0.6.9 (dont you dare laugh) --- .clang-format | 113 +++--- Makefile | 4 +- compile_commands.json | 12 +- compile_flags.txt | 2 +- cpr | 2 +- include/args.hpp | 14 +- include/config.hpp | 73 ++-- include/fmt/args.h | 107 +++--- include/fmt/base.h | 660 +++++++++++++++---------------- include/fmt/chrono.h | 179 ++++----- include/fmt/color.h | 116 +++--- include/fmt/compile.h | 106 +++-- include/fmt/format-inl.h | 67 +++- include/fmt/format.h | 812 +++++++++++++++++---------------------- include/fmt/os.h | 159 ++++---- include/fmt/ostream.h | 34 +- include/fmt/printf.h | 69 ++-- include/fmt/ranges.h | 179 +++++---- include/fmt/std.h | 294 ++++++++------ include/fmt/xchar.h | 27 +- include/pacman.hpp | 25 +- include/switch_fnv1a.hpp | 265 +++++++++++++ include/taur.hpp | 93 ++--- include/toml++/toml.hpp | 108 ++++-- include/util.hpp | 338 ++++++++-------- src/args.cpp | 60 ++- src/config.cpp | 152 +++++--- src/fmt/format.cc | 1 + src/fmt/os.cc | 74 ++-- src/main.cpp | 527 +++++++++++++------------ src/pacman.cpp | 193 ++++++---- src/taur.cpp | 513 +++++++++++++++---------- src/util.cpp | 780 ++++++++++++++++++++++--------------- 33 files changed, 3434 insertions(+), 2724 deletions(-) create mode 100644 include/switch_fnv1a.hpp diff --git a/.clang-format b/.clang-format index 3326b57..51873e1 100644 --- a/.clang-format +++ b/.clang-format @@ -1,65 +1,70 @@ --- -Language: Cpp -BasedOnStyle: LLVM +# Based on Source SDK 2013 style conventions +BasedOnStyle: Google -AccessModifierOffset: -2 -AlignAfterOpenBracket: Align -AlignConsecutiveMacros: true +# Align assignments and similar statements AlignConsecutiveAssignments: true -AlignEscapedNewlines: Right -AlignOperands: false -AlignTrailingComments: true -AllowAllArgumentsOnNextLine: true -AllowAllConstructorInitializersOnNextLine: true -AllowAllParametersOfDeclarationOnNextLine: true -AllowShortBlocksOnASingleLine: true -AllowShortFunctionsOnASingleLine: Empty -AllowShortIfStatementsOnASingleLine: Never -AllowShortLambdasOnASingleLine: All -AllowShortLoopsOnASingleLine: false -AlwaysBreakAfterDefinitionReturnType: None -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -AlwaysBreakTemplateDeclarations: Yes -BreakBeforeBraces: Attach -BreakBeforeTernaryOperators: false -BreakConstructorInitializers: AfterColon -ColumnLimit: 180 -CompactNamespaces: false -ConstructorInitializerAllOnOneLineOrOnePerLine: false -ExperimentalAutoDetectBinPacking: false -FixNamespaceComments: false -IncludeBlocks: Preserve -IndentCaseLabels: true +AlignConsecutiveDeclarations: true + +# Align the * in declarations +DerivePointerAlignment: false +PointerAlignment: Left + +# Column limit for wrapping code +ColumnLimit: 120 + +# Indentation settings IndentWidth: 4 -PointerAlignment: Right -ReflowComments: false -ReferenceAlignment: Left -SortIncludes: true -SortUsingDeclarations: false -SpaceAfterCStyleCast: false -SpaceAfterLogicalNot: false -SpaceAfterTemplateKeyword: true -SpaceBeforeCtorInitializerColon: true -SpaceBeforeInheritanceColon: true +TabWidth: 4 +UseTab: Never + +# Bracing styles +BraceWrapping: + AfterClass: true + AfterControlStatement: true + AfterEnum: true + AfterFunction: true + AfterNamespace: true + AfterStruct: true + AfterUnion: true + BeforeCatch: true + BeforeElse: true + IndentBraces: false + +# Control spaces around various constructs +#SpacesInParens: Custom +#SpacesInParensOptions: +# InCStyleCasts: false +# InEmptyParentheses: false +# InConditionalStatements: true +# Other: false + SpaceBeforeParens: ControlStatements -SpaceBeforeRangeBasedForLoopColon: true +SpaceAfterCStyleCast: false +SpaceBeforeAssignmentOperators: true SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -SpacesInAngles: false -SpacesInCStyleCastParentheses: false -SpacesInContainerLiterals: false -SpacesInParentheses: false SpacesInSquareBrackets: false -Standard: Auto -TabWidth: 4 -UseTab: Never -AllowShortEnumsOnASingleLine: false +# Control formatting of C++11 features +Cpp11BracedListStyle: false -BraceWrapping: - AfterEnum: false +# Namespace indentation +NamespaceIndentation: None + +# Format comments +ReflowComments: true + +# Additional settings to match Source SDK 2013 style +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +BreakBeforeBraces: Allman +IndentCaseLabels: true -AlignConsecutiveDeclarations: AcrossEmptyLines +AlignConsecutiveShortCaseStatements: + Enabled: true + AcrossEmptyLines: false + AcrossComments: false + AlignCaseColons: false -NamespaceIndentation: All +AllowShortCaseLabelsOnASingleLine: true diff --git a/Makefile b/Makefile index 0ac2275..4bef7ea 100644 --- a/Makefile +++ b/Makefile @@ -14,14 +14,14 @@ else CXXFLAGS := -O2 $(CXXFLAGS) endif -VERSION = 0.6.8 +VERSION = 0.6.9 BRANCH = main SRC = $(sort $(wildcard src/*.cpp)) OBJ = $(SRC:.cpp=.o) CURL_LIBS ?= -lcurl -lnghttp3 -lnghttp2 -lidn2 -lssh2 -lssl -lcrypto -lpsl -lgssapi_krb5 -lzstd -lbrotlidec -lz # pkg-config --static --libs libcurl (but fixed) LDFLAGS += -L./$(BUILDDIR)/fmt -L./$(BUILDDIR)/cpr -lcpr -lalpm -lfmt $(CURL_LIBS) CXXFLAGS ?= -mtune=generic -march=native -CXXFLAGS += -funroll-all-loops -isystem include -std=c++20 $(VARS) -DVERSION=\"$(VERSION)\" -DBRANCH=\"$(BRANCH)\" -DLOCALEDIR=\"$(LOCALEDIR)\" +CXXFLAGS += -funroll-all-loops -isystem include -std=c++23 $(VARS) -DVERSION=\"$(VERSION)\" -DBRANCH=\"$(BRANCH)\" -DLOCALEDIR=\"$(LOCALEDIR)\" is_cpr_installed = $(shell ldconfig -p | grep libcpr > /dev/null || test -d $(BUILDDIR)/cpr && echo -n yes) diff --git a/compile_commands.json b/compile_commands.json index 2510fca..99ab58c 100644 --- a/compile_commands.json +++ b/compile_commands.json @@ -10,7 +10,7 @@ "include", "-std=c++20", "-DENABLE_NLS=1", - "-DVERSION=\"0.6.7\"", + "-DVERSION=\"0.6.9\"", "-DBRANCH=\"main\"", "-DLOCALEDIR=\"/usr/share/locale\"", "-c", @@ -33,7 +33,7 @@ "include", "-std=c++20", "-DENABLE_NLS=1", - "-DVERSION=\"0.6.7\"", + "-DVERSION=\"0.6.9\"", "-DBRANCH=\"main\"", "-DLOCALEDIR=\"/usr/share/locale\"", "-c", @@ -56,7 +56,7 @@ "include", "-std=c++20", "-DENABLE_NLS=1", - "-DVERSION=\"0.6.7\"", + "-DVERSION=\"0.6.9\"", "-DBRANCH=\"main\"", "-DLOCALEDIR=\"/usr/share/locale\"", "-c", @@ -79,7 +79,7 @@ "include", "-std=c++20", "-DENABLE_NLS=1", - "-DVERSION=\"0.6.7\"", + "-DVERSION=\"0.6.9\"", "-DBRANCH=\"main\"", "-DLOCALEDIR=\"/usr/share/locale\"", "-c", @@ -102,7 +102,7 @@ "include", "-std=c++20", "-DENABLE_NLS=1", - "-DVERSION=\"0.6.7\"", + "-DVERSION=\"0.6.9\"", "-DBRANCH=\"main\"", "-DLOCALEDIR=\"/usr/share/locale\"", "-c", @@ -125,7 +125,7 @@ "include", "-std=c++20", "-DENABLE_NLS=1", - "-DVERSION=\"0.6.7\"", + "-DVERSION=\"0.6.9\"", "-DBRANCH=\"main\"", "-DLOCALEDIR=\"/usr/share/locale\"", "-c", diff --git a/compile_flags.txt b/compile_flags.txt index 3c155ce..dcf5588 100644 --- a/compile_flags.txt +++ b/compile_flags.txt @@ -3,6 +3,6 @@ -Wall -std=c++20 -DENABLE_NLS=1 --DVERSION="0.6.7" +-DVERSION="0.6.9" -DBRANCH="main" -DLOCALEDIR="/usr/share/locale" diff --git a/cpr b/cpr index 3b15fa8..aa581c9 160000 --- a/cpr +++ b/cpr @@ -1 +1 @@ -Subproject commit 3b15fa82ea74739b574d705fea44959b58142eb8 +Subproject commit aa581c9d8060b2502707ad96a04f4829cc524fcb diff --git a/include/args.hpp b/include/args.hpp index 76ce4e7..59f13bb 100644 --- a/include/args.hpp +++ b/include/args.hpp @@ -20,20 +20,23 @@ #ifndef ARGS_HPP #define ARGS_HPP -#include "util.hpp" #include #include -enum { +#include "util.hpp" + +enum +{ OP_MAIN = 1, OP_SYNC, OP_REM, OP_QUERY, OP_UPGRADE, - OP_PACMAN, // when it's different from -S,R,Q we gonna use pacman + OP_PACMAN, // when it's different from -S,R,Q we gonna use pacman }; -enum { +enum +{ OP_ASK = 1000, OP_CACHEDIR, OP_AURONLY, @@ -55,7 +58,8 @@ enum { OP_NOSAVE, }; -struct Operation_t { +struct Operation_t +{ u_short op; u_short op_s_sync; u_short op_s_upgrade; diff --git a/include/config.hpp b/include/config.hpp index a16c51a..0ccfd33 100644 --- a/include/config.hpp +++ b/include/config.hpp @@ -5,24 +5,23 @@ #define TOML_ENABLE_FORMATTERS 0 #include + +#include #include #include #include #include -#include #include "fmt/color.h" #include "toml++/toml.hpp" -using std::string; -using std::vector; -using std::string_view; using std::filesystem::path; // so we don't need to include util.hpp for getConfigValue() -string expandVar(string& str); +std::string expandVar(std::string& str); -struct _color_t { +struct _color_t +{ fmt::rgb red; fmt::rgb green; fmt::rgb blue; @@ -45,51 +44,53 @@ struct _color_t { fmt::rgb index; }; -class Config { - public: - alpm_handle_t *handle = nullptr; - alpm_list_t *repos = nullptr; - string makepkgBin; - vector editor; - path cacheDir; - string pmConfig; - string sudo; - string git; - string makepkgConf; - bool aurOnly; - bool useGit; - bool colors; - bool secretRecipe; - bool debug; - bool quiet; - bool noconfirm; +class Config +{ +public: + alpm_handle_t* handle = nullptr; + alpm_list_t* repos = nullptr; + std::string makepkgBin; + std::vector editor; + path cacheDir; + std::string pmConfig; + std::string sudo; + std::string git; + std::string makepkgConf; + bool aurOnly; + bool useGit; + bool colors; + bool secretRecipe; + bool debug; + bool quiet; + bool noconfirm; // alpm transaction flags - int flags; + int flags; - Config(string_view configFile, string_view themeFile, string_view configDir); + Config(std::string_view configFile, std::string_view themeFile, std::string_view configDir); ~Config(); void initVars(); void initColors(); - void loadConfigFile(string_view filename); - void loadPacmanConfigFile(string filename); - void loadThemeFile(string_view filename); + void loadConfigFile(std::string_view filename); + void loadPacmanConfigFile(std::string filename); + void loadThemeFile(std::string_view filename); // stupid c++ that wants template functions in header template - T getConfigValue(const string& value, T fallback) { + T getConfigValue(const std::string& value, T fallback) + { std::optional ret = this->tbl.at_path(value).value(); - if constexpr (toml::is_string) // if we want to get a value that's a string + if constexpr (toml::is_string) // if we want to get a value that's a std::string return ret ? expandVar(ret.value()) : expandVar(fallback); else return ret.value_or(fallback); } - fmt::rgb getThemeValue(const string& value, const string& fallback); - string getThemeHexValue(const string& value, const string& fallback); + fmt::rgb getThemeValue(const std::string& value, const std::string& fallback); + std::string getThemeHexValue(const std::string& value, const std::string& fallback); - private: +private: toml::table tbl, theme_tbl; }; @@ -97,7 +98,7 @@ extern std::unique_ptr config; inline struct _color_t color; // we comment the default config values, just like /etc/pacman.conf -inline const constexpr string_view AUTOCONFIG = R"#([general] +inline constexpr std::string_view AUTOCONFIG = R"#([general] # All options are commented out with their default values listed. # If you wish to use different options values, uncomment and update those. # It's safe to remove any options you want, just remember their default value @@ -142,7 +143,7 @@ inline const constexpr string_view AUTOCONFIG = R"#([general] #MakepkgConf = "/etc/makepkg.conf" )#"; -inline const constexpr string_view AUTOTHEME = R"#([theme] +inline constexpr std::string_view AUTOTHEME = R"#([theme] red = "#ff2000" green = "#00ff00" blue = "#00aaff" diff --git a/include/fmt/args.h b/include/fmt/args.h index 71c3bb9..31a60e8 100644 --- a/include/fmt/args.h +++ b/include/fmt/args.h @@ -8,7 +8,7 @@ #ifndef FMT_ARGS_H_ #define FMT_ARGS_H_ -#ifndef FMT_IMPORT_STD +#ifndef FMT_MODULE # include // std::reference_wrapper # include // std::unique_ptr # include @@ -30,15 +30,18 @@ auto unwrap(const std::reference_wrapper& v) -> const T& { return static_cast(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 struct node { - virtual ~node() = default; - std::unique_ptr> next; - }; +// node is defined outside dynamic_arg_list to workaround a C2504 bug in MSVC +// 2022 (v17.10.0). +// +// 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 node is made a fake template. +template struct node { + virtual ~node() = default; + std::unique_ptr> next; +}; +class dynamic_arg_list { template struct typed_node : node<> { T value; @@ -64,14 +67,10 @@ class dynamic_arg_list { } // 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 + * A dynamic list of formatting arguments with storage. + * + * It can be implicitly converted into `fmt::basic_format_args` for passing + * into type-erased formatting functions such as `fmt::vformat`. */ template class dynamic_format_arg_store @@ -149,22 +148,20 @@ class dynamic_format_arg_store 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 store; - store.push_back(42); - store.push_back("abc"); - store.push_back(1.5f); - std::string result = fmt::vformat("{} and {} and {}", store); - \endrst - */ + * 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 store; + * store.push_back(42); + * store.push_back("abc"); + * store.push_back(1.5f); + * std::string result = fmt::vformat("{} and {} and {}", store); + */ template void push_back(const T& arg) { if (detail::const_check(need_copy::value)) emplace_arg(dynamic_args_.push>(arg)); @@ -173,20 +170,18 @@ class dynamic_format_arg_store } /** - \rst - Adds a reference to the argument into the dynamic store for later passing to - a formatting function. - - **Example**:: - - fmt::dynamic_format_arg_store 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 - */ + * Adds a reference to the argument into the dynamic store for later passing + * to a formatting function. + * + * **Example**: + * + * fmt::dynamic_format_arg_store 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" + */ template void push_back(std::reference_wrapper arg) { static_assert( need_copy::value, @@ -195,10 +190,10 @@ class dynamic_format_arg_store } /** - 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. - */ + * 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 void push_back(const detail::named_arg& arg) { const char_type* arg_name = @@ -211,19 +206,15 @@ class dynamic_format_arg_store } } - /** Erase all elements from the store */ + /// 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 - */ + /// Reserves space to store at least `new_cap` arguments including + /// `new_cap_named` named arguments. 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"); diff --git a/include/fmt/base.h b/include/fmt/base.h index 772bc02..86f3f5a 100644 --- a/include/fmt/base.h +++ b/include/fmt/base.h @@ -8,20 +8,22 @@ #ifndef FMT_BASE_H_ #define FMT_BASE_H_ -#include // CHAR_BIT -#include // FILE -#include // strlen +#if defined(FMT_IMPORT_STD) && !defined(FMT_MODULE) +# define FMT_MODULE +#endif + +#ifndef FMT_MODULE +# include // CHAR_BIT +# include // FILE +# include // strlen -#ifndef FMT_IMPORT_STD // is also included transitively from . # include // std::byte # include // std::enable_if -#else -import std; #endif // The fmt library version in the form major * 10000 + minor * 100 + patch. -#define FMT_VERSION 100202 +#define FMT_VERSION 110002 // Detect compiler versions. #if defined(__clang__) && !defined(__ibmxl__) @@ -145,10 +147,20 @@ import std; #elif defined(__cpp_nontype_template_args) && \ __cpp_nontype_template_args >= 201911L # define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 +#elif FMT_CLANG_VERSION >= 1200 && FMT_CPLUSPLUS >= 202002L +# define FMT_USE_NONTYPE_TEMPLATE_ARGS 1 #else # define FMT_USE_NONTYPE_TEMPLATE_ARGS 0 #endif +#ifdef FMT_USE_CONCEPTS +// Use the provided definition. +#elif defined(__cpp_concepts) +# define FMT_USE_CONCEPTS 1 +#else +# define FMT_USE_CONCEPTS 0 +#endif + // Check if exceptions are disabled. #ifdef FMT_EXCEPTIONS // Use the provided definition. @@ -250,7 +262,7 @@ import std; #ifndef FMT_BEGIN_NAMESPACE # define FMT_BEGIN_NAMESPACE \ namespace fmt { \ - inline namespace v10 { + inline namespace v11 { # define FMT_END_NAMESPACE \ } \ } @@ -276,7 +288,18 @@ import std; #endif #ifndef FMT_UNICODE -# define FMT_UNICODE !FMT_MSC_VERSION +# define FMT_UNICODE 1 +#endif + +// Check if rtti is available. +#ifndef FMT_USE_RTTI +// __RTTI is for EDG compilers. _CPPRTTI is for MSVC. +# if defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || defined(_CPPRTTI) || \ + defined(__INTEL_RTTI__) || defined(__RTTI) +# define FMT_USE_RTTI 1 +# else +# define FMT_USE_RTTI 0 +# endif #endif #define FMT_FWD(...) static_cast(__VA_ARGS__) @@ -418,15 +441,22 @@ struct is_std_string_like : std::false_type {}; template struct is_std_string_like().find_first_of( typename T::value_type(), 0))>> - : std::true_type {}; + : std::is_convertible().data()), + const typename T::value_type*> {}; -FMT_CONSTEXPR inline auto is_utf8() -> bool { - FMT_MSC_WARNING(suppress : 4566) constexpr unsigned char section[] = "\u00A7"; +// Returns true iff the literal encoding is UTF-8. +constexpr auto is_utf8_enabled() -> bool { // Avoid an MSVC sign extension bug: https://github.com/fmtlib/fmt/pull/2297. using uchar = unsigned char; - return FMT_UNICODE || (sizeof(section) == 3 && uchar(section[0]) == 0xC2 && - uchar(section[1]) == 0xA7); + return sizeof("\u00A7") == 3 && uchar("\u00A7"[0]) == 0xC2 && + uchar("\u00A7"[1]) == 0xA7; } +constexpr auto use_utf8() -> bool { + return !FMT_MSC_VERSION || is_utf8_enabled(); +} + +static_assert(!FMT_UNICODE || use_utf8(), + "Unicode support requires compiling with /utf-8"); template FMT_CONSTEXPR auto length(const Char* s) -> size_t { size_t len = 0; @@ -437,6 +467,7 @@ template FMT_CONSTEXPR auto length(const Char* s) -> size_t { template FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n) -> int { + if (!is_constant_evaluated() && sizeof(Char) == 1) return memcmp(s1, s2, n); for (; n != 0; ++s1, ++s2, --n) { if (*s1 < *s2) return -1; if (*s1 > *s2) return 1; @@ -444,18 +475,27 @@ FMT_CONSTEXPR auto compare(const Char* s1, const Char* s2, std::size_t n) return 0; } +namespace adl { +using namespace std; + +template +auto invoke_back_inserter() + -> decltype(back_inserter(std::declval())); +} // namespace adl + template struct is_back_insert_iterator : std::false_type {}; + template struct is_back_insert_iterator< - It, - bool_constant())), - It>::value>> : std::true_type {}; + It, bool_constant()), + It>::value>> : std::true_type {}; // Extracts a reference to the container from *insert_iterator. template -inline auto get_container(OutputIt it) -> typename OutputIt::container_type& { +inline FMT_CONSTEXPR20 auto get_container(OutputIt it) -> + typename OutputIt::container_type& { struct accessor : OutputIt { accessor(OutputIt base) : OutputIt(base) {} using OutputIt::container; @@ -468,11 +508,11 @@ inline auto get_container(OutputIt it) -> typename OutputIt::container_type& { template struct is_contiguous : std::false_type {}; /** - An implementation of ``std::basic_string_view`` for pre-C++17. It provides a - subset of the API. ``fmt::basic_string_view`` is used for format strings even - if ``std::string_view`` is available to prevent issues when a library is - compiled with a different ``-std`` option than the client code (which is not - recommended). + * An implementation of `std::basic_string_view` for pre-C++17. It provides a + * subset of the API. `fmt::basic_string_view` is used for format strings even + * if `std::basic_string_view` is available to prevent issues when a library is + * compiled with a different `-std` option than the client code (which is not + * recommended). */ FMT_EXPORT template class basic_string_view { @@ -486,15 +526,13 @@ template class basic_string_view { constexpr basic_string_view() noexcept : data_(nullptr), size_(0) {} - /** Constructs a string reference object from a C string and a size. */ + /// Constructs a string reference object from a C string and a size. constexpr basic_string_view(const Char* s, size_t count) noexcept : data_(s), size_(count) {} constexpr basic_string_view(std::nullptr_t) = delete; - /** - Constructs a string reference object from a C string. - */ + /// Constructs a string reference object from a C string. FMT_CONSTEXPR20 basic_string_view(const Char* s) : data_(s), @@ -503,20 +541,18 @@ template class basic_string_view { ? strlen(reinterpret_cast(s)) : detail::length(s)) {} - /** - Constructs a string reference from a ``std::basic_string`` or a - ``std::basic_string_view`` object. - */ + /// Constructs a string reference from a `std::basic_string` or a + /// `std::basic_string_view` object. template ::value&& std::is_same< typename S::value_type, Char>::value)> FMT_CONSTEXPR basic_string_view(const S& s) noexcept : data_(s.data()), size_(s.size()) {} - /** Returns a pointer to the string data. */ + /// Returns a pointer to the string data. constexpr auto data() const noexcept -> const Char* { return data_; } - /** Returns the string size. */ + /// Returns the string size. constexpr auto size() const noexcept -> size_t { return size_; } constexpr auto begin() const noexcept -> iterator { return data_; } @@ -575,7 +611,7 @@ template class basic_string_view { FMT_EXPORT using string_view = basic_string_view; -/** Specifies if ``T`` is a character type. Can be specialized by users. */ +/// Specifies if `T` is a character type. Can be specialized by users. FMT_EXPORT template struct is_char : std::false_type {}; template <> struct is_char : std::true_type {}; @@ -586,11 +622,12 @@ namespace detail { // to it, deducing Char. Explicitly convertible types such as the ones returned // from FMT_STRING are intentionally excluded. template ::value)> -auto to_string_view(const Char* s) -> basic_string_view { +constexpr auto to_string_view(const Char* s) -> basic_string_view { return s; } template ::value)> -auto to_string_view(const T& s) -> basic_string_view { +constexpr auto to_string_view(const T& s) + -> basic_string_view { return s; } template @@ -693,10 +730,8 @@ enum { }; } // namespace detail -/** - Reports a format error at compile time or, via a ``format_error`` exception, - at runtime. - */ +/// Reports a format error at compile time or, via a `format_error` exception, +/// at runtime. // This function is intentionally not constexpr to give a compile-time error. FMT_NORETURN FMT_API void report_error(const char* message); @@ -705,17 +740,15 @@ FMT_DEPRECATED FMT_NORETURN inline void throw_format_error( report_error(message); } -/** String's character (code unit) type. */ +/// String's character (code unit) type. template ()))> using char_t = typename V::value_type; /** - \rst - Parsing context consisting of a format string range being parsed and an - argument counter for automatic indexing. - You can use the ``format_parse_context`` type alias for ``char`` instead. - \endrst + * Parsing context consisting of a format string range being parsed and an + * argument counter for automatic indexing. + * You can use the `format_parse_context` type alias for `char` instead. */ FMT_EXPORT template class basic_format_parse_context { @@ -733,28 +766,22 @@ template class basic_format_parse_context { basic_string_view format_str, int next_arg_id = 0) : format_str_(format_str), next_arg_id_(next_arg_id) {} - /** - Returns an iterator to the beginning of the format string range being - parsed. - */ + /// Returns an iterator to the beginning of the format string range being + /// parsed. constexpr auto begin() const noexcept -> iterator { return format_str_.begin(); } - /** - Returns an iterator past the end of the format string range being parsed. - */ + /// Returns an iterator past the end of the format string range being parsed. constexpr auto end() const noexcept -> iterator { return format_str_.end(); } - /** Advances the begin iterator to ``it``. */ + /// Advances the begin iterator to `it`. FMT_CONSTEXPR void advance_to(iterator it) { format_str_.remove_prefix(detail::to_unsigned(it - begin())); } - /** - Reports an error if using the manual argument indexing; otherwise returns - the next argument index and switches to the automatic indexing. - */ + /// Reports an error if using the manual argument indexing; otherwise returns + /// the next argument index and switches to the automatic indexing. FMT_CONSTEXPR auto next_arg_id() -> int { if (next_arg_id_ < 0) { report_error("cannot switch from manual to automatic argument indexing"); @@ -765,10 +792,8 @@ template class basic_format_parse_context { return id; } - /** - Reports an error if using the automatic argument indexing; otherwise - switches to the manual indexing. - */ + /// Reports an error if using the automatic argument indexing; otherwise + /// switches to the manual indexing. FMT_CONSTEXPR void check_arg_id(int id) { if (next_arg_id_ > 0) { report_error("cannot switch from automatic to manual argument indexing"); @@ -823,12 +848,8 @@ class compile_parse_context : public basic_format_parse_context { } }; -/** - \rst - A contiguous memory buffer with an optional growing ability. It is an internal - class and shouldn't be used directly, only via `~fmt::basic_memory_buffer`. - \endrst - */ +/// A contiguous memory buffer with an optional growing ability. It is an +/// internal class and shouldn't be used directly, only via `memory_buffer`. template class buffer { private: T* ptr_; @@ -851,7 +872,7 @@ template class buffer { FMT_CONSTEXPR20 ~buffer() = default; buffer(buffer&&) = default; - /** Sets the buffer data and capacity. */ + /// Sets the buffer data and capacity. FMT_CONSTEXPR void set(T* buf_data, size_t buf_capacity) noexcept { ptr_ = buf_data; capacity_ = buf_capacity; @@ -870,27 +891,27 @@ template class buffer { auto begin() const noexcept -> const T* { return ptr_; } auto end() const noexcept -> const T* { return ptr_ + size_; } - /** Returns the size of this buffer. */ + /// Returns the size of this buffer. constexpr auto size() const noexcept -> size_t { return size_; } - /** Returns the capacity of this buffer. */ + /// Returns the capacity of this buffer. constexpr auto capacity() const noexcept -> size_t { return capacity_; } - /** Returns a pointer to the buffer data (not null-terminated). */ + /// Returns a pointer to the buffer data (not null-terminated). FMT_CONSTEXPR auto data() noexcept -> T* { return ptr_; } FMT_CONSTEXPR auto data() const noexcept -> const T* { return ptr_; } - /** Clears this buffer. */ - void clear() { size_ = 0; } + /// Clears this buffer. + FMT_CONSTEXPR void clear() { size_ = 0; } - // Tries resizing the buffer to contain *count* elements. If T is a POD type + // Tries resizing the buffer to contain `count` elements. If T is a POD type // the new elements may not be initialized. FMT_CONSTEXPR void try_resize(size_t count) { try_reserve(count); size_ = count <= capacity_ ? count : capacity_; } - // Tries increasing the buffer capacity to *new_capacity*. It can increase the + // Tries increasing the buffer capacity to `new_capacity`. It can increase the // capacity by a smaller amount than requested but guarantees there is space // for at least one additional element either by increasing the capacity or by // flushing the buffer if it is full. @@ -903,19 +924,24 @@ template class buffer { ptr_[size_++] = value; } - /** Appends data to the end of the buffer. */ - template void append(const U* begin, const U* end) { + /// Appends data to the end of the buffer. + template +// Workaround for Visual Studio 2019 to fix error C2893: Failed to specialize +// function template 'void fmt::v11::detail::buffer::append(const U *,const +// U *)' +#if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1930 + FMT_CONSTEXPR20 +#endif + void + append(const U* begin, const U* end) { while (begin != end) { auto count = to_unsigned(end - begin); try_reserve(size_ + count); auto free_cap = capacity_ - size_; if (free_cap < count) count = free_cap; - if (std::is_same::value) { - memcpy(ptr_ + size_, begin, count * sizeof(T)); - } else { - T* out = ptr_ + size_; - for (size_t i = 0; i < count; ++i) out[i] = begin[i]; - } + // A loop is faster than memcpy on small sizes. + T* out = ptr_ + size_; + for (size_t i = 0; i < count; ++i) out[i] = begin[i]; size_ += count; begin += count; } @@ -1086,9 +1112,11 @@ template class counting_buffer : public buffer { } public: - counting_buffer() : buffer(grow, data_, 0, buffer_size) {} + FMT_CONSTEXPR counting_buffer() : buffer(grow, data_, 0, buffer_size) {} - auto count() -> size_t { return count_ + this->size(); } + constexpr auto count() const noexcept -> size_t { + return count_ + this->size(); + } }; } // namespace detail @@ -1138,7 +1166,8 @@ template class basic_appender { private: detail::buffer* buffer_; - friend auto get_container(basic_appender app) -> detail::buffer& { + friend FMT_CONSTEXPR20 auto get_container(basic_appender app) + -> detail::buffer& { return *app.buffer_; } @@ -1148,47 +1177,31 @@ template class basic_appender { using difference_type = ptrdiff_t; using pointer = T*; using reference = T&; + using container_type = detail::buffer; FMT_UNCHECKED_ITERATOR(basic_appender); FMT_CONSTEXPR basic_appender(detail::buffer& buf) : buffer_(&buf) {} - auto operator=(T c) -> basic_appender& { + FMT_CONSTEXPR20 auto operator=(T c) -> basic_appender& { buffer_->push_back(c); return *this; } - auto operator*() -> basic_appender& { return *this; } - auto operator++() -> basic_appender& { return *this; } - auto operator++(int) -> basic_appender { return *this; } + FMT_CONSTEXPR20 auto operator*() -> basic_appender& { return *this; } + FMT_CONSTEXPR20 auto operator++() -> basic_appender& { return *this; } + FMT_CONSTEXPR20 auto operator++(int) -> basic_appender { return *this; } }; using appender = basic_appender; namespace detail { - -template -struct locking : std::true_type {}; template -struct locking>::nonlocking>> - : std::false_type {}; - -template FMT_CONSTEXPR inline auto is_locking() -> bool { - return locking::value; -} -template -FMT_CONSTEXPR inline auto is_locking() -> bool { - return locking::value || is_locking(); -} +struct is_back_insert_iterator> : std::true_type {}; // An optimized version of std::copy with the output value type (T). -template -auto copy(InputIt begin, InputIt end, appender out) -> appender { - get_container(out).append(begin, end); - return out; -} - template ::value)> -auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt { +FMT_CONSTEXPR20 auto copy(InputIt begin, InputIt end, OutputIt out) + -> OutputIt { get_container(out).append(begin, end); return out; } @@ -1200,14 +1213,6 @@ FMT_CONSTEXPR auto copy(InputIt begin, InputIt end, OutputIt out) -> OutputIt { return out; } -template -FMT_CONSTEXPR auto copy(const T* begin, const T* end, T* out) -> T* { - if (is_constant_evaluated()) return copy(begin, end, out); - auto size = to_unsigned(end - begin); - if (size > 0) memcpy(out, begin, size * sizeof(T)); - return out + size; -} - template FMT_CONSTEXPR auto copy(basic_string_view s, OutputIt out) -> OutputIt { return copy(s.begin(), s.end(), out); @@ -1229,12 +1234,25 @@ constexpr auto has_const_formatter() -> bool { return has_const_formatter_impl(static_cast(nullptr)); } +template +struct is_buffer_appender : std::false_type {}; +template +struct is_buffer_appender< + It, bool_constant< + is_back_insert_iterator::value && + std::is_base_of, + typename It::container_type>::value>> + : std::true_type {}; + // Maps an output iterator to a buffer. -template +template ::value)> auto get_buffer(OutputIt out) -> iterator_buffer { return iterator_buffer(out); } -template auto get_buffer(basic_appender out) -> buffer& { +template ::value)> +auto get_buffer(OutputIt out) -> buffer& { return get_container(out); } @@ -1466,6 +1484,12 @@ template struct arg_mapper { FMT_MAP_API auto map(void* val) -> const void* { return val; } FMT_MAP_API auto map(const void* val) -> const void* { return val; } + FMT_MAP_API auto map(volatile void* val) -> const void* { + return const_cast(val); + } + FMT_MAP_API auto map(const volatile void* val) -> const void* { + return const_cast(val); + } FMT_MAP_API auto map(std::nullptr_t val) -> const void* { return val; } // Use SFINAE instead of a const T* parameter to avoid a conflict with the @@ -1746,14 +1770,12 @@ template class basic_format_arg { } /** - \rst - Visits an argument dispatching to the appropriate visit method based on - the argument type. For example, if the argument type is ``double`` then - ``vis(value)`` will be called with the value of type ``double``. - \endrst - */ + * Visits an argument dispatching to the appropriate visit method based on + * the argument type. For example, if the argument type is `double` then + * `vis(value)` will be called with the value of type `double`. + */ template - FMT_CONSTEXPR auto visit(Visitor&& vis) -> decltype(vis(0)) { + FMT_CONSTEXPR FMT_INLINE auto visit(Visitor&& vis) const -> decltype(vis(0)) { switch (type_) { case detail::type::none_type: break; @@ -1809,14 +1831,12 @@ FMT_DEPRECATED FMT_CONSTEXPR auto visit_format_arg( } /** - \rst - A view of a collection of formatting arguments. To avoid lifetime issues it - should only be used as a parameter type in type-erased functions such as - ``vformat``:: - - void vlog(string_view format_str, format_args args); // OK - format_args args = make_format_args(); // Error: dangling reference - \endrst + * A view of a collection of formatting arguments. To avoid lifetime issues it + * should only be used as a parameter type in type-erased functions such as + * `vformat`: + * + * void vlog(fmt::string_view fmt, fmt::format_args args); // OK + * fmt::format_args args = fmt::make_format_args(); // Dangling reference */ template class basic_format_args { public: @@ -1848,18 +1868,14 @@ template class basic_format_args { FMT_CONSTEXPR auto type(int index) const -> detail::type { int shift = index * detail::packed_arg_bits; - unsigned int mask = (1 << detail::packed_arg_bits) - 1; + unsigned mask = (1 << detail::packed_arg_bits) - 1; return static_cast((desc_ >> shift) & mask); } public: constexpr basic_format_args() : desc_(0), args_(nullptr) {} - /** - \rst - Constructs a `basic_format_args` object from `~fmt::format_arg_store`. - \endrst - */ + /// Constructs a `basic_format_args` object from `format_arg_store`. template constexpr FMT_ALWAYS_INLINE basic_format_args( @@ -1874,25 +1890,16 @@ template class basic_format_args { store) : desc_(DESC), args_(store.args + (NUM_NAMED_ARGS != 0 ? 1 : 0)) {} - /** - \rst - Constructs a `basic_format_args` object from - `~fmt::dynamic_format_arg_store`. - \endrst - */ + /// Constructs a `basic_format_args` object from `dynamic_format_arg_store`. constexpr basic_format_args(const dynamic_format_arg_store& store) : desc_(store.get_types()), args_(store.data()) {} - /** - \rst - Constructs a `basic_format_args` object from a dynamic list of arguments. - \endrst - */ + /// Constructs a `basic_format_args` object from a dynamic list of arguments. constexpr basic_format_args(const format_arg* args, int count) : desc_(detail::is_unpacked_bit | detail::to_unsigned(count)), args_(args) {} - /** Returns the argument with the specified id. */ + /// Returns the argument with the specified id. FMT_CONSTEXPR auto get(int id) const -> format_arg { format_arg arg; if (!is_packed()) { @@ -1901,8 +1908,7 @@ template class basic_format_args { } if (static_cast(id) >= detail::max_packed_args) return arg; arg.type_ = type(id); - if (arg.type_ == detail::type::none_type) return arg; - arg.value_ = values_[id]; + if (arg.type_ != detail::type::none_type) arg.value_ = values_[id]; return arg; } @@ -1938,7 +1944,7 @@ class context { detail::locale_ref loc_; public: - /** The character type for the output. */ + /// The character type for the output. using char_type = char; using iterator = appender; @@ -1946,10 +1952,8 @@ class context { using parse_context_type = basic_format_parse_context; template using formatter_type = formatter; - /** - Constructs a ``basic_format_context`` object. References to the arguments - are stored in the object so make sure they have appropriate lifetimes. - */ + /// Constructs a `basic_format_context` object. References to the arguments + /// are stored in the object so make sure they have appropriate lifetimes. FMT_CONSTEXPR context(iterator out, basic_format_args ctx_args, detail::locale_ref loc = {}) : out_(out), args_(ctx_args), loc_(loc) {} @@ -1967,7 +1971,7 @@ class context { // Returns an iterator to the beginning of the output range. FMT_CONSTEXPR auto out() -> iterator { return out_; } - // Advances the begin iterator to ``it``. + // Advances the begin iterator to `it`. void advance_to(iterator) {} FMT_CONSTEXPR auto locale() -> detail::locale_ref { return loc_; } @@ -1990,13 +1994,15 @@ using is_formattable = bool_constant>() .map(std::declval()))>::value>; +#if FMT_USE_CONCEPTS +template +concept formattable = is_formattable, Char>::value; +#endif + /** - \rst - Constructs an object that stores references to arguments and can be implicitly - converted to `~fmt::format_args`. `Context` can be omitted in which case it - defaults to `~fmt::format_context`. See `~fmt::arg` for lifetime - considerations. - \endrst + * Constructs an object that stores references to arguments and can be + * implicitly converted to `format_args`. `Context` can be omitted in which case + * it defaults to `format_context`. See `arg` for lifetime considerations. */ // Take arguments by lvalue references to avoid some lifetime issues, e.g. // auto args = make_format_args(std::string()); @@ -2025,15 +2031,13 @@ constexpr auto make_format_args(T&... args) #endif /** - \rst - Returns a named argument to be used in a formatting function. - It should only be used in a call to a formatting function or - `dynamic_format_arg_store::push_back`. - - **Example**:: - - fmt::print("Elapsed time: {s:.2f} seconds", fmt::arg("s", 1.23)); - \endrst + * Returns a named argument to be used in a formatting function. + * It should only be used in a call to a formatting function or + * `dynamic_format_arg_store::push_back`. + * + * **Example**: + * + * fmt::print("The answer is {answer}.", fmt::arg("answer", 42)); */ template inline auto arg(const Char* name, const T& arg) -> detail::named_arg { @@ -2042,7 +2046,7 @@ inline auto arg(const Char* name, const T& arg) -> detail::named_arg { } FMT_END_EXPORT -/** An alias to ``basic_format_args``. */ +/// An alias for `basic_format_args`. // A separate type would result in shorter symbols but break ABI compatibility // between clang and gcc on ARM (#1919). FMT_EXPORT using format_args = basic_format_args; @@ -2068,6 +2072,21 @@ using sign_t = sign::type; namespace detail { +template +struct locking : bool_constant::value == + type::custom_type> {}; +template +struct locking>::nonlocking>> + : std::false_type {}; + +template FMT_CONSTEXPR inline auto is_locking() -> bool { + return locking::value; +} +template +FMT_CONSTEXPR inline auto is_locking() -> bool { + return locking::value || is_locking(); +} + template using unsigned_char = typename conditional_t::value, std::make_unsigned, @@ -2275,8 +2294,8 @@ template constexpr auto is_name_start(Char c) -> bool { } template -FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { +FMT_CONSTEXPR auto parse_arg_id(const Char* begin, const Char* end, + Handler&& handler) -> const Char* { Char c = *begin; if (c >= '0' && c <= '9') { int index = 0; @@ -2302,25 +2321,10 @@ FMT_CONSTEXPR auto do_parse_arg_id(const Char* begin, const Char* end, return it; } -template -FMT_CONSTEXPR auto parse_arg_id(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { - FMT_ASSERT(begin != end, ""); - Char c = *begin; - if (c != '}' && c != ':') return do_parse_arg_id(begin, end, handler); - handler.on_auto(); - return begin; -} - template struct dynamic_spec_id_handler { basic_format_parse_context& ctx; arg_ref& ref; - FMT_CONSTEXPR void on_auto() { - int id = ctx.next_arg_id(); - ref = arg_ref(id); - ctx.check_dynamic_spec(id); - } FMT_CONSTEXPR void on_index(int id) { ref = arg_ref(id); ctx.check_arg_id(id); @@ -2332,7 +2336,7 @@ template struct dynamic_spec_id_handler { } }; -// Parses [integer | "{" [arg_id] "}"]. +// Parses integer | "{" [arg_id] "}". template FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, int& value, arg_ref& ref, @@ -2341,15 +2345,24 @@ FMT_CONSTEXPR auto parse_dynamic_spec(const Char* begin, const Char* end, FMT_ASSERT(begin != end, ""); if ('0' <= *begin && *begin <= '9') { int val = parse_nonnegative_int(begin, end, -1); - if (val != -1) - value = val; - else - report_error("number is too big"); - } else if (*begin == '{') { - ++begin; - auto handler = dynamic_spec_id_handler{ctx, ref}; - if (begin != end) begin = parse_arg_id(begin, end, handler); - if (begin != end && *begin == '}') return ++begin; + if (val == -1) report_error("number is too big"); + value = val; + } else { + if (*begin == '{') { + ++begin; + if (begin != end) { + Char c = *begin; + if (c == '}' || c == ':') { + int id = ctx.next_arg_id(); + ref = arg_ref(id); + ctx.check_dynamic_spec(id); + } else { + begin = + parse_arg_id(begin, end, dynamic_spec_id_handler{ctx, ref}); + } + } + if (begin != end && *begin == '}') return ++begin; + } report_error("invalid format string"); } return begin; @@ -2361,11 +2374,11 @@ FMT_CONSTEXPR auto parse_precision(const Char* begin, const Char* end, basic_format_parse_context& ctx) -> const Char* { ++begin; - if (begin == end || *begin == '}') { + if (begin != end) + begin = parse_dynamic_spec(begin, end, value, ref, ctx); + else report_error("invalid precision"); - return begin; - } - return parse_dynamic_spec(begin, end, value, ref, ctx); + return begin; } enum class state { start, align, sign, hash, zero, width, precision, locale }; @@ -2398,14 +2411,11 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, constexpr auto integral_set = sint_set | uint_set | bool_set | char_set; struct { const Char*& begin; - dynamic_format_specs& specs; + format_specs& specs; type arg_type; FMT_CONSTEXPR auto operator()(pres pres_type, int set) -> const Char* { - if (!in(arg_type, set)) { - if (arg_type == type::none_type) return begin; - report_error("invalid format specifier"); - } + if (!in(arg_type, set)) report_error("invalid format specifier"); specs.type = pres_type; return begin + 1; } @@ -2421,35 +2431,23 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, ++begin; break; case '+': - case '-': + FMT_FALLTHROUGH; case ' ': - if (arg_type == type::none_type) return begin; + specs.sign = c == ' ' ? sign::space : sign::plus; + FMT_FALLTHROUGH; + case '-': enter_state(state::sign, in(arg_type, sint_set | float_set)); - switch (c) { - case '+': - specs.sign = sign::plus; - break; - case '-': - specs.sign = sign::minus; - break; - case ' ': - specs.sign = sign::space; - break; - } ++begin; break; case '#': - if (arg_type == type::none_type) return begin; enter_state(state::hash, is_arithmetic_type(arg_type)); specs.alt = true; ++begin; break; case '0': enter_state(state::zero); - if (!is_arithmetic_type(arg_type)) { - if (arg_type == type::none_type) return begin; + if (!is_arithmetic_type(arg_type)) report_error("format specifier requires numeric argument"); - } if (specs.align == align::none) { // Ignore 0 if align is specified for compatibility with std::format. specs.align = align::numeric; @@ -2471,14 +2469,12 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, begin = parse_dynamic_spec(begin, end, specs.width, specs.width_ref, ctx); break; case '.': - if (arg_type == type::none_type) return begin; enter_state(state::precision, in(arg_type, float_set | string_set | cstring_set)); begin = parse_precision(begin, end, specs.precision, specs.precision_ref, ctx); break; case 'L': - if (arg_type == type::none_type) return begin; enter_state(state::locale, is_arithmetic_type(arg_type)); specs.localized = true; ++begin; @@ -2556,39 +2552,53 @@ FMT_CONSTEXPR auto parse_format_specs(const Char* begin, const Char* end, } template -FMT_CONSTEXPR auto parse_replacement_field(const Char* begin, const Char* end, - Handler&& handler) -> const Char* { - struct id_adapter { - Handler& handler; - int arg_id; - - FMT_CONSTEXPR void on_auto() { arg_id = handler.on_arg_id(); } - FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); } - FMT_CONSTEXPR void on_name(basic_string_view id) { - arg_id = handler.on_arg_id(id); - } - }; - +FMT_CONSTEXPR FMT_INLINE auto parse_replacement_field(const Char* begin, + const Char* end, + Handler&& handler) + -> const Char* { ++begin; - if (begin == end) return handler.on_error("invalid format string"), end; - if (*begin == '}') { + if (begin == end) { + handler.on_error("invalid format string"); + return end; + } + int arg_id = 0; + switch (*begin) { + case '}': handler.on_replacement_field(handler.on_arg_id(), begin); - } else if (*begin == '{') { + return begin + 1; + case '{': handler.on_text(begin, begin + 1); - } else { - auto adapter = id_adapter{handler, 0}; + return begin + 1; + case ':': + arg_id = handler.on_arg_id(); + break; + default: { + struct id_adapter { + Handler& handler; + int arg_id; + + FMT_CONSTEXPR void on_index(int id) { arg_id = handler.on_arg_id(id); } + FMT_CONSTEXPR void on_name(basic_string_view id) { + arg_id = handler.on_arg_id(id); + } + } adapter = {handler, 0}; begin = parse_arg_id(begin, end, adapter); + arg_id = adapter.arg_id; Char c = begin != end ? *begin : Char(); if (c == '}') { - handler.on_replacement_field(adapter.arg_id, begin); - } else if (c == ':') { - begin = handler.on_format_specs(adapter.arg_id, begin + 1, end); - if (begin == end || *begin != '}') - return handler.on_error("unknown format specifier"), end; - } else { - return handler.on_error("missing '}' in format string"), end; + handler.on_replacement_field(arg_id, begin); + return begin + 1; } + if (c != ':') { + handler.on_error("missing '}' in format string"); + return end; + } + break; + } } + begin = handler.on_format_specs(arg_id, begin + 1, end); + if (begin == end || *begin != '}') + return handler.on_error("unknown format specifier"), end; return begin + 1; } @@ -2787,6 +2797,11 @@ void check_format_string(S format_str) { ignore_unused(error); } +// Report truncation to prevent silent data loss. +inline void report_truncation(bool truncated) { + if (truncated) report_error("output is truncated"); +} + // Use vformat_args and avoid type_identity to keep symbols short and workaround // a GCC <= 4.8 bug. template struct vformat_args { @@ -2847,7 +2862,7 @@ template struct runtime_format_string { basic_string_view str; }; -/** A compile-time format string. */ +/// A compile-time format string. template class basic_format_string { private: basic_string_view str_; @@ -2890,19 +2905,17 @@ inline auto runtime(string_view s) -> string_view { return s; } template using format_string = basic_format_string...>; /** - \rst - Creates a runtime format string. - - **Example**:: - - // Check format string at runtime instead of compile-time. - fmt::print(fmt::runtime("{:d}"), "I am not a number"); - \endrst + * Creates a runtime format string. + * + * **Example**: + * + * // Check format string at runtime instead of compile-time. + * fmt::print(fmt::runtime("{:d}"), "I am not a number"); */ inline auto runtime(string_view s) -> runtime_format_string<> { return {{s}}; } #endif -/** Formats a string and writes the output to ``out``. */ +/// Formats a string and writes the output to `out`. template , char>::value)> @@ -2914,16 +2927,14 @@ auto vformat_to(OutputIt&& out, string_view fmt, format_args args) } /** - \rst - Formats ``args`` according to specifications in ``fmt``, writes the result to - the output iterator ``out`` and returns the iterator past the end of the output - range. `format_to` does not append a terminating null character. - - **Example**:: - - auto out = std::vector(); - fmt::format_to(std::back_inserter(out), "{}", 42); - \endrst + * Formats `args` according to specifications in `fmt`, writes the result to + * the output iterator `out` and returns the iterator past the end of the output + * range. `format_to` does not append a terminating null character. + * + * **Example**: + * + * auto out = std::vector(); + * fmt::format_to(std::back_inserter(out), "{}", 42); */ template , @@ -2934,9 +2945,9 @@ FMT_INLINE auto format_to(OutputIt&& out, format_string fmt, T&&... args) } template struct format_to_n_result { - /** Iterator past the end of the output range. */ + /// Iterator past the end of the output range. OutputIt out; - /** Total (not truncated) output size. */ + /// Total (not truncated) output size. size_t size; }; @@ -2951,12 +2962,10 @@ auto vformat_to_n(OutputIt out, size_t n, string_view fmt, format_args args) } /** - \rst - Formats ``args`` according to specifications in ``fmt``, writes up to ``n`` - characters of the result to the output iterator ``out`` and returns the total - (not truncated) output size and the iterator past the end of the output range. - `format_to_n` does not append a terminating null character. - \endrst + * Formats `args` according to specifications in `fmt`, writes up to `n` + * characters of the result to the output iterator `out` and returns the total + * (not truncated) output size and the iterator past the end of the output + * range. `format_to_n` does not append a terminating null character. */ template ::value)> @@ -2967,14 +2976,21 @@ FMT_INLINE auto format_to_n(OutputIt out, size_t n, format_string fmt, template struct format_to_result { - /** Iterator pointing to just after the last successful write in the range. */ + /// Iterator pointing to just after the last successful write in the range. OutputIt out; - /** Specifies if the output was truncated. */ + /// Specifies if the output was truncated. bool truncated; - FMT_CONSTEXPR operator OutputIt&() & noexcept { return out; } - FMT_CONSTEXPR operator const OutputIt&() const& noexcept { return out; } - FMT_CONSTEXPR operator OutputIt&&() && noexcept { + FMT_CONSTEXPR operator OutputIt&() & { + detail::report_truncation(truncated); + return out; + } + FMT_CONSTEXPR operator const OutputIt&() const& { + detail::report_truncation(truncated); + return out; + } + FMT_CONSTEXPR operator OutputIt&&() && { + detail::report_truncation(truncated); return static_cast(out); } }; @@ -2993,7 +3009,7 @@ FMT_INLINE auto format_to(char (&out)[N], format_string fmt, T&&... args) return {result.out, result.size > N}; } -/** Returns the number of chars in the output of ``format(fmt, args...)``. */ +/// Returns the number of chars in the output of `format(fmt, args...)`. template FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, T&&... args) -> size_t { @@ -3004,60 +3020,52 @@ FMT_NODISCARD FMT_INLINE auto formatted_size(format_string fmt, FMT_API void vprint(string_view fmt, format_args args); FMT_API void vprint(FILE* f, string_view fmt, format_args args); -FMT_API void vprint_locked(FILE* f, string_view fmt, format_args args); +FMT_API void vprint_buffered(FILE* f, string_view fmt, format_args args); FMT_API void vprintln(FILE* f, string_view fmt, format_args args); /** - \rst - Formats ``args`` according to specifications in ``fmt`` and writes the output - to ``stdout``. - - **Example**:: - - fmt::print("Elapsed time: {0:.2f} seconds", 1.23); - \endrst + * Formats `args` according to specifications in `fmt` and writes the output + * to `stdout`. + * + * **Example**: + * + * fmt::print("The answer is {}.", 42); */ template FMT_INLINE void print(format_string fmt, T&&... args) { const auto& vargs = fmt::make_format_args(args...); - if (!detail::is_utf8()) return detail::vprint_mojibake(stdout, fmt, vargs); - return detail::is_locking() ? vprint(fmt, vargs) - : vprint_locked(stdout, fmt, vargs); + if (!detail::use_utf8()) return detail::vprint_mojibake(stdout, fmt, vargs); + return detail::is_locking() ? vprint_buffered(stdout, fmt, vargs) + : vprint(fmt, vargs); } /** - \rst - Formats ``args`` according to specifications in ``fmt`` and writes the - output to the file ``f``. - - **Example**:: - - fmt::print(stderr, "Don't {}!", "panic"); - \endrst + * Formats `args` according to specifications in `fmt` and writes the + * output to the file `f`. + * + * **Example**: + * + * fmt::print(stderr, "Don't {}!", "panic"); */ template FMT_INLINE void print(FILE* f, format_string fmt, T&&... args) { const auto& vargs = fmt::make_format_args(args...); - if (!detail::is_utf8()) return detail::vprint_mojibake(f, fmt, vargs); - return detail::is_locking() ? vprint(f, fmt, vargs) - : vprint_locked(f, fmt, vargs); + if (!detail::use_utf8()) return detail::vprint_mojibake(f, fmt, vargs); + return detail::is_locking() ? vprint_buffered(f, fmt, vargs) + : vprint(f, fmt, vargs); } -/** - Formats ``args`` according to specifications in ``fmt`` and writes the - output to the file ``f`` followed by a newline. - */ +/// Formats `args` according to specifications in `fmt` and writes the output +/// to the file `f` followed by a newline. template FMT_INLINE void println(FILE* f, format_string fmt, T&&... args) { const auto& vargs = fmt::make_format_args(args...); - return detail::is_utf8() ? vprintln(f, fmt, vargs) - : detail::vprint_mojibake(f, fmt, vargs, true); + return detail::use_utf8() ? vprintln(f, fmt, vargs) + : detail::vprint_mojibake(f, fmt, vargs, true); } -/** - Formats ``args`` according to specifications in ``fmt`` and writes the output - to ``stdout`` followed by a newline. - */ +/// Formats `args` according to specifications in `fmt` and writes the output +/// to `stdout` followed by a newline. template FMT_INLINE void println(format_string fmt, T&&... args) { return fmt::println(stdout, fmt, static_cast(args)...); diff --git a/include/fmt/chrono.h b/include/fmt/chrono.h index 88dd911..06b2093 100644 --- a/include/fmt/chrono.h +++ b/include/fmt/chrono.h @@ -8,7 +8,7 @@ #ifndef FMT_CHRONO_H_ #define FMT_CHRONO_H_ -#ifndef FMT_IMPORT_STD +#ifndef FMT_MODULE # include # include # include // std::isfinite @@ -96,10 +96,8 @@ FMT_CONSTEXPR auto lossless_integral_conversion(const From from, int& ec) return static_cast(from); } -/** - * converts From to To, without loss. If the dynamic value of from - * can't be converted to To without loss, ec is set. - */ +/// Converts From to To, without loss. If the dynamic value of from +/// can't be converted to To without loss, ec is set. template ::value && std::numeric_limits::is_signed != @@ -187,9 +185,7 @@ FMT_CONSTEXPR auto safe_float_conversion(const From from, int& ec) -> To { return from; } -/** - * safe duration cast between integral durations - */ +/// Safe duration cast between integral durations template ::value), FMT_ENABLE_IF(std::is_integral::value)> @@ -239,9 +235,7 @@ auto safe_duration_cast(std::chrono::duration from, return ec ? To() : To(tocount); } -/** - * safe duration_cast between floating point durations - */ +/// Safe duration_cast between floating point durations template ::value), FMT_ENABLE_IF(std::is_floating_point::value)> @@ -397,7 +391,7 @@ void write_codecvt(codecvt_result& out, string_view in_buf, template auto write_encoded_tm_str(OutputIt out, string_view in, const std::locale& loc) -> OutputIt { - if (detail::is_utf8() && loc != get_classic_locale()) { + if (detail::use_utf8() && loc != get_classic_locale()) { // char16_t and char32_t codecvts are broken in MSVC (linkage errors) and // gcc-4. #if FMT_MSC_VERSION != 0 || \ @@ -518,9 +512,9 @@ auto to_time_t( FMT_BEGIN_EXPORT /** - Converts given time since epoch as ``std::time_t`` value into calendar time, - expressed in local time. Unlike ``std::localtime``, this function is - thread-safe on most platforms. + * Converts given time since epoch as `std::time_t` value into calendar time, + * expressed in local time. Unlike `std::localtime`, this function is + * thread-safe on most platforms. */ inline auto localtime(std::time_t time) -> std::tm { struct dispatcher { @@ -567,9 +561,9 @@ inline auto localtime(std::chrono::local_time time) -> std::tm { #endif /** - Converts given time since epoch as ``std::time_t`` value into calendar time, - expressed in Coordinated Universal Time (UTC). Unlike ``std::gmtime``, this - function is thread-safe on most platforms. + * Converts given time since epoch as `std::time_t` value into calendar time, + * expressed in Coordinated Universal Time (UTC). Unlike `std::gmtime`, this + * function is thread-safe on most platforms. */ inline auto gmtime(std::time_t time) -> std::tm { struct dispatcher { @@ -655,7 +649,7 @@ FMT_CONSTEXPR inline auto get_units() -> const char* { if (std::is_same::value) return "fs"; if (std::is_same::value) return "ps"; if (std::is_same::value) return "ns"; - if (std::is_same::value) return "µs"; + if (std::is_same::value) return use_utf8() ? "µs" : "us"; if (std::is_same::value) return "ms"; if (std::is_same::value) return "cs"; if (std::is_same::value) return "ds"; @@ -682,12 +676,10 @@ enum class numeric_system { // Glibc extensions for formatting numeric values. enum class pad_type { - unspecified, + // Pad a numeric result string with zeros (the default). + zero, // Do not pad a numeric result string. none, - // Pad a numeric result string with zeros even if the conversion specifier - // character uses space-padding by default. - zero, // Pad a numeric result string with spaces. space, }; @@ -712,7 +704,7 @@ FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, if (*begin != '%') FMT_THROW(format_error("invalid format")); auto ptr = begin; while (ptr != end) { - pad_type pad = pad_type::unspecified; + pad_type pad = pad_type::zero; auto c = *ptr; if (c == '}') break; if (c != '%') { @@ -732,10 +724,6 @@ FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, pad = pad_type::none; ++ptr; break; - case '0': - pad = pad_type::zero; - ++ptr; - break; } if (ptr == end) FMT_THROW(format_error("invalid format")); c = *ptr++; @@ -795,22 +783,22 @@ FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, break; // Day of the year/month: case 'U': - handler.on_dec0_week_of_year(numeric_system::standard); + handler.on_dec0_week_of_year(numeric_system::standard, pad); break; case 'W': - handler.on_dec1_week_of_year(numeric_system::standard); + handler.on_dec1_week_of_year(numeric_system::standard, pad); break; case 'V': - handler.on_iso_week_of_year(numeric_system::standard); + handler.on_iso_week_of_year(numeric_system::standard, pad); break; case 'j': handler.on_day_of_year(); break; case 'd': - handler.on_day_of_month(numeric_system::standard); + handler.on_day_of_month(numeric_system::standard, pad); break; case 'e': - handler.on_day_of_month_space(numeric_system::standard); + handler.on_day_of_month(numeric_system::standard, pad_type::space); break; // Hour, minute, second: case 'H': @@ -907,19 +895,19 @@ FMT_CONSTEXPR auto parse_chrono_format(const Char* begin, const Char* end, handler.on_dec_month(numeric_system::alternative); break; case 'U': - handler.on_dec0_week_of_year(numeric_system::alternative); + handler.on_dec0_week_of_year(numeric_system::alternative, pad); break; case 'W': - handler.on_dec1_week_of_year(numeric_system::alternative); + handler.on_dec1_week_of_year(numeric_system::alternative, pad); break; case 'V': - handler.on_iso_week_of_year(numeric_system::alternative); + handler.on_iso_week_of_year(numeric_system::alternative, pad); break; case 'd': - handler.on_day_of_month(numeric_system::alternative); + handler.on_day_of_month(numeric_system::alternative, pad); break; case 'e': - handler.on_day_of_month_space(numeric_system::alternative); + handler.on_day_of_month(numeric_system::alternative, pad_type::space); break; case 'w': handler.on_dec0_weekday(numeric_system::alternative); @@ -972,12 +960,19 @@ template struct null_chrono_spec_handler { FMT_CONSTEXPR void on_abbr_month() { unsupported(); } FMT_CONSTEXPR void on_full_month() { unsupported(); } FMT_CONSTEXPR void on_dec_month(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) { + unsupported(); + } + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) { + unsupported(); + } + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) { + unsupported(); + } FMT_CONSTEXPR void on_day_of_year() { unsupported(); } - FMT_CONSTEXPR void on_day_of_month(numeric_system) { unsupported(); } - FMT_CONSTEXPR void on_day_of_month_space(numeric_system) { unsupported(); } + FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) { + unsupported(); + } FMT_CONSTEXPR void on_24_hour(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_12_hour(numeric_system) { unsupported(); } FMT_CONSTEXPR void on_minute(numeric_system) { unsupported(); } @@ -1015,12 +1010,11 @@ struct tm_format_checker : null_chrono_spec_handler { FMT_CONSTEXPR void on_abbr_month() {} FMT_CONSTEXPR void on_full_month() {} FMT_CONSTEXPR void on_dec_month(numeric_system) {} - FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system) {} - FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system) {} - FMT_CONSTEXPR void on_iso_week_of_year(numeric_system) {} + FMT_CONSTEXPR void on_dec0_week_of_year(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_dec1_week_of_year(numeric_system, pad_type) {} + FMT_CONSTEXPR void on_iso_week_of_year(numeric_system, pad_type) {} FMT_CONSTEXPR void on_day_of_year() {} - FMT_CONSTEXPR void on_day_of_month(numeric_system) {} - FMT_CONSTEXPR void on_day_of_month_space(numeric_system) {} + FMT_CONSTEXPR void on_day_of_month(numeric_system, pad_type) {} FMT_CONSTEXPR void on_24_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_12_hour(numeric_system, pad_type) {} FMT_CONSTEXPR void on_minute(numeric_system, pad_type) {} @@ -1153,7 +1147,7 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { std::chrono::seconds::period>::value) { *out++ = '.'; out = detail::fill_n(out, leading_zeroes, '0'); - out = format_decimal(out, n, num_digits).end; + out = format_decimal(out, n, num_digits); } } else if (precision > 0) { *out++ = '.'; @@ -1164,12 +1158,12 @@ void write_fractional_seconds(OutputIt& out, Duration d, int precision = -1) { int num_truncated_digits = num_digits - remaining; n /= to_unsigned(detail::pow10(to_unsigned(num_truncated_digits))); if (n) { - out = format_decimal(out, n, remaining).end; + out = format_decimal(out, n, remaining); } return; } if (n) { - out = format_decimal(out, n, num_digits).end; + out = format_decimal(out, n, num_digits); remaining -= num_digits; } out = detail::fill_n(out, remaining, '0'); @@ -1325,7 +1319,7 @@ class tm_writer { const int num_digits = count_digits(n); if (width > num_digits) out_ = detail::fill_n(out_, width - num_digits, '0'); - out_ = format_decimal(out_, n, num_digits).end; + out_ = format_decimal(out_, n, num_digits); } void write_year(long long year) { if (year >= 0 && year < 10000) { @@ -1454,7 +1448,7 @@ class tm_writer { *out_++ = ' '; on_abbr_month(); *out_++ = ' '; - on_day_of_month_space(numeric_system::standard); + on_day_of_month(numeric_system::standard, pad_type::space); *out_++ = ' '; on_iso_time(); *out_++ = ' '; @@ -1487,7 +1481,7 @@ class tm_writer { char buf[10]; size_t offset = 0; if (year >= 0 && year < 10000) { - copy2(buf, digits2(static_cast(year / 100))); + write2digits(buf, static_cast(year / 100)); } else { offset = 4; write_year_extended(year); @@ -1541,24 +1535,26 @@ class tm_writer { format_localized('m', 'O'); } - void on_dec0_week_of_year(numeric_system ns) { + void on_dec0_week_of_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) - return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week); + return write2((tm_yday() + days_per_week - tm_wday()) / days_per_week, + pad); format_localized('U', 'O'); } - void on_dec1_week_of_year(numeric_system ns) { + void on_dec1_week_of_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) { auto wday = tm_wday(); write2((tm_yday() + days_per_week - (wday == 0 ? (days_per_week - 1) : (wday - 1))) / - days_per_week); + days_per_week, + pad); } else { format_localized('W', 'O'); } } - void on_iso_week_of_year(numeric_system ns) { + void on_iso_week_of_year(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) - return write2(tm_iso_week_of_year()); + return write2(tm_iso_week_of_year(), pad); format_localized('V', 'O'); } @@ -1572,20 +1568,11 @@ class tm_writer { write1(yday / 100); write2(yday % 100); } - void on_day_of_month(numeric_system ns) { - if (is_classic_ || ns == numeric_system::standard) return write2(tm_mday()); + void on_day_of_month(numeric_system ns, pad_type pad) { + if (is_classic_ || ns == numeric_system::standard) + return write2(tm_mday(), pad); format_localized('d', 'O'); } - void on_day_of_month_space(numeric_system ns) { - if (is_classic_ || ns == numeric_system::standard) { - auto mday = to_unsigned(tm_mday()) % 100; - const char* d2 = digits2(mday); - *out_++ = mday < 10 ? ' ' : d2[0]; - *out_++ = d2[1]; - } else { - format_localized('e', 'O'); - } - } void on_24_hour(numeric_system ns, pad_type pad) { if (is_classic_ || ns == numeric_system::standard) @@ -1644,7 +1631,7 @@ class tm_writer { void on_iso_time() { on_24_hour_time(); *out_++ = ':'; - on_second(numeric_system::standard, pad_type::unspecified); + on_second(numeric_system::standard, pad_type::zero); } void on_am_pm() { @@ -1787,8 +1774,10 @@ class get_locale { public: get_locale(bool localized, locale_ref loc) : has_locale_(localized) { +#ifndef FMT_STATIC_THOUSANDS_SEPARATOR if (localized) ::new (&locale_) std::locale(loc.template get()); +#endif } ~get_locale() { if (has_locale_) locale_.~locale(); @@ -1883,7 +1872,7 @@ struct chrono_formatter { } } - void write(Rep value, int width, pad_type pad = pad_type::unspecified) { + void write(Rep value, int width, pad_type pad = pad_type::zero) { write_sign(); if (isnan(value)) return write_nan(); uint32_or_64_or_128_t n = @@ -1892,7 +1881,7 @@ struct chrono_formatter { if (width > num_digits) { out = detail::write_padding(out, pad, width - num_digits); } - out = format_decimal(out, n, num_digits).end; + out = format_decimal(out, n, num_digits); } void write_nan() { std::copy_n("nan", 3, out); } @@ -1933,11 +1922,10 @@ struct chrono_formatter { void on_iso_week_based_year() {} void on_iso_week_based_short_year() {} void on_dec_month(numeric_system) {} - void on_dec0_week_of_year(numeric_system) {} - void on_dec1_week_of_year(numeric_system) {} - void on_iso_week_of_year(numeric_system) {} - void on_day_of_month(numeric_system) {} - void on_day_of_month_space(numeric_system) {} + void on_dec0_week_of_year(numeric_system, pad_type) {} + void on_dec1_week_of_year(numeric_system, pad_type) {} + void on_iso_week_of_year(numeric_system, pad_type) {} + void on_day_of_month(numeric_system, pad_type) {} void on_day_of_year() { if (handle_nan_inf()) return; @@ -2017,7 +2005,7 @@ struct chrono_formatter { on_24_hour_time(); *out++ = ':'; if (handle_nan_inf()) return; - on_second(numeric_system::standard, pad_type::unspecified); + on_second(numeric_system::standard, pad_type::zero); } void on_am_pm() { @@ -2156,7 +2144,7 @@ struct formatter : private formatter { if (use_tm_formatter_) return formatter::format(time, ctx); detail::get_locale loc(false, ctx.locale()); auto w = detail::tm_writer(loc, ctx.out(), time); - w.on_day_of_month(detail::numeric_system::standard); + w.on_day_of_month(detail::numeric_system::standard, detail::pad_type::zero); return w.out(); } }; @@ -2263,8 +2251,11 @@ struct formatter, Char> { it = detail::parse_align(it, end, specs_); if (it == end) return it; - it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); - if (it == end) return it; + Char c = *it; + if ((c >= '0' && c <= '9') || c == '{') { + it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); + if (it == end) return it; + } auto checker = detail::chrono_format_checker(); if (*it == '.') { @@ -2291,11 +2282,9 @@ struct formatter, Char> { // As a possible future optimization, we could avoid extra copying if width // is not specified. auto buf = basic_memory_buffer(); - auto out = std::back_inserter(buf); - detail::handle_dynamic_spec(specs.width, width_ref_, - ctx); - detail::handle_dynamic_spec(precision, - precision_ref_, ctx); + auto out = basic_appender(buf); + detail::handle_dynamic_spec(specs.width, width_ref_, ctx); + detail::handle_dynamic_spec(precision, precision_ref_, ctx); if (begin == end || *begin == '}') { out = detail::format_duration_value(out, d.count(), precision); detail::format_duration_unit(out); @@ -2400,9 +2389,8 @@ template struct formatter { const Duration* subsecs) const -> decltype(ctx.out()) { auto specs = specs_; auto buf = basic_memory_buffer(); - auto out = std::back_inserter(buf); - detail::handle_dynamic_spec(specs.width, width_ref_, - ctx); + auto out = basic_appender(buf); + detail::handle_dynamic_spec(specs.width, width_ref_, ctx); auto loc_ref = ctx.locale(); detail::get_locale loc(static_cast(loc_ref), loc_ref); @@ -2422,8 +2410,11 @@ template struct formatter { it = detail::parse_align(it, end, specs_); if (it == end) return it; - it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); - if (it == end) return it; + Char c = *it; + if ((c >= '0' && c <= '9') || c == '{') { + it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); + if (it == end) return it; + } end = detail::parse_chrono_format(it, end, detail::tm_format_checker()); // Replace the default format_str only if the new spec is not empty. diff --git a/include/fmt/color.h b/include/fmt/color.h index b71fafa..b8ef2d3 100644 --- a/include/fmt/color.h +++ b/include/fmt/color.h @@ -187,7 +187,7 @@ enum class emphasis : uint8_t { strikethrough = 1 << 7, }; -inline bool disable_colors = true; +inline bool disable_colors = false; // rgb is a struct for red, green and blue colors. // Using the name "rgb" makes some editors show the color in a tooltip. @@ -229,7 +229,7 @@ struct color_type { }; } // namespace detail -/** A text style consisting of foreground and background colors and emphasis. */ +/// A text style consisting of foreground and background colors and emphasis. class text_style { public: FMT_CONSTEXPR text_style(emphasis em = emphasis()) noexcept @@ -312,13 +312,13 @@ class text_style { emphasis ems; }; -/** Creates a text style from the foreground (text) color. */ +/// Creates a text style from the foreground (text) color. FMT_CONSTEXPR inline auto fg(detail::color_type foreground) noexcept -> text_style { return text_style(true, foreground); } -/** Creates a text style from the background color. */ +/// Creates a text style from the background color. FMT_CONSTEXPR inline auto bg(detail::color_type background) noexcept -> text_style { return text_style(false, background); @@ -446,7 +446,6 @@ template void vformat_to( buffer& buf, const text_style& ts, basic_string_view format_str, basic_format_args>> args) { - if (disable_colors) { detail::vformat_to(buf, format_str, args, {}); return; @@ -481,15 +480,13 @@ inline void vprint(FILE* f, const text_style& ts, string_view fmt, } /** - \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 + * 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); */ template void print(FILE* f, const text_style& ts, format_string fmt, @@ -498,15 +495,13 @@ void print(FILE* f, const text_style& ts, format_string fmt, } /** - \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 + * 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); */ template void print(const text_style& ts, format_string fmt, T&&... args) { @@ -554,26 +549,24 @@ inline auto vformat(const text_style& ts, string_view fmt, format_args args) } /** - \rst - Formats arguments and returns the result as a string using ANSI - escape sequences to specify text formatting. - - **Example**:: - - #include - std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), - "The answer is {}", 42); - \endrst -*/ + * Formats arguments and returns the result as a string using ANSI escape + * sequences to specify text formatting. + * + * **Example**: + * + * ``` + * #include + * std::string message = fmt::format(fmt::emphasis::bold | fg(fmt::color::red), + * "The answer is {}", 42); + * ``` + */ template inline auto format(const text_style& ts, format_string fmt, T&&... args) -> std::string { return fmt::vformat(ts, fmt, fmt::make_format_args(args...)); } -/** - Formats a string with the given text_style and writes the output to ``out``. - */ +/// Formats a string with the given text_style and writes the output to `out`. template ::value)> auto vformat_to(OutputIt out, const text_style& ts, string_view fmt, @@ -584,17 +577,15 @@ auto vformat_to(OutputIt out, const text_style& ts, string_view fmt, } /** - \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 out; - fmt::format_to(std::back_inserter(out), - fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); - \endrst -*/ + * 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 out; + * fmt::format_to(std::back_inserter(out), + * fmt::emphasis::bold | fg(fmt::color::red), "{}", 42); + */ template ::value)> inline auto format_to(OutputIt out, const text_style& ts, @@ -608,47 +599,44 @@ struct formatter, Char> : formatter { auto format(const detail::styled_arg& 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(ts.get_emphasis()); - out = std::copy(emphasis.begin(), emphasis.end(), out); + out = detail::copy(emphasis.begin(), emphasis.end(), out); } if (ts.has_foreground()) { has_style = true; auto foreground = detail::make_foreground_color(ts.get_foreground()); - out = std::copy(foreground.begin(), foreground.end(), out); + out = detail::copy(foreground.begin(), foreground.end(), out); } if (ts.has_background()) { has_style = true; auto background = detail::make_background_color(ts.get_background()); - out = std::copy(background.begin(), background.end(), out); + out = detail::copy(background.begin(), background.end(), out); } - out = formatter::format(value, ctx); + out = formatter::format(arg.value, ctx); if (has_style) { auto reset_color = string_view("\x1b[0m"); - out = std::copy(reset_color.begin(), reset_color.end(), out); + out = detail::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 + * 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))); */ template FMT_CONSTEXPR auto styled(const T& value, text_style ts) diff --git a/include/fmt/compile.h b/include/fmt/compile.h index 65c68a4..49d077e 100644 --- a/include/fmt/compile.h +++ b/include/fmt/compile.h @@ -8,43 +8,35 @@ #ifndef FMT_COMPILE_H_ #define FMT_COMPILE_H_ -#ifndef FMT_IMPORT_STD +#ifndef FMT_MODULE # include // std::back_inserter #endif #include "format.h" FMT_BEGIN_NAMESPACE -namespace detail { - -template -FMT_CONSTEXPR inline auto copy(InputIt begin, InputIt end, counting_iterator it) - -> counting_iterator { - return it + (end - begin); -} // A compile-time string which is compiled into fast formatting code. -class compiled_string {}; +FMT_EXPORT class compiled_string {}; + +namespace detail { template struct is_compiled_string : std::is_base_of {}; /** - \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 + * 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); */ #if defined(__cpp_if_constexpr) && defined(__cpp_return_type_deduction) -# define FMT_COMPILE(s) \ - FMT_STRING_IMPL(s, fmt::detail::compiled_string, explicit) +# define FMT_COMPILE(s) FMT_STRING_IMPL(s, fmt::compiled_string, explicit) #else # define FMT_COMPILE(s) FMT_STRING(s) #endif @@ -148,7 +140,7 @@ template struct field { template constexpr OutputIt format(OutputIt out, const Args&... args) const { const T& arg = get_arg_checked(args...); - if constexpr (std::is_convertible_v>) { + if constexpr (std::is_convertible>::value) { auto s = basic_string_view(arg); return copy(s.begin(), s.end(), out); } @@ -240,13 +232,12 @@ constexpr size_t parse_text(basic_string_view str, size_t pos) { } template -constexpr auto compile_format_string(S format_str); +constexpr auto compile_format_string(S fmt); template -constexpr auto parse_tail(T head, S format_str) { - if constexpr (POS != - basic_string_view(format_str).size()) { - constexpr auto tail = compile_format_string(format_str); +constexpr auto parse_tail(T head, S fmt) { + if constexpr (POS != basic_string_view(fmt).size()) { + constexpr auto tail = compile_format_string(fmt); if constexpr (std::is_same, unknown_format>()) return tail; @@ -317,14 +308,13 @@ struct field_type::value>> { template -constexpr auto parse_replacement_field_then_tail(S format_str) { +constexpr auto parse_replacement_field_then_tail(S fmt) { using char_type = typename S::char_type; - constexpr auto str = basic_string_view(format_str); + constexpr auto str = basic_string_view(fmt); constexpr char_type c = END_POS != str.size() ? str[END_POS] : char_type(); if constexpr (c == '}') { return parse_tail( - field::type, ARG_INDEX>(), - format_str); + field::type, ARG_INDEX>(), fmt); } else if constexpr (c != ':') { FMT_THROW(format_error("expected ':'")); } else { @@ -337,7 +327,7 @@ constexpr auto parse_replacement_field_then_tail(S format_str) { return parse_tail( spec_field::type, ARG_INDEX>{ result.fmt}, - format_str); + fmt); } } } @@ -345,22 +335,21 @@ constexpr auto parse_replacement_field_then_tail(S format_str) { // Compiles a non-empty format string and returns the compiled representation // or unknown_format() on unrecognized input. template -constexpr auto compile_format_string(S format_str) { +constexpr auto compile_format_string(S fmt) { using char_type = typename S::char_type; - constexpr auto str = basic_string_view(format_str); + constexpr auto str = basic_string_view(fmt); 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(make_text(str, POS, 1), format_str); + return parse_tail(make_text(str, POS, 1), fmt); } 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, Args, - POS + 1, ID, next_id>( - format_str); + POS + 1, ID, next_id>(fmt); } else { constexpr auto arg_id_result = parse_arg_id(str.data() + POS + 1, str.data() + str.size()); @@ -376,7 +365,7 @@ constexpr auto compile_format_string(S format_str) { return parse_replacement_field_then_tail, Args, arg_id_end_pos, arg_index, manual_indexing_id>( - format_str); + fmt); } 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{}); @@ -385,11 +374,11 @@ constexpr auto compile_format_string(S format_str) { ID != manual_indexing_id ? ID + 1 : manual_indexing_id; return parse_replacement_field_then_tail< decltype(get_type::value), Args, arg_id_end_pos, - arg_index, next_id>(format_str); + arg_index, next_id>(fmt); } else if constexpr (c == '}') { return parse_tail( runtime_named_field{arg_id_result.arg_id.val.name}, - format_str); + fmt); } else if constexpr (c == ':') { return unknown_format(); // no type info for specs parsing } @@ -398,29 +387,26 @@ constexpr auto compile_format_string(S format_str) { } else if constexpr (str[POS] == '}') { if constexpr (POS + 1 == str.size()) FMT_THROW(format_error("unmatched '}' in format string")); - return parse_tail(make_text(str, POS, 1), format_str); + return parse_tail(make_text(str, POS, 1), fmt); } else { constexpr auto end = parse_text(str, POS + 1); if constexpr (end - POS > 1) { - return parse_tail(make_text(str, POS, end - POS), - format_str); + return parse_tail(make_text(str, POS, end - POS), fmt); } else { - return parse_tail(code_unit{str[POS]}, - format_str); + return parse_tail(code_unit{str[POS]}, fmt); } } } template ::value)> -constexpr auto compile(S format_str) { - constexpr auto str = basic_string_view(format_str); +constexpr auto compile(S fmt) { + constexpr auto str = basic_string_view(fmt); if constexpr (str.size() == 0) { return detail::make_text(str, 0, 0); } else { constexpr auto result = - detail::compile_format_string, 0, 0>( - format_str); + detail::compile_format_string, 0, 0>(fmt); return result; } } @@ -492,35 +478,35 @@ FMT_CONSTEXPR OutputIt format_to(OutputIt out, const S&, Args&&... args) { template ::value)> -auto format_to_n(OutputIt out, size_t n, const S& format_str, Args&&... args) +auto format_to_n(OutputIt out, size_t n, const S& fmt, Args&&... args) -> format_to_n_result { using traits = detail::fixed_buffer_traits; auto buf = detail::iterator_buffer(out, n); - fmt::format_to(std::back_inserter(buf), format_str, - std::forward(args)...); + fmt::format_to(std::back_inserter(buf), fmt, std::forward(args)...); return {buf.out(), buf.count()}; } template ::value)> -FMT_CONSTEXPR20 auto formatted_size(const S& format_str, const Args&... args) +FMT_CONSTEXPR20 auto formatted_size(const S& fmt, const Args&... args) -> size_t { - return fmt::format_to(detail::counting_iterator(), format_str, args...) - .count(); + auto buf = detail::counting_buffer<>(); + fmt::format_to(appender(buf), fmt, args...); + return buf.count(); } template ::value)> -void print(std::FILE* f, const S& format_str, const Args&... args) { +void print(std::FILE* f, const S& fmt, const Args&... args) { memory_buffer buffer; - fmt::format_to(std::back_inserter(buffer), format_str, args...); + fmt::format_to(std::back_inserter(buffer), fmt, args...); detail::print(f, {buffer.data(), buffer.size()}); } template ::value)> -void print(const S& format_str, const Args&... args) { - print(stdout, format_str, args...); +void print(const S& fmt, const Args&... args) { + print(stdout, fmt, args...); } #if FMT_USE_NONTYPE_TEMPLATE_ARGS diff --git a/include/fmt/format-inl.h b/include/fmt/format-inl.h index fca26d4..ab06f1a 100644 --- a/include/fmt/format-inl.h +++ b/include/fmt/format-inl.h @@ -8,16 +8,16 @@ #ifndef FMT_FORMAT_INL_H_ #define FMT_FORMAT_INL_H_ -#ifndef FMT_IMPORT_STD +#ifndef FMT_MODULE # include +# include // errno +# include # include # include -#endif -#include // errno -#include -#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) && !defined(FMT_IMPORT_STD) -# include +# if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) +# include +# endif #endif #if defined(_WIN32) && !defined(FMT_USE_WRITE_CONSOLE) @@ -1443,12 +1443,26 @@ template struct span { size_t size; }; -#ifdef _WIN32 -inline void flockfile(FILE* f) { _lock_file(f); } -inline void funlockfile(FILE* f) { _unlock_file(f); } -inline int getc_unlocked(FILE* f) { return _fgetc_nolock(f); } +template auto flockfile(F* f) -> decltype(_lock_file(f)) { + _lock_file(f); +} +template auto funlockfile(F* f) -> decltype(_unlock_file(f)) { + _unlock_file(f); +} + +#ifndef getc_unlocked +template auto getc_unlocked(F* f) -> decltype(_fgetc_nolock(f)) { + return _fgetc_nolock(f); +} #endif +template +struct has_flockfile : std::false_type {}; + +template +struct has_flockfile()))>> + : std::true_type {}; + // A FILE wrapper. F is FILE defined as a template parameter to make system API // detection work. template class file_base { @@ -1600,19 +1614,34 @@ template class fallback_file : public file_base { } }; -template +#ifndef FMT_USE_FALLBACK_FILE +# define FMT_USE_FALLBACK_FILE 0 +#endif + +template auto get_file(F* f, int) -> apple_file { return f; } -template +template inline auto get_file(F* f, int) -> glibc_file { return f; } + inline auto get_file(FILE* f, ...) -> fallback_file { return f; } using file_ref = decltype(get_file(static_cast(nullptr), 0)); +template class file_print_buffer : public buffer { + public: + explicit file_print_buffer(F*) : buffer(nullptr, size_t()) {} +}; + +template +class file_print_buffer::value>> + : public buffer { private: file_ref file_; @@ -1627,7 +1656,7 @@ class file_print_buffer : public buffer { } public: - explicit file_print_buffer(FILE* f) : buffer(grow, size_t()), file_(f) { + explicit file_print_buffer(F* f) : buffer(grow, size_t()), file_(f) { flockfile(f); file_.init_buffer(); auto buf = file_.get_write_buffer(); @@ -1636,7 +1665,8 @@ class file_print_buffer : public buffer { ~file_print_buffer() { file_.advance_write_buffer(size()); bool flush = file_.needs_flush(); - funlockfile(file_); + F* f = file_; // Make funlockfile depend on the template parameter F + funlockfile(f); // for the system API detection to work. if (flush) fflush(file_); } }; @@ -1678,15 +1708,16 @@ FMT_FUNC void print(std::FILE* f, string_view text) { } } // namespace detail -FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) { +FMT_FUNC void vprint_buffered(std::FILE* f, string_view fmt, format_args args) { auto buffer = memory_buffer(); detail::vformat_to(buffer, fmt, args); detail::print(f, {buffer.data(), buffer.size()}); } -FMT_FUNC void vprint_locked(std::FILE* f, string_view fmt, format_args args) { - if (!detail::file_ref(f).is_buffered()) return vprint(f, fmt, args); - auto&& buffer = detail::file_print_buffer(f); +FMT_FUNC void vprint(std::FILE* f, string_view fmt, format_args args) { + if (!detail::file_ref(f).is_buffered() || !detail::has_flockfile<>()) + return vprint_buffered(f, fmt, args); + auto&& buffer = detail::file_print_buffer<>(f); return detail::vformat_to(buffer, fmt, args); } diff --git a/include/fmt/format.h b/include/fmt/format.h index 95df0ad..e15c2d5 100644 --- a/include/fmt/format.h +++ b/include/fmt/format.h @@ -38,7 +38,9 @@ # define FMT_REMOVE_TRANSITIVE_INCLUDES #endif -#ifndef FMT_IMPORT_STD +#include "base.h" + +#ifndef FMT_MODULE # include // std::signbit # include // uint32_t # include // std::memcpy @@ -51,24 +53,19 @@ # include // std::runtime_error # include // std::string # include // std::system_error -#endif - -#include "base.h" // Checking FMT_CPLUSPLUS for warning suppression in MSVC. -#if FMT_HAS_INCLUDE() && FMT_CPLUSPLUS > 201703L && \ - !defined(FMT_IMPORT_STD) -# include // std::bit_cast -#endif +# if FMT_HAS_INCLUDE() && FMT_CPLUSPLUS > 201703L +# include // std::bit_cast +# endif // libc++ supports string_view in pre-c++17. -#if FMT_HAS_INCLUDE() && \ - (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) -# ifndef FMT_IMPORT_STD +# if FMT_HAS_INCLUDE() && \ + (FMT_CPLUSPLUS >= 201703L || defined(_LIBCPP_VERSION)) # include +# define FMT_USE_STRING_VIEW # endif -# define FMT_USE_STRING_VIEW -#endif +#endif // FMT_MODULE #if defined __cpp_inline_variables && __cpp_inline_variables >= 201606L # define FMT_INLINE_VARIABLE inline @@ -109,6 +106,14 @@ # define FMT_NOINLINE #endif +namespace std { +template <> struct iterator_traits { + using iterator_category = output_iterator_tag; + using value_type = char; + using difference_type = ptrdiff_t; +}; +} // namespace std + #ifndef FMT_THROW # if FMT_EXCEPTIONS # if FMT_MSC_VERSION || defined(__NVCC__) @@ -518,7 +523,7 @@ template = 307 && !FMT_ICC_VERSION __attribute__((no_sanitize("undefined"))) #endif -inline auto +FMT_CONSTEXPR20 inline auto reserve(OutputIt it, size_t n) -> typename OutputIt::value_type* { auto& c = get_container(it); size_t size = c.size(); @@ -527,7 +532,8 @@ reserve(OutputIt it, size_t n) -> typename OutputIt::value_type* { } template -inline auto reserve(basic_appender it, size_t n) -> basic_appender { +FMT_CONSTEXPR20 inline auto reserve(basic_appender it, size_t n) + -> basic_appender { buffer& buf = get_container(it); buf.try_reserve(buf.size() + n); return it; @@ -546,9 +552,11 @@ template constexpr auto to_pointer(OutputIt, size_t) -> T* { return nullptr; } -template auto to_pointer(basic_appender it, size_t n) -> T* { +template +FMT_CONSTEXPR20 auto to_pointer(basic_appender it, size_t n) -> T* { buffer& buf = get_container(it); auto size = buf.size(); + buf.try_reserve(size + n); if (buf.capacity() < size + n) return nullptr; buf.try_resize(size + n); return buf.data() + size; @@ -813,25 +821,17 @@ FMT_BEGIN_EXPORT enum { inline_buffer_size = 500 }; /** - \rst - A dynamically growing memory buffer for trivially copyable/constructible types - with the first ``SIZE`` elements stored in the object itself. - - You can use the ``memory_buffer`` type alias for ``char`` instead. - - **Example**:: - - auto out = fmt::memory_buffer(); - fmt::format_to(std::back_inserter(out), "The answer is {}.", 42); - - This will append the following output to the ``out`` object: - - .. code-block:: none - - The answer is 42. - - The output can be converted to an ``std::string`` with ``to_string(out)``. - \endrst + * A dynamically growing memory buffer for trivially copyable/constructible + * types with the first `SIZE` elements stored in the object itself. Most + * commonly used via the `memory_buffer` alias for `char`. + * + * **Example**: + * + * auto out = fmt::memory_buffer(); + * fmt::format_to(std::back_inserter(out), "The answer is {}.", 42); + * + * This will append "The answer is 42." to `out`. The buffer content can be + * converted to `std::string` with `to_string(out)`. */ template > @@ -904,22 +904,14 @@ class basic_memory_buffer : public detail::buffer { } public: - /** - \rst - Constructs a :class:`fmt::basic_memory_buffer` object moving the content - of the other object to it. - \endrst - */ + /// Constructs a `basic_memory_buffer` object moving the content of the other + /// object to it. FMT_CONSTEXPR20 basic_memory_buffer(basic_memory_buffer&& other) noexcept : detail::buffer(grow) { move(other); } - /** - \rst - Moves the content of the other ``basic_memory_buffer`` object to this one. - \endrst - */ + /// Moves the content of the other `basic_memory_buffer` object to this one. auto operator=(basic_memory_buffer&& other) noexcept -> basic_memory_buffer& { FMT_ASSERT(this != &other, ""); deallocate(); @@ -930,18 +922,16 @@ class basic_memory_buffer : public detail::buffer { // Returns a copy of the allocator associated with this buffer. auto get_allocator() const -> Allocator { return alloc_; } - /** - Resizes the buffer to contain *count* elements. If T is a POD type new - elements may not be initialized. - */ + /// Resizes the buffer to contain `count` elements. If T is a POD type new + /// elements may not be initialized. FMT_CONSTEXPR20 void resize(size_t count) { this->try_resize(count); } - /** Increases the buffer capacity to *new_capacity*. */ + /// Increases the buffer capacity to `new_capacity`. void reserve(size_t new_capacity) { this->try_reserve(new_capacity); } using detail::buffer::append; template - void append(const ContiguousRange& range) { + FMT_CONSTEXPR20 void append(const ContiguousRange& range) { append(range.data(), range.data() + range.size()); } }; @@ -965,7 +955,7 @@ FMT_BEGIN_EXPORT # pragma clang diagnostic ignored "-Wweak-vtables" #endif -/** An error reported from a formatting function. */ +/// An error reported from a formatting function. class FMT_SO_VISIBILITY("default") format_error : public std::runtime_error { public: using std::runtime_error::runtime_error; @@ -1087,6 +1077,8 @@ template class format_facet : public Locale::facet { } }; +FMT_END_EXPORT + namespace detail { // Returns true if value is negative, false otherwise. @@ -1124,13 +1116,17 @@ using uint64_or_128_t = conditional_t() <= 64, uint64_t, uint128_t>; (factor) * 100000000, (factor) * 1000000000 // Converts value in the range [0, 100) to a string. -constexpr auto digits2(size_t value) -> const char* { - // GCC generates slightly better code when value is pointer-size. - return &"0001020304050607080910111213141516171819" - "2021222324252627282930313233343536373839" - "4041424344454647484950515253545556575859" - "6061626364656667686970717273747576777879" - "8081828384858687888990919293949596979899"[value * 2]; +// GCC generates slightly better code when value is pointer-size. +inline auto digits2(size_t value) -> const char* { + // Align data since unaligned access may be slower when crossing a + // hardware-specific boundary. + alignas(2) static const char data[] = + "0001020304050607080910111213141516171819" + "2021222324252627282930313233343536373839" + "4041424344454647484950515253545556575859" + "6061626364656667686970717273747576777879" + "8081828384858687888990919293949596979899"; + return &data[value * 2]; } // Sign is a template parameter to workaround a bug in gcc 4.8. @@ -1138,7 +1134,7 @@ template constexpr auto sign(Sign s) -> Char { #if !FMT_GCC_VERSION || FMT_GCC_VERSION >= 604 static_assert(std::is_same::value, ""); #endif - return static_cast("\0-+ "[s]); + return static_cast(((' ' << 24) | ('+' << 16) | ('-' << 8)) >> (s * 8)); } template FMT_CONSTEXPR auto count_digits_fallback(T n) -> int { @@ -1186,9 +1182,7 @@ inline auto do_count_digits(uint64_t n) -> int { // except for n == 0 in which case count_digits returns 1. FMT_CONSTEXPR20 inline auto count_digits(uint64_t n) -> int { #ifdef FMT_BUILTIN_CLZLL - if (!is_constant_evaluated()) { - return do_count_digits(n); - } + if (!is_constant_evaluated()) return do_count_digits(n); #endif return count_digits_fallback(n); } @@ -1285,56 +1279,60 @@ inline auto equal2(const char* lhs, const char* rhs) -> bool { return memcmp(lhs, rhs, 2) == 0; } -// Copies two characters from src to dst. +// Writes a two-digit value to out. template -FMT_CONSTEXPR20 FMT_INLINE void copy2(Char* dst, const char* src) { - if (!is_constant_evaluated() && sizeof(Char) == sizeof(char)) { - memcpy(dst, src, 2); +FMT_CONSTEXPR20 FMT_INLINE void write2digits(Char* out, size_t value) { + if (!is_constant_evaluated() && std::is_same::value) { + memcpy(out, digits2(value), 2); return; } - *dst++ = static_cast(*src++); - *dst = static_cast(*src); + *out++ = static_cast('0' + value / 10); + *out = static_cast('0' + value % 10); } -template struct format_decimal_result { - Iterator begin; - Iterator end; -}; - -// Formats a decimal unsigned integer value writing into out pointing to a -// buffer of specified size. The caller must ensure that the buffer is large -// enough. +// Formats a decimal unsigned integer value writing to out pointing to a buffer +// of specified size. The caller must ensure that the buffer is large enough. template -FMT_CONSTEXPR20 auto format_decimal(Char* out, UInt value, int size) - -> format_decimal_result { +FMT_CONSTEXPR20 auto do_format_decimal(Char* out, UInt value, int size) + -> Char* { FMT_ASSERT(size >= count_digits(value), "invalid digit count"); - out += size; - Char* end = out; + unsigned n = to_unsigned(size); while (value >= 100) { // Integer division is slow so do it for a group of two digits instead // of for every digit. The idea comes from the talk by Alexandrescu // "Three Optimization Tips for C++". See speed-test for a comparison. - out -= 2; - copy2(out, digits2(static_cast(value % 100))); + n -= 2; + write2digits(out + n, static_cast(value % 100)); value /= 100; } - if (value < 10) { - *--out = static_cast('0' + value); - return {out, end}; + if (value >= 10) { + n -= 2; + write2digits(out + n, static_cast(value)); + } else { + out[--n] = static_cast('0' + value); } - out -= 2; - copy2(out, digits2(static_cast(value))); - return {out, end}; + return out + n; } -template >::value)> -FMT_CONSTEXPR inline auto format_decimal(Iterator out, UInt value, int size) - -> format_decimal_result { +template +FMT_CONSTEXPR FMT_INLINE auto format_decimal(Char* out, UInt value, + int num_digits) -> Char* { + do_format_decimal(out, value, num_digits); + return out + num_digits; +} + +template ::value)> +FMT_CONSTEXPR auto format_decimal(OutputIt out, UInt value, int num_digits) + -> OutputIt { + if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { + do_format_decimal(ptr, value, num_digits); + return out; + } // Buffer is large enough to hold all digits (digits10 + 1). - Char buffer[digits10() + 1] = {}; - auto end = format_decimal(buffer, value, size).end; - return {out, detail::copy_noinline(buffer, end, out)}; + char buffer[digits10() + 1] = {}; + do_format_decimal(buffer, value, num_digits); + return detail::copy_noinline(buffer, buffer + num_digits, out); } template @@ -1351,9 +1349,10 @@ FMT_CONSTEXPR auto format_uint(Char* buffer, UInt value, int num_digits, return end; } -template -FMT_CONSTEXPR inline auto format_uint(It out, UInt value, int num_digits, - bool upper = false) -> It { +template ::value)> +FMT_CONSTEXPR inline auto format_uint(OutputIt out, UInt value, int num_digits, + bool upper = false) -> OutputIt { if (auto ptr = to_pointer(out, to_unsigned(num_digits))) { format_uint(ptr, value, num_digits, upper); return out; @@ -1421,10 +1420,12 @@ template class to_utf8 { if (policy == to_utf8_error_policy::abort) return false; buf.append(string_view("\xEF\xBF\xBD")); --p; + continue; } else { c = (c << 10) + static_cast(*p) - 0x35fdc00; } - } else if (c < 0x80) { + } + if (c < 0x80) { buf.push_back(static_cast(c)); } else if (c < 0x800) { buf.push_back(static_cast(0xc0 | (c >> 6))); @@ -1592,25 +1593,30 @@ template constexpr auto exponent_bias() -> int { } // Writes the exponent exp in the form "[+-]d{2,3}" to buffer. -template -FMT_CONSTEXPR auto write_exponent(int exp, It it) -> It { +template +FMT_CONSTEXPR auto write_exponent(int exp, OutputIt out) -> OutputIt { FMT_ASSERT(-10000 < exp && exp < 10000, "exponent out of range"); if (exp < 0) { - *it++ = static_cast('-'); + *out++ = static_cast('-'); exp = -exp; } else { - *it++ = static_cast('+'); - } - if (exp >= 100) { - const char* top = digits2(to_unsigned(exp / 100)); - if (exp >= 1000) *it++ = static_cast(top[0]); - *it++ = static_cast(top[1]); - exp %= 100; + *out++ = static_cast('+'); } - const char* d = digits2(to_unsigned(exp)); - *it++ = static_cast(d[0]); - *it++ = static_cast(d[1]); - return it; + unsigned uexp = to_unsigned(exp); + if (is_constant_evaluated()) { + if (uexp < 10) *out++ = '0'; + return format_decimal(out, uexp, count_digits(uexp)); + } + if (uexp >= 100u) { + const char* top = digits2(uexp / 100); + if (uexp >= 1000u) *out++ = static_cast(top[0]); + *out++ = static_cast(top[1]); + uexp %= 100; + } + const char* d = digits2(uexp); + *out++ = static_cast(d[0]); + *out++ = static_cast(d[1]); + return out; } // A floating-point number f * pow(2, e) where F is an unsigned type. @@ -1802,7 +1808,7 @@ auto find_escape(const Char* begin, const Char* end) inline auto find_escape(const char* begin, const char* end) -> find_escape_result { - if (!is_utf8()) return find_escape(begin, end); + if (!use_utf8()) return find_escape(begin, end); auto result = find_escape_result{end, nullptr, 0}; for_each_codepoint(string_view(begin, to_unsigned(end - begin)), [&](uint32_t cp, string_view sv) { @@ -1830,14 +1836,12 @@ inline auto find_escape(const char* begin, const char* end) }() /** - \rst - Constructs a compile-time format string from a string literal *s*. - - **Example**:: - - // A compile-time error because 'd' is an invalid specifier for strings. - std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); - \endrst + * Constructs a compile-time format string from a string literal `s`. + * + * **Example**: + * + * // A compile-time error because 'd' is an invalid specifier for strings. + * std::string s = fmt::format(FMT_STRING("{:d}"), "foo"); */ #define FMT_STRING(s) FMT_STRING_IMPL(s, fmt::detail::compile_string, ) @@ -2182,7 +2186,7 @@ FMT_CONSTEXPR FMT_INLINE auto write_int(OutputIt out, write_int_arg arg, int num_digits = count_digits(abs_value); return write_int( out, num_digits, prefix, specs, [=](reserve_iterator it) { - return format_decimal(it, abs_value, num_digits).end; + return format_decimal(it, abs_value, num_digits); }); } case presentation_type::hex: { @@ -2250,46 +2254,6 @@ FMT_CONSTEXPR FMT_INLINE auto write(OutputIt out, T value, loc); } -// An output iterator that counts the number of objects written to it and -// discards them. -class counting_iterator { - private: - size_t count_; - - public: - using iterator_category = std::output_iterator_tag; - using difference_type = std::ptrdiff_t; - using pointer = void; - using reference = void; - FMT_UNCHECKED_ITERATOR(counting_iterator); - - struct value_type { - template FMT_CONSTEXPR void operator=(const T&) {} - }; - - FMT_CONSTEXPR counting_iterator() : count_(0) {} - - FMT_CONSTEXPR auto count() const -> size_t { return count_; } - - FMT_CONSTEXPR auto operator++() -> counting_iterator& { - ++count_; - return *this; - } - FMT_CONSTEXPR auto operator++(int) -> counting_iterator { - auto it = *this; - ++*this; - return it; - } - - FMT_CONSTEXPR friend auto operator+(counting_iterator it, difference_type n) - -> counting_iterator { - it.count_ += static_cast(n); - return it; - } - - FMT_CONSTEXPR auto operator*() const -> value_type { return {}; } -}; - template FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, const format_specs& specs) -> OutputIt { @@ -2300,7 +2264,11 @@ FMT_CONSTEXPR auto write(OutputIt out, basic_string_view s, bool is_debug = specs.type == presentation_type::debug; size_t width = 0; - if (is_debug) size = write_escaped_string(counting_iterator{}, s).count(); + if (is_debug) { + auto buf = counting_buffer(); + write_escaped_string(basic_appender(buf), s); + size = buf.count(); + } if (specs.width != 0) { if (is_debug) @@ -2340,18 +2308,15 @@ FMT_CONSTEXPR auto write(OutputIt out, T value) -> OutputIt { if (negative) abs_value = ~abs_value + 1; int num_digits = count_digits(abs_value); auto size = (negative ? 1 : 0) + static_cast(num_digits); - auto it = reserve(out, size); - if (auto ptr = to_pointer(it, size)) { + if (auto ptr = to_pointer(out, size)) { if (negative) *ptr++ = static_cast('-'); format_decimal(ptr, abs_value, num_digits); return out; } - if (negative) *it++ = static_cast('-'); - it = format_decimal(it, abs_value, num_digits).end; - return base_iterator(out, it); + if (negative) *out++ = static_cast('-'); + return format_decimal(out, abs_value, num_digits); } -// DEPRECATED! template FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, format_specs& specs) -> const Char* { @@ -2394,49 +2359,6 @@ FMT_CONSTEXPR auto parse_align(const Char* begin, const Char* end, return begin; } -// A floating-point presentation format. -enum class float_format : unsigned char { - general, // General: exponent notation or fixed point based on magnitude. - exp, // Exponent notation with the default precision of 6, e.g. 1.2e-3. - fixed // Fixed point with the default precision of 6, e.g. 0.0012. -}; - -struct float_specs { - int precision; - float_format format : 8; - sign_t sign : 8; - bool locale : 1; - bool binary32 : 1; - bool showpoint : 1; -}; - -// DEPRECATED! -FMT_CONSTEXPR inline auto parse_float_type_spec(const format_specs& specs) - -> float_specs { - auto result = float_specs(); - result.showpoint = specs.alt; - result.locale = specs.localized; - switch (specs.type) { - default: - FMT_FALLTHROUGH; - case presentation_type::none: - result.format = float_format::general; - break; - case presentation_type::exp: - result.format = float_format::exp; - result.showpoint |= specs.precision != 0; - break; - case presentation_type::fixed: - result.format = float_format::fixed; - result.showpoint |= specs.precision != 0; - break; - case presentation_type::general: - result.format = float_format::general; - break; - } - return result; -} - template FMT_CONSTEXPR20 auto write_nonfinite(OutputIt out, bool isnan, format_specs specs, sign_t sign) @@ -2479,7 +2401,7 @@ constexpr auto write_significand(OutputIt out, const char* significand, template inline auto write_significand(OutputIt out, UInt significand, int significand_size) -> OutputIt { - return format_decimal(out, significand, significand_size).end; + return format_decimal(out, significand, significand_size); } template FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, @@ -2499,14 +2421,13 @@ template ::value)> inline auto write_significand(Char* out, UInt significand, int significand_size, int integral_size, Char decimal_point) -> Char* { - if (!decimal_point) - return format_decimal(out, significand, significand_size).end; + if (!decimal_point) return format_decimal(out, significand, significand_size); out += significand_size + 1; Char* end = out; int floating_size = significand_size - integral_size; for (int i = floating_size / 2; i > 0; --i) { out -= 2; - copy2(out, digits2(static_cast(significand % 100))); + write2digits(out, static_cast(significand % 100)); significand /= 100; } if (floating_size % 2 != 0) { @@ -2563,33 +2484,31 @@ FMT_CONSTEXPR20 auto write_significand(OutputIt out, T significand, template > FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, - const format_specs& specs, - float_specs fspecs, locale_ref loc) - -> OutputIt { + const format_specs& specs, sign_t sign, + locale_ref loc) -> OutputIt { auto significand = f.significand; int significand_size = get_significand_size(f); const Char zero = static_cast('0'); - auto sign = fspecs.sign; size_t size = to_unsigned(significand_size) + (sign ? 1 : 0); using iterator = reserve_iterator; - Char decimal_point = - fspecs.locale ? detail::decimal_point(loc) : static_cast('.'); + Char decimal_point = specs.localized ? detail::decimal_point(loc) + : static_cast('.'); int output_exp = f.exponent + significand_size - 1; auto use_exp_format = [=]() { - if (fspecs.format == float_format::exp) return true; - if (fspecs.format != float_format::general) return false; + if (specs.type == presentation_type::exp) return true; + if (specs.type == presentation_type::fixed) return false; // Use the fixed notation if the exponent is in [exp_lower, exp_upper), // e.g. 0.0001 instead of 1e-04. Otherwise use the exponent notation. const int exp_lower = -4, exp_upper = 16; return output_exp < exp_lower || - output_exp >= (fspecs.precision > 0 ? fspecs.precision : exp_upper); + output_exp >= (specs.precision > 0 ? specs.precision : exp_upper); }; if (use_exp_format()) { int num_zeros = 0; - if (fspecs.showpoint) { - num_zeros = fspecs.precision - significand_size; + if (specs.alt) { + num_zeros = specs.precision - significand_size; if (num_zeros < 0) num_zeros = 0; size += to_unsigned(num_zeros); } else if (significand_size == 1) { @@ -2619,28 +2538,29 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, if (f.exponent >= 0) { // 1234e5 -> 123400000[.0+] size += to_unsigned(f.exponent); - int num_zeros = fspecs.precision - exp; + int num_zeros = specs.precision - exp; abort_fuzzing_if(num_zeros > 5000); - if (fspecs.showpoint) { + if (specs.alt) { ++size; - if (num_zeros <= 0 && fspecs.format != float_format::fixed) num_zeros = 0; + if (num_zeros <= 0 && specs.type != presentation_type::fixed) + num_zeros = 0; if (num_zeros > 0) size += to_unsigned(num_zeros); } - auto grouping = Grouping(loc, fspecs.locale); + auto grouping = Grouping(loc, specs.localized); size += to_unsigned(grouping.count_separators(exp)); return write_padded(out, specs, size, [&](iterator it) { if (sign) *it++ = detail::sign(sign); it = write_significand(it, significand, significand_size, f.exponent, grouping); - if (!fspecs.showpoint) return it; + if (!specs.alt) return it; *it++ = decimal_point; return num_zeros > 0 ? detail::fill_n(it, num_zeros, zero) : it; }); } else if (exp > 0) { // 1234e-2 -> 12.34[0+] - int num_zeros = fspecs.showpoint ? fspecs.precision - significand_size : 0; + int num_zeros = specs.alt ? specs.precision - significand_size : 0; size += 1 + to_unsigned(num_zeros > 0 ? num_zeros : 0); - auto grouping = Grouping(loc, fspecs.locale); + auto grouping = Grouping(loc, specs.localized); size += to_unsigned(grouping.count_separators(exp)); return write_padded(out, specs, size, [&](iterator it) { if (sign) *it++ = detail::sign(sign); @@ -2651,11 +2571,11 @@ FMT_CONSTEXPR20 auto do_write_float(OutputIt out, const DecimalFP& f, } // 1234e-6 -> 0.001234 int num_zeros = -exp; - if (significand_size == 0 && fspecs.precision >= 0 && - fspecs.precision < num_zeros) { - num_zeros = fspecs.precision; + if (significand_size == 0 && specs.precision >= 0 && + specs.precision < num_zeros) { + num_zeros = specs.precision; } - bool pointy = num_zeros != 0 || significand_size != 0 || fspecs.showpoint; + bool pointy = num_zeros != 0 || significand_size != 0 || specs.alt; size += 1 + (pointy ? 1 : 0) + to_unsigned(num_zeros); return write_padded(out, specs, size, [&](iterator it) { if (sign) *it++ = detail::sign(sign); @@ -2683,14 +2603,14 @@ template class fallback_digit_grouping { template FMT_CONSTEXPR20 auto write_float(OutputIt out, const DecimalFP& f, - const format_specs& specs, float_specs fspecs, + const format_specs& specs, sign_t sign, locale_ref loc) -> OutputIt { if (is_constant_evaluated()) { return do_write_float>(out, f, specs, fspecs, + fallback_digit_grouping>(out, f, specs, sign, loc); } else { - return do_write_float(out, f, specs, fspecs, loc); + return do_write_float(out, f, specs, sign, loc); } } @@ -3241,15 +3161,15 @@ constexpr auto fractional_part_rounding_thresholds(int index) -> uint32_t { } template -FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, +FMT_CONSTEXPR20 auto format_float(Float value, int precision, + const format_specs& specs, bool binary32, buffer& buf) -> int { // float is passed as double to reduce the number of instantiations. static_assert(!std::is_same::value, ""); - FMT_ASSERT(value >= 0, "value is negative"); auto converted_value = convert_float(value); - const bool fixed = specs.format == float_format::fixed; - if (value <= 0) { // <= instead of == to silence a warning. + const bool fixed = specs.type == presentation_type::fixed; + if (value == 0) { if (precision <= 0 || !fixed) { buf.push_back('0'); return 0; @@ -3274,16 +3194,6 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, exp = static_cast(e); if (e > exp) ++exp; // Compute ceil. dragon_flags = dragon::fixup; - } else if (precision < 0) { - // Use Dragonbox for the shortest format. - if (specs.binary32) { - auto dec = dragonbox::to_decimal(static_cast(value)); - write(appender(buf), dec.significand); - return dec.exponent; - } - auto dec = dragonbox::to_decimal(static_cast(value)); - write(appender(buf), dec.significand); - return dec.exponent; } else { // Extract significand bits and exponent bits. using info = dragonbox::float_info; @@ -3410,7 +3320,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, // for details. prod = ((subsegment * static_cast(450359963)) >> 20) + 1; digits = static_cast(prod >> 32); - copy2(buffer, digits2(digits)); + write2digits(buffer, digits); number_of_digits_printed += 2; } @@ -3418,7 +3328,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, while (number_of_digits_printed < number_of_digits_to_print) { prod = static_cast(prod) * static_cast(100); digits = static_cast(prod >> 32); - copy2(buffer + number_of_digits_printed, digits2(digits)); + write2digits(buffer + number_of_digits_printed, digits); number_of_digits_printed += 2; } }; @@ -3527,9 +3437,8 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, } if (use_dragon) { auto f = basic_fp(); - bool is_predecessor_closer = specs.binary32 - ? f.assign(static_cast(value)) - : f.assign(converted_value); + bool is_predecessor_closer = binary32 ? f.assign(static_cast(value)) + : f.assign(converted_value); if (is_predecessor_closer) dragon_flags |= dragon::predecessor_closer; if (fixed) dragon_flags |= dragon::fixed; // Limit precision to the maximum possible number of significant digits in @@ -3538,7 +3447,7 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, if (precision > max_double_digits) precision = max_double_digits; format_dragon(f, dragon_flags, precision, buf, exp); } - if (!fixed && !specs.showpoint) { + if (!fixed && !specs.alt) { // Remove trailing zeros. auto num_digits = buf.size(); while (num_digits > 0 && buf[num_digits - 1] == '0') { @@ -3553,25 +3462,30 @@ FMT_CONSTEXPR20 auto format_float(Float value, int precision, float_specs specs, template FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, locale_ref loc) -> OutputIt { - sign_t sign = specs.sign; - if (detail::signbit(value)) { // value < 0 is false for NaN so use signbit. - sign = sign::minus; - value = -value; - } else if (sign == sign::minus) { - sign = sign::none; - } + // Use signbit because value < 0 is false for NaN. + sign_t sign = detail::signbit(value) ? sign::minus : specs.sign; if (!detail::isfinite(value)) return write_nonfinite(out, detail::isnan(value), specs, sign); if (specs.align == align::numeric && sign) { - auto it = reserve(out, 1); - *it++ = detail::sign(sign); - out = base_iterator(out, it); + *out++ = detail::sign(sign); sign = sign::none; if (specs.width != 0) --specs.width; } + int precision = specs.precision; + if (precision < 0) { + if (specs.type != presentation_type::none) { + precision = 6; + } else if (is_fast_float::value && !is_constant_evaluated()) { + // Use Dragonbox for the shortest format. + using floaty = conditional_t= sizeof(double), double, float>; + auto dec = dragonbox::to_decimal(static_cast(value)); + return write_float(out, dec, specs, sign, loc); + } + } + memory_buffer buffer; if (specs.type == presentation_type::hexfloat) { if (sign) buffer.push_back(detail::sign(sign)); @@ -3580,24 +3494,23 @@ FMT_CONSTEXPR20 auto write_float(OutputIt out, T value, format_specs specs, specs); } - int precision = specs.precision >= 0 || specs.type == presentation_type::none - ? specs.precision - : 6; if (specs.type == presentation_type::exp) { if (precision == max_value()) report_error("number is too big"); else ++precision; - } else if (specs.type != presentation_type::fixed && precision == 0) { + specs.alt |= specs.precision != 0; + } else if (specs.type == presentation_type::fixed) { + specs.alt |= specs.precision != 0; + } else if (precision == 0) { precision = 1; } - float_specs fspecs = parse_float_type_spec(specs); - fspecs.sign = sign; - if (const_check(std::is_same())) fspecs.binary32 = true; - int exp = format_float(convert_float(value), precision, fspecs, buffer); - fspecs.precision = precision; + int exp = format_float(convert_float(value), precision, specs, + std::is_same(), buffer); + + specs.precision = precision; auto f = big_decimal_fp{buffer.data(), static_cast(buffer.size()), exp}; - return write_float(out, f, specs, fspecs, loc); + return write_float(out, f, specs, sign, loc); } template OutputIt { if (is_constant_evaluated()) return write(out, value, format_specs()); if (const_check(!is_supported_floating_point(value))) return out; - auto sign = sign_t::none; - if (detail::signbit(value)) { - sign = sign::minus; - value = -value; - } + auto sign = detail::signbit(value) ? sign::minus : sign_t::none; constexpr auto specs = format_specs(); - using floaty = conditional_t::value, double, T>; + using floaty = conditional_t= sizeof(double), double, float>; using floaty_uint = typename dragonbox::float_info::carrier_uint; floaty_uint mask = exponent_mask(); if ((bit_cast(value) & mask) == mask) return write_nonfinite(out, std::isnan(value), specs, sign); - auto fspecs = float_specs(); - fspecs.sign = sign; auto dec = dragonbox::to_decimal(static_cast(value)); - return write_float(out, dec, specs, fspecs, {}); + return write_float(out, dec, specs, sign, {}); } template FMT_CONSTEXPR auto write(OutputIt out, basic_string_view value) -> OutputIt { - auto it = reserve(out, value.size()); - it = copy_noinline(value.begin(), value.end(), it); - return base_iterator(out, it); + return copy_noinline(value.begin(), value.end(), out); } template struct arg_formatter { } }; -struct width_checker { +struct dynamic_spec_getter { template ::value)> FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { - if (is_negative(value)) report_error("negative width"); - return static_cast(value); + return is_negative(value) ? ~0ull : static_cast(value); } template ::value)> FMT_CONSTEXPR auto operator()(T) -> unsigned long long { - report_error("width is not integer"); + report_error("width/precision is not integer"); return 0; } }; -struct precision_checker { - template ::value)> - FMT_CONSTEXPR auto operator()(T value) -> unsigned long long { - if (is_negative(value)) report_error("negative precision"); - return static_cast(value); - } - - template ::value)> - FMT_CONSTEXPR auto operator()(T) -> unsigned long long { - report_error("precision is not integer"); - return 0; - } -}; - -template -FMT_CONSTEXPR auto get_dynamic_spec(FormatArg arg) -> int { - unsigned long long value = arg.visit(Handler()); - if (value > to_unsigned(max_value())) report_error("number is too big"); - return static_cast(value); -} - template -FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> decltype(ctx.arg(id)) { +FMT_CONSTEXPR auto get_arg(Context& ctx, ID id) -> basic_format_arg { auto arg = ctx.arg(id); if (!arg) report_error("argument not found"); return arg; } -template -FMT_CONSTEXPR void handle_dynamic_spec(int& value, - arg_ref ref, - Context& ctx) { - switch (ref.kind) { - case arg_id_kind::none: - break; - case arg_id_kind::index: - value = detail::get_dynamic_spec(get_arg(ctx, ref.val.index)); - break; - case arg_id_kind::name: - value = detail::get_dynamic_spec(get_arg(ctx, ref.val.name)); - break; - } +template +FMT_CONSTEXPR int get_dynamic_spec( + const arg_ref& ref, Context& ctx) { + FMT_ASSERT(ref.kind != arg_id_kind::none, ""); + auto arg = ref.kind == arg_id_kind::index ? ctx.arg(ref.val.index) + : ctx.arg(ref.val.name); + if (!arg) report_error("argument not found"); + unsigned long long value = arg.visit(dynamic_spec_getter()); + if (value > to_unsigned(max_value())) + report_error("width/precision is out of range"); + return static_cast(value); +} + +template +FMT_CONSTEXPR void handle_dynamic_spec( + int& value, const arg_ref& ref, Context& ctx) { + if (ref.kind != arg_id_kind::none) value = get_dynamic_spec(ref, ctx); } #if FMT_USE_USER_DEFINED_LITERALS @@ -3887,25 +3773,24 @@ FMT_API void report_error(format_func func, int error_code, const char* message) noexcept; } // namespace detail +FMT_BEGIN_EXPORT FMT_API auto vsystem_error(int error_code, string_view format_str, format_args args) -> std::system_error; /** - \rst - Constructs :class:`std::system_error` with a message formatted with - ``fmt::format(fmt, args...)``. - *error_code* is a system error code as given by ``errno``. - - **Example**:: - - // This throws std::system_error with the description - // cannot open file 'madeup': No such file or directory - // or similar (system message may vary). - const char* filename = "madeup"; - std::FILE* file = std::fopen(filename, "r"); - if (!file) - throw fmt::system_error(errno, "cannot open file '{}'", filename); - \endrst + * Constructs `std::system_error` with a message formatted with + * `fmt::format(fmt, args...)`. + * `error_code` is a system error code as given by `errno`. + * + * **Example**: + * + * // This throws std::system_error with the description + * // cannot open file 'madeup': No such file or directory + * // or similar (system message may vary). + * const char* filename = "madeup"; + * std::FILE* file = std::fopen(filename, "r"); + * if (!file) + * throw fmt::system_error(errno, "cannot open file '{}'", filename); */ template auto system_error(int error_code, format_string fmt, T&&... args) @@ -3914,20 +3799,17 @@ auto system_error(int error_code, format_string fmt, T&&... args) } /** - \rst - Formats an error message for an error returned by an operating system or a - language runtime, for example a file opening error, and writes it to *out*. - The format is the same as the one used by ``std::system_error(ec, message)`` - where ``ec`` is ``std::error_code(error_code, std::generic_category()})``. - It is implementation-defined but normally looks like: - - .. parsed-literal:: - **: ** - - where ** is the passed message and ** is the system - message corresponding to the error code. - *error_code* is a system error code as given by ``errno``. - \endrst + * Formats an error message for an error returned by an operating system or a + * language runtime, for example a file opening error, and writes it to `out`. + * The format is the same as the one used by `std::system_error(ec, message)` + * where `ec` is `std::error_code(error_code, std::generic_category())`. + * It is implementation-defined but normally looks like: + * + * : + * + * where `` is the passed message and `` is the system + * message corresponding to the error code. + * `error_code` is a system error code as given by `errno`. */ FMT_API void format_system_error(detail::buffer& out, int error_code, const char* message) noexcept; @@ -3936,7 +3818,7 @@ FMT_API void format_system_error(detail::buffer& out, int error_code, // Can be used to report errors from destructors. FMT_API void report_system_error(int error_code, const char* message) noexcept; -/** Fast integer formatter. */ +/// A fast integer formatter. class format_int { private: // Buffer should be large enough to hold all digits (digits10 + 1), @@ -3945,12 +3827,14 @@ class format_int { mutable char buffer_[buffer_size]; char* str_; - template auto format_unsigned(UInt value) -> char* { + template + FMT_CONSTEXPR20 auto format_unsigned(UInt value) -> char* { auto n = static_cast>(value); - return detail::format_decimal(buffer_, n, buffer_size - 1).begin; + return detail::do_format_decimal(buffer_, n, buffer_size - 1); } - template auto format_signed(Int value) -> char* { + template + FMT_CONSTEXPR20 auto format_signed(Int value) -> char* { auto abs_value = static_cast>(value); bool negative = value < 0; if (negative) abs_value = 0 - abs_value; @@ -3960,39 +3844,35 @@ class format_int { } public: - explicit format_int(int value) : str_(format_signed(value)) {} - explicit format_int(long value) : str_(format_signed(value)) {} - explicit format_int(long long value) : str_(format_signed(value)) {} - explicit format_int(unsigned value) : str_(format_unsigned(value)) {} - explicit format_int(unsigned long value) : str_(format_unsigned(value)) {} - explicit format_int(unsigned long long value) + explicit FMT_CONSTEXPR20 format_int(int value) : str_(format_signed(value)) {} + explicit FMT_CONSTEXPR20 format_int(long value) + : str_(format_signed(value)) {} + explicit FMT_CONSTEXPR20 format_int(long long value) + : str_(format_signed(value)) {} + explicit FMT_CONSTEXPR20 format_int(unsigned value) + : str_(format_unsigned(value)) {} + explicit FMT_CONSTEXPR20 format_int(unsigned long value) + : str_(format_unsigned(value)) {} + explicit FMT_CONSTEXPR20 format_int(unsigned long long value) : str_(format_unsigned(value)) {} - /** Returns the number of characters written to the output buffer. */ - auto size() const -> size_t { + /// Returns the number of characters written to the output buffer. + FMT_CONSTEXPR20 auto size() const -> size_t { return detail::to_unsigned(buffer_ - str_ + buffer_size - 1); } - /** - Returns a pointer to the output buffer content. No terminating null - character is appended. - */ - auto data() const -> const char* { return str_; } + /// Returns a pointer to the output buffer content. No terminating null + /// character is appended. + FMT_CONSTEXPR20 auto data() const -> const char* { return str_; } - /** - Returns a pointer to the output buffer content with terminating null - character appended. - */ - auto c_str() const -> const char* { + /// Returns a pointer to the output buffer content with terminating null + /// character appended. + FMT_CONSTEXPR20 auto c_str() const -> const char* { buffer_[buffer_size - 1] = '\0'; return str_; } - /** - \rst - Returns the content of the output buffer as an ``std::string``. - \endrst - */ + /// Returns the content of the output buffer as an `std::string`. auto str() const -> std::string { return std::string(str_, size()); } }; @@ -4001,14 +3881,19 @@ struct formatter::value>> : formatter, Char> { template auto format(const T& value, FormatContext& ctx) const -> decltype(ctx.out()) { - using base = formatter, Char>; - return base::format(format_as(value), ctx); + auto&& val = format_as(value); // Make an lvalue reference for format. + return formatter, Char>::format(val, ctx); } }; -#define FMT_FORMAT_AS(Type, Base) \ - template \ - struct formatter : formatter {} +#define FMT_FORMAT_AS(Type, Base) \ + template \ + struct formatter : formatter { \ + template \ + auto format(Type value, FormatContext& ctx) const -> decltype(ctx.out()) { \ + return formatter::format(value, ctx); \ + } \ + } FMT_FORMAT_AS(signed char, int); FMT_FORMAT_AS(unsigned char, unsigned); @@ -4029,13 +3914,11 @@ template struct formatter : formatter, Char> {}; /** - \rst - Converts ``p`` to ``const void*`` for pointer formatting. - - **Example**:: - - auto s = fmt::format("{}", fmt::ptr(p)); - \endrst + * Converts `p` to `const void*` for pointer formatting. + * + * **Example**: + * + * auto s = fmt::format("{}", fmt::ptr(p)); */ template auto ptr(T p) -> const void* { static_assert(std::is_pointer::value, ""); @@ -4043,14 +3926,12 @@ template auto ptr(T p) -> const void* { } /** - \rst - Converts ``e`` to the underlying type. - - **Example**:: - - enum class color { red, green, blue }; - auto s = fmt::format("{}", fmt::underlying(color::red)); - \endrst + * Converts `e` to the underlying type. + * + * **Example**: + * + * enum class color { red, green, blue }; + * auto s = fmt::format("{}", fmt::underlying(color::red)); */ template constexpr auto underlying(Enum e) noexcept -> underlying_t { @@ -4087,10 +3968,8 @@ template <> struct formatter { template auto format(bytes b, FormatContext& ctx) const -> decltype(ctx.out()) { auto specs = specs_; - detail::handle_dynamic_spec(specs.width, - specs.width_ref, ctx); - detail::handle_dynamic_spec( - specs.precision, specs.precision_ref, ctx); + detail::handle_dynamic_spec(specs.width, specs.width_ref, ctx); + detail::handle_dynamic_spec(specs.precision, specs.precision_ref, ctx); return detail::write_bytes(ctx.out(), b.data_, specs); } }; @@ -4101,15 +3980,13 @@ template struct group_digits_view { }; /** - \rst - Returns a view that formats an integer value using ',' as a locale-independent - thousands separator. - - **Example**:: - - fmt::print("{}", fmt::group_digits(12345)); - // Output: "12,345" - \endrst + * Returns a view that formats an integer value using ',' as a + * locale-independent thousands separator. + * + * **Example**: + * + * fmt::print("{}", fmt::group_digits(12345)); + * // Output: "12,345" */ template auto group_digits(T value) -> group_digits_view { return {value}; @@ -4130,10 +4007,8 @@ template struct formatter> : formatter { auto format(group_digits_view t, FormatContext& ctx) const -> decltype(ctx.out()) { auto specs = specs_; - detail::handle_dynamic_spec(specs.width, - specs.width_ref, ctx); - detail::handle_dynamic_spec( - specs.precision, specs.precision_ref, ctx); + detail::handle_dynamic_spec(specs.width, specs.width_ref, ctx); + detail::handle_dynamic_spec(specs.precision, specs.precision_ref, ctx); auto arg = detail::make_write_int_arg(t.value, specs.sign); return detail::write_int( ctx.out(), static_cast>(arg.abs_value), @@ -4171,12 +4046,16 @@ template struct nested_formatter { FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) -> decltype(ctx.begin()) { - auto specs = detail::dynamic_format_specs(); - auto it = parse_format_specs(ctx.begin(), ctx.end(), specs, ctx, - detail::type::none_type); - width_ = specs.width; + auto it = ctx.begin(), end = ctx.end(); + if (it == end) return it; + auto specs = format_specs(); + it = detail::parse_align(it, end, specs); fill_ = specs.fill; align_ = specs.align; + Char c = *it; + auto width_ref = detail::arg_ref(); + if ((c >= '0' && c <= '9') || c == '{') + it = detail::parse_dynamic_spec(it, end, width_, width_ref, ctx); ctx.advance_to(it); return formatter_.parse(ctx); } @@ -4200,15 +4079,11 @@ template struct nested_formatter { }; /** - \rst - Converts *value* to ``std::string`` using the default format for type *T*. - - **Example**:: - - #include - - std::string answer = fmt::to_string(42); - \endrst + * Converts `value` to `std::string` using the default format for type `T`. + * + * **Example**: + * + * std::string answer = fmt::to_string(42); */ template ::value && !detail::has_format_as::value)> @@ -4267,8 +4142,7 @@ void vformat_to(buffer& buf, basic_string_view fmt, : parse_context(str), context(p_out, p_args, p_loc) {} void on_text(const Char* begin, const Char* end) { - auto text = basic_string_view(begin, to_unsigned(end - begin)); - context.advance_to(write(context.out(), text)); + context.advance_to(copy_noinline(begin, end, context.out())); } FMT_CONSTEXPR auto on_arg_id() -> int { @@ -4299,10 +4173,12 @@ void vformat_to(buffer& buf, basic_string_view fmt, return parse_context.begin(); auto specs = detail::dynamic_format_specs(); begin = parse_format_specs(begin, end, specs, parse_context, arg.type()); - detail::handle_dynamic_spec( - specs.width, specs.width_ref, context); - detail::handle_dynamic_spec( - specs.precision, specs.precision_ref, context); + if (specs.width_ref.kind != detail::arg_id_kind::none) + specs.width = detail::get_dynamic_spec(specs.width_ref, context); + if (specs.precision_ref.kind != detail::arg_id_kind::none) { + specs.precision = + detail::get_dynamic_spec(specs.precision_ref, context); + } if (begin == end || *begin != '}') report_error("missing '}' in format string"); context.advance_to(arg.visit( @@ -4329,6 +4205,8 @@ extern template FMT_API auto decimal_point_impl(locale_ref) -> char; extern template FMT_API auto decimal_point_impl(locale_ref) -> wchar_t; #endif // FMT_HEADER_ONLY +FMT_END_EXPORT + template template FMT_CONSTEXPR FMT_INLINE auto native_formatter::format( @@ -4338,13 +4216,11 @@ FMT_CONSTEXPR FMT_INLINE auto native_formatter::format( return write(ctx.out(), val, specs_, ctx.locale()); } auto specs = specs_; - handle_dynamic_spec(specs.width, specs.width_ref, ctx); - handle_dynamic_spec(specs.precision, specs.precision_ref, - ctx); + handle_dynamic_spec(specs.width, specs.width_ref, ctx); + handle_dynamic_spec(specs.precision, specs.precision_ref, ctx); return write(ctx.out(), val, specs, ctx.locale()); } -FMT_END_EXPORT } // namespace detail FMT_BEGIN_EXPORT @@ -4357,14 +4233,12 @@ struct formatter #if FMT_USE_USER_DEFINED_LITERALS inline namespace literals { /** - \rst - User-defined literal equivalent of :func:`fmt::arg`. - - **Example**:: - - using namespace fmt::literals; - fmt::print("Elapsed time: {s:.2f} seconds", "s"_a=1.23); - \endrst + * User-defined literal equivalent of `fmt::arg`. + * + * **Example**: + * + * using namespace fmt::literals; + * fmt::print("The answer is {answer}.", "answer"_a=42); */ # if FMT_USE_NONTYPE_TEMPLATE_ARGS template constexpr auto operator""_a() { @@ -4382,16 +4256,14 @@ constexpr auto operator""_a(const char* s, size_t) -> detail::udl_arg { FMT_API auto vformat(string_view fmt, format_args args) -> std::string; /** - \rst - Formats ``args`` according to specifications in ``fmt`` and returns the result - as a string. - - **Example**:: - - #include - std::string message = fmt::format("The answer is {}.", 42); - \endrst -*/ + * Formats `args` according to specifications in `fmt` and returns the result + * as a string. + * + * **Example**: + * + * #include + * std::string message = fmt::format("The answer is {}.", 42); + */ template FMT_NODISCARD FMT_INLINE auto format(format_string fmt, T&&... args) -> std::string { diff --git a/include/fmt/os.h b/include/fmt/os.h index c338809..974c5c2 100644 --- a/include/fmt/os.h +++ b/include/fmt/os.h @@ -8,20 +8,18 @@ #ifndef FMT_OS_H_ #define FMT_OS_H_ -#include -#ifndef FMT_IMPORT_STD +#include "format.h" + +#ifndef FMT_MODULE +# include # include # include # include // std::system_error -#endif - -#include "format.h" -#if defined __APPLE__ || defined(__FreeBSD__) # if FMT_HAS_INCLUDE() -# include // for LC_NUMERIC_MASK on OS X +# include // LC_NUMERIC_MASK on macOS # endif -#endif +#endif // FMT_MODULE #ifndef FMT_USE_FCNTL // UWP doesn't provide _pipe. @@ -79,46 +77,33 @@ 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 | - +---------------+-----------------------------+ - | wcstring_view | basic_cstring_view | - +---------------+-----------------------------+ - - This class is most useful as a parameter type to allow passing - different types of strings to a function, for example:: - - template - std::string format(cstring_view format_str, const Args & ... args); - - format("{}", 42); - format(std::string("{}"), 42); - \endrst + * 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 | + * +---------------+-----------------------------+ + * | wcstring_view | basic_cstring_view | + * +---------------+-----------------------------+ + * + * This class is most useful as a parameter type for functions that wrap C APIs. */ template class basic_cstring_view { private: const Char* data_; public: - /** Constructs a string reference object from a C string. */ + /// 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 - */ + /// Constructs a string reference from an `std::string` object. basic_cstring_view(const std::basic_string& s) : data_(s.c_str()) {} - /** Returns the pointer to a C string. */ + /// Returns the pointer to a C string. auto c_str() const -> const Char* { return data_; } }; @@ -137,33 +122,30 @@ 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:: - **: ** - - where ** is the formatted message and ** 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 -*/ + * Constructs a `std::system_error` object with the description of the form + * + * : + * + * where `` is the formatted message and `` 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); + * } + */ template std::system_error windows_error(int error_code, string_view message, const Args&... args) { @@ -229,17 +211,11 @@ class buffered_file { FMT_API auto descriptor() const -> int; - void vprint(string_view fmt, format_args args) { - fmt::vprint(file_, fmt, args); - } - void vprint_locked(string_view fmt, format_args args) { - fmt::vprint_locked(file_, fmt, args); - } - template inline void print(string_view fmt, const T&... args) { const auto& vargs = fmt::make_format_args(args...); - detail::is_locking() ? vprint(fmt, vargs) : vprint_locked(fmt, vargs); + detail::is_locking() ? fmt::vprint_buffered(file_, fmt, vargs) + : fmt::vprint(file_, fmt, vargs); } }; @@ -407,11 +383,10 @@ class file_buffer final : public buffer { } // namespace detail -// 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{}; +FMT_INLINE_VARIABLE constexpr auto buffer_size = detail::buffer_size(); -/** A fast output stream which is not thread-safe. */ +/// A fast output stream for writing from a single thread. Writing from +/// multiple threads without external synchronization may result in a data race. class FMT_API ostream { private: FMT_MSC_WARNING(suppress : 4251) @@ -432,29 +407,25 @@ class FMT_API ostream { void close() { buffer_.close(); } - /** - Formats ``args`` according to specifications in ``fmt`` and writes the - output to the file. - */ + /// Formats `args` according to specifications in `fmt` and writes the + /// output to the file. template void print(format_string fmt, T&&... args) { vformat_to(appender(buffer_), fmt, fmt::make_format_args(args...)); } }; /** - \rst - Opens a file for writing. Supported parameters passed in *params*: - - * ````: Flags passed to `open - `_ - (``file::WRONLY | file::CREATE | file::TRUNC`` by default) - * ``buffer_size=``: Output buffer size - - **Example**:: - - auto out = fmt::output_file("guide.txt"); - out.print("Don't {}", "Panic"); - \endrst + * Opens a file for writing. Supported parameters passed in `params`: + * + * - ``: Flags passed to [open]( + * https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html) + * (`file::WRONLY | file::CREATE | file::TRUNC` by default) + * - `buffer_size=`: Output buffer size + * + * **Example**: + * + * auto out = fmt::output_file("guide.txt"); + * out.print("Don't {}", "Panic"); */ template inline auto output_file(cstring_view path, T... params) -> ostream { diff --git a/include/fmt/ostream.h b/include/fmt/ostream.h index 7ab2458..98faef6 100644 --- a/include/fmt/ostream.h +++ b/include/fmt/ostream.h @@ -8,7 +8,7 @@ #ifndef FMT_OSTREAM_H_ #define FMT_OSTREAM_H_ -#ifndef FMT_IMPORT_STD +#ifndef FMT_MODULE # include // std::filebuf #endif @@ -44,12 +44,12 @@ auto get_file(std::filebuf&) -> FILE*; inline auto write_ostream_unicode(std::ostream& os, fmt::string_view data) -> bool { FILE* f = nullptr; -#if FMT_MSC_VERSION +#if FMT_MSC_VERSION && FMT_USE_RTTI if (auto* buf = dynamic_cast(os.rdbuf())) f = get_file(*buf); else return false; -#elif defined(_WIN32) && defined(__GLIBCXX__) +#elif defined(_WIN32) && defined(__GLIBCXX__) && FMT_USE_RTTI auto* rdbuf = os.rdbuf(); if (auto* sfbuf = dynamic_cast<__gnu_cxx::stdio_sync_filebuf*>(rdbuf)) f = sfbuf->file(); @@ -136,14 +136,12 @@ struct formatter, Char> }; /** - \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 + * Returns a view that formats `value` via an ostream `operator<<`. + * + * **Example**: + * + * fmt::print("Current thread id: {}\n", + * fmt::streamed(std::this_thread::get_id())); */ template constexpr auto streamed(const T& value) -> detail::streamed_view { @@ -172,18 +170,16 @@ void vprint(std::basic_ostream& os, } /** - \rst - Prints formatted data to the stream *os*. - - **Example**:: - - fmt::print(cerr, "Don't {}!", "panic"); - \endrst + * Prints formatted data to the stream `os`. + * + * **Example**: + * + * fmt::print(cerr, "Don't {}!", "panic"); */ FMT_EXPORT template void print(std::ostream& os, format_string fmt, T&&... args) { const auto& vargs = fmt::make_format_args(args...); - if (detail::is_utf8()) + if (detail::use_utf8()) vprint(os, fmt, vargs); else detail::vprint_directly(os, fmt, vargs); diff --git a/include/fmt/printf.h b/include/fmt/printf.h index fb57d23..072cc6b 100644 --- a/include/fmt/printf.h +++ b/include/fmt/printf.h @@ -8,7 +8,7 @@ #ifndef FMT_PRINTF_H_ #define FMT_PRINTF_H_ -#ifndef FMT_IMPORT_STD +#ifndef FMT_MODULE # include // std::max # include // std::numeric_limits #endif @@ -36,12 +36,8 @@ template class basic_printf_context { using parse_context_type = basic_format_parse_context; template using formatter_type = printf_formatter; - /** - \rst - Constructs a ``printf_context`` object. References to the arguments are - stored in the context object so make sure they have appropriate lifetimes. - \endrst - */ + /// Constructs a `printf_context` object. References to the arguments are + /// stored in the context object so make sure they have appropriate lifetimes. basic_printf_context(basic_appender out, basic_format_args args) : out_(out), args_(args) {} @@ -227,7 +223,7 @@ auto make_arg_formatter(basic_appender iter, format_specs& s) return {iter, s, locale_ref()}; } -// The ``printf`` argument formatter. +// The `printf` argument formatter. template class printf_arg_formatter : public arg_formatter { private: @@ -276,7 +272,6 @@ class printf_arg_formatter : public arg_formatter { base::operator()(value); } - /** Formats a null-terminated C string. */ void operator()(const char* value) { if (value) base::operator()(value); @@ -284,7 +279,6 @@ class printf_arg_formatter : public arg_formatter { 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); @@ -294,7 +288,6 @@ class printf_arg_formatter : public arg_formatter { void operator()(basic_string_view value) { base::operator()(value); } - /** Formats a pointer. */ void operator()(const void* value) { if (value) base::operator()(value); @@ -302,7 +295,6 @@ class printf_arg_formatter : public arg_formatter { write_null_pointer(); } - /** Formats an argument of a custom (user-defined) type. */ void operator()(typename basic_format_arg::handle handle) { auto parse_ctx = basic_format_parse_context({}); handle.format(parse_ctx, context_); @@ -573,16 +565,12 @@ using wprintf_context = basic_printf_context; using printf_args = basic_format_args; using wprintf_args = basic_format_args; -/** - \rst - Constructs an `~fmt::format_arg_store` object that contains references to - arguments and can be implicitly converted to `~fmt::printf_args`. - \endrst - */ +/// Constructs an `format_arg_store` object that contains references to +/// arguments and can be implicitly converted to `printf_args`. template inline auto make_printf_args(T&... args) - -> decltype(make_format_args>(args...)) { - return make_format_args>(args...); + -> decltype(fmt::make_format_args>(args...)) { + return fmt::make_format_args>(args...); } template struct vprintf_args { @@ -599,14 +587,13 @@ inline auto vsprintf(basic_string_view fmt, } /** - \rst - Formats arguments and returns the result as a string. - - **Example**:: - - std::string message = fmt::sprintf("The answer is %d", 42); - \endrst -*/ + * Formats `args` according to specifications in `fmt` and returns the result + * as as string. + * + * **Example**: + * + * std::string message = fmt::sprintf("The answer is %d", 42); + */ template > inline auto sprintf(const S& fmt, const T&... args) -> std::basic_string { return vsprintf(detail::to_string_view(fmt), @@ -625,13 +612,12 @@ inline auto vfprintf(std::FILE* f, basic_string_view fmt, } /** - \rst - Prints formatted data to the file *f*. - - **Example**:: - - fmt::fprintf(stderr, "Don't %s!", "panic"); - \endrst + * Formats `args` according to specifications in `fmt` and writes the output + * to `f`. + * + * **Example**: + * + * fmt::fprintf(stderr, "Don't %s!", "panic"); */ template > inline auto fprintf(std::FILE* f, const S& fmt, const T&... args) -> int { @@ -647,13 +633,12 @@ FMT_DEPRECATED inline auto vprintf(basic_string_view fmt, } /** - \rst - Prints formatted data to ``stdout``. - - **Example**:: - - fmt::printf("Elapsed time: %.2f seconds", 1.23); - \endrst + * Formats `args` according to specifications in `fmt` and writes the output + * to `stdout`. + * + * **Example**: + * + * fmt::printf("Elapsed time: %.2f seconds", 1.23); */ template inline auto printf(string_view fmt, const T&... args) -> int { diff --git a/include/fmt/ranges.h b/include/fmt/ranges.h index 3d9bcaf..0d3dfbd 100644 --- a/include/fmt/ranges.h +++ b/include/fmt/ranges.h @@ -8,17 +8,20 @@ #ifndef FMT_RANGES_H_ #define FMT_RANGES_H_ -#ifndef FMT_IMPORT_STD +#ifndef FMT_MODULE # include # include +# include # include # include +# include #endif #include "format.h" FMT_BEGIN_NAMESPACE +FMT_EXPORT enum class range_format { disabled, map, set, sequence, string, debug_string }; namespace detail { @@ -67,7 +70,7 @@ template struct has_member_fn_begin_end_t : std::false_type {}; template -struct has_member_fn_begin_end_t().begin()), +struct has_member_fn_begin_end_t().begin()), decltype(std::declval().end())>> : std::true_type {}; @@ -98,15 +101,15 @@ struct has_mutable_begin_end : std::false_type {}; template struct has_const_begin_end< - T, - void_t< - decltype(detail::range_begin(std::declval&>())), - decltype(detail::range_end(std::declval&>()))>> + T, void_t&>())), + decltype(detail::range_end( + std::declval&>()))>> : std::true_type {}; template struct has_mutable_begin_end< - T, void_t())), + T, void_t())), decltype(detail::range_end(std::declval())), // the extra int here is because older versions of MSVC don't // SFINAE properly unless there are distinct types @@ -487,14 +490,15 @@ struct range_formatter< auto out = ctx.out(); auto it = detail::range_begin(range); auto end = detail::range_end(range); - if (is_debug) return write_debug_string(out, it, end); + if (is_debug) return write_debug_string(out, std::move(it), end); out = detail::copy(opening_bracket_, out); int i = 0; for (; it != end; ++it) { if (i > 0) out = detail::copy(separator_, out); ctx.advance_to(out); - out = underlying_.format(mapper.map(*it), ctx); + auto&& item = *it; // Need an lvalue + out = underlying_.format(mapper.map(item), ctx); ++i; } out = detail::copy(closing_bracket_, out); @@ -502,6 +506,7 @@ struct range_formatter< } }; +FMT_EXPORT template struct range_format_kind : conditional_t< @@ -512,9 +517,11 @@ template struct formatter< R, Char, enable_if_t::value != - range_format::disabled && - range_format_kind::value != range_format::map> + bool_constant< + range_format_kind::value != range_format::disabled && + range_format_kind::value != range_format::map && + range_format_kind::value != range_format::string && + range_format_kind::value != range_format::debug_string> // Workaround a bug in MSVC 2015 and earlier. #if !FMT_MSC_VERSION || FMT_MSC_VERSION >= 1910 , @@ -526,8 +533,12 @@ struct formatter< range_formatter, Char> range_formatter_; public: + using nonlocking = void; + FMT_CONSTEXPR formatter() { - if (range_format_kind::value != range_format::set) return; + if (detail::const_check(range_format_kind::value != + range_format::set)) + return; range_formatter_.set_brackets(detail::string_literal{}, detail::string_literal{}); } @@ -581,19 +592,16 @@ struct formatter< template auto format(map_type& map, FormatContext& ctx) const -> decltype(ctx.out()) { - auto mapper = detail::range_mapper>(); auto out = ctx.out(); - auto it = detail::range_begin(map); - auto end = detail::range_end(map); - basic_string_view open = detail::string_literal{}; if (!no_delimiters_) out = detail::copy(open, out); int i = 0; + auto mapper = detail::range_mapper>(); basic_string_view sep = detail::string_literal{}; - for (; it != end; ++it) { + for (auto&& value : map) { if (i > 0) out = detail::copy(sep, out); ctx.advance_to(out); - detail::for_each2(formatters_, mapper.map(*it), + detail::for_each2(formatters_, mapper.map(value), detail::format_tuple_element{ 0, ctx, detail::string_literal{}}); ++i; @@ -604,6 +612,46 @@ struct formatter< } }; +// A (debug_)string formatter. +template +struct formatter< + R, Char, + enable_if_t::value == range_format::string || + range_format_kind::value == + range_format::debug_string>> { + private: + using range_type = detail::maybe_const_range; + using string_type = + conditional_t, + decltype(detail::range_begin(std::declval())), + decltype(detail::range_end(std::declval()))>::value, + detail::std_string_view, std::basic_string>; + + formatter underlying_; + + public: + template + FMT_CONSTEXPR auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { + return underlying_.parse(ctx); + } + + template + auto format(range_type& range, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto out = ctx.out(); + if (detail::const_check(range_format_kind::value == + range_format::debug_string)) + *out++ = '"'; + out = underlying_.format( + string_type{detail::range_begin(range), detail::range_end(range)}, ctx); + if (detail::const_check(range_format_kind::value == + range_format::debug_string)) + *out++ = '"'; + return out; + } +}; + template struct join_view : detail::view { It begin; @@ -611,7 +659,7 @@ struct join_view : detail::view { basic_string_view sep; join_view(It b, Sentinel e, basic_string_view s) - : begin(b), end(e), sep(s) {} + : begin(std::move(b)), end(e), sep(s) {} }; template @@ -625,55 +673,56 @@ struct formatter, Char> { #endif formatter, Char> value_formatter_; + using view_ref = conditional_t::value, + const join_view&, + join_view&&>; + public: + using nonlocking = void; + template FMT_CONSTEXPR auto parse(ParseContext& ctx) -> const Char* { return value_formatter_.parse(ctx); } template - auto format(const join_view& value, - FormatContext& ctx) const -> decltype(ctx.out()) { - auto it = value.begin; + auto format(view_ref& value, FormatContext& ctx) const + -> decltype(ctx.out()) { + auto it = std::forward(value).begin; auto out = ctx.out(); - if (it != value.end) { + if (it == value.end) return out; + out = value_formatter_.format(*it, ctx); + ++it; + while (it != value.end) { + out = detail::copy(value.sep.begin(), value.sep.end(), out); + ctx.advance_to(out); out = value_formatter_.format(*it, ctx); ++it; - while (it != value.end) { - out = detail::copy(value.sep.begin(), value.sep.end(), out); - ctx.advance_to(out); - out = value_formatter_.format(*it, ctx); - ++it; - } } return out; } }; -/** - Returns a view that formats the iterator range `[begin, end)` with elements - separated by `sep`. - */ +/// Returns a view that formats the iterator range `[begin, end)` with elements +/// separated by `sep`. template auto join(It begin, Sentinel end, string_view sep) -> join_view { - return {begin, end, sep}; + return {std::move(begin), end, sep}; } /** - \rst - Returns a view that formats `range` with elements separated by `sep`. - - **Example**:: - - std::vector v = {1, 2, 3}; - fmt::print("{}", fmt::join(v, ", ")); - // Output: "1, 2, 3" - - ``fmt::join`` applies passed format specifiers to the range elements:: - - fmt::print("{:02}", fmt::join(v, ", ")); - // Output: "01, 02, 03" - \endrst + * Returns a view that formats `range` with elements separated by `sep`. + * + * **Example**: + * + * auto v = std::vector{1, 2, 3}; + * fmt::print("{}", fmt::join(v, ", ")); + * // Output: 1, 2, 3 + * + * `fmt::join` applies passed format specifiers to the range elements: + * + * fmt::print("{:02}", fmt::join(v, ", ")); + * // Output: 01, 02, 03 */ template auto join(Range&& r, string_view sep) @@ -798,15 +847,13 @@ struct formatter< FMT_BEGIN_EXPORT /** - \rst - Returns an object that formats `tuple` with elements separated by `sep`. - - **Example**:: - - std::tuple t = {1, 'a'}; - fmt::print("{}", fmt::join(t, ", ")); - // Output: "1, a" - \endrst + * Returns an object that formats `std::tuple` with elements separated by `sep`. + * + * **Example**: + * + * auto t = std::tuple{1, 'a'}; + * fmt::print("{}", fmt::join(t, ", ")); + * // Output: 1, a */ template FMT_CONSTEXPR auto join(const std::tuple& tuple, string_view sep) @@ -815,15 +862,13 @@ FMT_CONSTEXPR auto join(const std::tuple& tuple, string_view 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 + * Returns an object that formats `std::initializer_list` with elements + * separated by `sep`. + * + * **Example**: + * + * fmt::print("{}", fmt::join({1, 2, 3}, ", ")); + * // Output: "1, 2, 3" */ template auto join(std::initializer_list list, string_view sep) diff --git a/include/fmt/std.h b/include/fmt/std.h index 5a5026f..8629ee1 100644 --- a/include/fmt/std.h +++ b/include/fmt/std.h @@ -8,7 +8,10 @@ #ifndef FMT_STD_H_ #define FMT_STD_H_ -#ifndef FMT_IMPORT_STD +#include "format.h" +#include "ostream.h" + +#ifndef FMT_MODULE # include # include # include @@ -20,17 +23,8 @@ # include # include # include -#endif - -#include "format.h" -#include "ostream.h" - -#if FMT_HAS_INCLUDE() -# include -#endif -#ifndef FMT_IMPORT_STD -// Checking FMT_CPLUSPLUS for warning suppression in MSVC. +// Check FMT_CPLUSPLUS to suppress a bogus warning in MSVC. # if FMT_CPLUSPLUS >= 201703L # if FMT_HAS_INCLUDE() # include @@ -42,14 +36,18 @@ # include # endif # endif - -# if FMT_HAS_INCLUDE() && FMT_CPLUSPLUS > 202002L -# include -# endif - +// Use > instead of >= in the version check because may be +// available after C++17 but before C++20 is marked as implemented. # if FMT_CPLUSPLUS > 201703L && FMT_HAS_INCLUDE() # include # endif +# if FMT_CPLUSPLUS > 202002L && FMT_HAS_INCLUDE() +# include +# endif +#endif // FMT_MODULE + +#if FMT_HAS_INCLUDE() +# include #endif // GCC 4 does not support FMT_HAS_INCLUDE. @@ -62,17 +60,6 @@ # endif #endif -// Check if typeid is available. -#ifndef FMT_USE_TYPEID -// __RTTI is for EDG compilers. _CPPRTTI is for MSVC. -# if defined(__GXX_RTTI) || FMT_HAS_FEATURE(cxx_rtti) || defined(_CPPRTTI) || \ - defined(__INTEL_RTTI__) || defined(__RTTI) -# define FMT_USE_TYPEID 1 -# else -# define FMT_USE_TYPEID 0 -# endif -#endif - // For older Xcode versions, __cpp_lib_xxx flags are inaccurately defined. #ifndef FMT_CPP_LIB_FILESYSTEM # ifdef __cpp_lib_filesystem @@ -142,7 +129,9 @@ template struct formatter { it = detail::parse_align(it, end, specs_); if (it == end) return it; - it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); + Char c = *it; + if ((c >= '0' && c <= '9') || c == '{') + it = detail::parse_dynamic_spec(it, end, specs_.width, width_ref_, ctx); if (it != end && *it == '?') { debug_ = true; ++it; @@ -158,8 +147,7 @@ template struct formatter { !path_type_ ? p.native() : p.generic_string(); - detail::handle_dynamic_spec(specs.width, width_ref_, - ctx); + detail::handle_dynamic_spec(specs.width, width_ref_, ctx); if (!debug_) { auto s = detail::get_path_string(p, path_string); return detail::write(ctx.out(), basic_string_view(s), specs); @@ -171,6 +159,22 @@ template struct formatter { specs); } }; + +class path : public std::filesystem::path { + public: + auto display_string() const -> std::string { + const std::filesystem::path& base = *this; + return fmt::format(FMT_STRING("{}"), base); + } + auto system_string() const -> std::string { return string(); } + + auto generic_display_string() const -> std::string { + const std::filesystem::path& base = *this; + return fmt::format(FMT_STRING("{:g}"), base); + } + auto generic_system_string() const -> std::string { return generic_string(); } +}; + FMT_END_NAMESPACE #endif // FMT_CPP_LIB_FILESYSTEM @@ -288,7 +292,7 @@ struct formatter, Char, if (value.has_value()) { out = detail::write(out, "expected("); - out = detail::write_escaped_alternative(out, value.value()); + out = detail::write_escaped_alternative(out, *value); } else { out = detail::write(out, "unexpected("); out = detail::write_escaped_alternative(out, value.error()); @@ -427,6 +431,95 @@ template struct formatter { } }; +#if FMT_USE_RTTI +namespace detail { + +template +auto write_demangled_name(OutputIt out, const std::type_info& ti) -> OutputIt { +# ifdef FMT_HAS_ABI_CXA_DEMANGLE + int status = 0; + std::size_t size = 0; + std::unique_ptr 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()); + } + return detail::write_bytes(out, demangled_name_view); +# elif FMT_MSC_VERSION + const string_view demangled_name(ti.name()); + for (std::size_t i = 0; i < demangled_name.size(); ++i) { + auto sub = demangled_name; + sub.remove_prefix(i); + if (sub.starts_with("enum ")) { + i += 4; + continue; + } + if (sub.starts_with("class ") || sub.starts_with("union ")) { + i += 5; + continue; + } + if (sub.starts_with("struct ")) { + i += 6; + continue; + } + if (*sub.begin() != ' ') *out++ = *sub.begin(); + } + return out; +# else + return detail::write_bytes(out, string_view(ti.name())); +# endif +} + +} // namespace detail + +FMT_EXPORT +template +struct formatter { + public: + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + return ctx.begin(); + } + + template + auto format(const std::type_info& ti, Context& ctx) const + -> decltype(ctx.out()) { + return detail::write_demangled_name(ctx.out(), ti); + } +}; +#endif + FMT_EXPORT template struct formatter< @@ -443,7 +536,7 @@ struct formatter< if (it == end || *it == '}') return it; if (*it == 't') { ++it; - with_typename_ = FMT_USE_TYPEID != 0; + with_typename_ = FMT_USE_RTTI != 0; } return it; } @@ -452,65 +545,14 @@ struct formatter< auto format(const std::exception& ex, Context& ctx) const -> decltype(ctx.out()) { auto out = ctx.out(); - if (!with_typename_) - return detail::write_bytes(out, string_view(ex.what())); - -#if FMT_USE_TYPEID - const std::type_info& ti = typeid(ex); -# ifdef FMT_HAS_ABI_CXA_DEMANGLE - int status = 0; - std::size_t size = 0; - std::unique_ptr 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()); +#if FMT_USE_RTTI + if (with_typename_) { + out = detail::write_demangled_name(out, typeid(ex)); + *out++ = ':'; + *out++ = ' '; } - out = detail::write_bytes(out, demangled_name_view); -# 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); -# else - out = detail::write_bytes(out, string_view(ti.name()) - }); -# endif - *out++ = ':'; - *out++ = ' '; - return detail::write_bytes(out, string_view(ex.what())); #endif + return detail::write_bytes(out, string_view(ex.what())); } }; @@ -590,33 +632,65 @@ struct formatter : formatter { #endif // __cpp_lib_atomic_flag_test FMT_EXPORT -template -struct formatter, Char> : nested_formatter { +template struct formatter, Char> { private: - // Functor because C++11 doesn't support generic lambdas. - struct writer { - const formatter, Char>* f; - const std::complex& c; - - template - FMT_CONSTEXPR auto operator()(OutputIt out) -> OutputIt { - if (c.real() != 0) { - auto format_full = detail::string_literal{}; - return fmt::format_to(out, basic_string_view(format_full), - f->nested(c.real()), f->nested(c.imag())); - } - auto format_imag = detail::string_literal{}; - return fmt::format_to(out, basic_string_view(format_imag), - f->nested(c.imag())); + detail::dynamic_format_specs specs_; + + template + FMT_CONSTEXPR auto do_format(const std::complex& c, + detail::dynamic_format_specs& specs, + FormatContext& ctx, OutputIt out) const + -> OutputIt { + if (c.real() != 0) { + *out++ = Char('('); + out = detail::write(out, c.real(), specs, ctx.locale()); + specs.sign = sign::plus; + out = detail::write(out, c.imag(), specs, ctx.locale()); + if (!detail::isfinite(c.imag())) *out++ = Char(' '); + *out++ = Char('i'); + *out++ = Char(')'); + return out; } - }; + out = detail::write(out, c.imag(), specs, ctx.locale()); + if (!detail::isfinite(c.imag())) *out++ = Char(' '); + *out++ = Char('i'); + return out; + } public: + FMT_CONSTEXPR auto parse(basic_format_parse_context& ctx) + -> decltype(ctx.begin()) { + if (ctx.begin() == ctx.end() || *ctx.begin() == '}') return ctx.begin(); + return parse_format_specs(ctx.begin(), ctx.end(), specs_, ctx, + detail::type_constant::value); + } + template - auto format(const std::complex& c, FormatContext& ctx) const + auto format(const std::complex& c, FormatContext& ctx) const -> decltype(ctx.out()) { - return this->write_padded(ctx, writer{this, c}); + auto specs = specs_; + if (specs.width_ref.kind != detail::arg_id_kind::none || + specs.precision_ref.kind != detail::arg_id_kind::none) { + detail::handle_dynamic_spec(specs.width, specs.width_ref, ctx); + detail::handle_dynamic_spec(specs.precision, specs.precision_ref, ctx); + } + + if (specs.width == 0) return do_format(c, specs, ctx, ctx.out()); + auto buf = basic_memory_buffer(); + + auto outer_specs = format_specs(); + outer_specs.width = specs.width; + outer_specs.fill = specs.fill; + outer_specs.align = specs.align; + + specs.width = 0; + specs.fill = {}; + specs.align = align::none; + + do_format(c, specs, ctx, basic_appender(buf)); + return detail::write(ctx.out(), + basic_string_view(buf.data(), buf.size()), + outer_specs); } }; diff --git a/include/fmt/xchar.h b/include/fmt/xchar.h index a6bf47f..b1f39ed 100644 --- a/include/fmt/xchar.h +++ b/include/fmt/xchar.h @@ -8,27 +8,20 @@ #ifndef FMT_XCHAR_H_ #define FMT_XCHAR_H_ -#ifndef FMT_IMPORT_STD -# include -#endif - #include "color.h" #include "format.h" #include "ranges.h" -#if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) && !defined(FMT_IMPORT_STD) -# include +#ifndef FMT_MODULE +# include +# if !defined(FMT_STATIC_THOUSANDS_SEPARATOR) +# include +# endif #endif FMT_BEGIN_NAMESPACE namespace detail { -#ifdef __cpp_char8_t -using char8_type = char8_t; -#else -enum char8_type : unsigned char {}; -#endif - template using is_exotic_char = bool_constant::value>; @@ -83,10 +76,14 @@ inline auto runtime(wstring_view s) -> runtime_format_string { #endif template <> struct is_char : std::true_type {}; -template <> struct is_char : std::true_type {}; template <> struct is_char : std::true_type {}; template <> struct is_char : std::true_type {}; +#ifdef __cpp_char8_t +template <> +struct is_char : bool_constant {}; +#endif + template constexpr auto make_wformat_args(T&... args) -> decltype(fmt::make_format_args(args...)) { @@ -315,9 +312,7 @@ FMT_DEPRECATED void print(const text_style& ts, wformat_string fmt, return print(stdout, ts, fmt, args...); } -/** - Converts *value* to ``std::wstring`` using the default format for type *T*. - */ +/// Converts `value` to `std::wstring` using the default format for type `T`. template inline auto to_wstring(const T& value) -> std::wstring { return format(FMT_STRING(L"{}"), value); } diff --git a/include/pacman.hpp b/include/pacman.hpp index acb457a..9fb1831 100644 --- a/include/pacman.hpp +++ b/include/pacman.hpp @@ -23,19 +23,19 @@ #include #include -#include -#include -#include - #include #include #include +#include +#include +#include + using std::string; using std::string_view; #define ARRAYSIZE(a) (sizeof(a) / sizeof(a[0])) -#define CLBUF_SIZE 4096 +#define CLBUF_SIZE 4096 /* As of 2015/10/20, the longest title (all locales considered) was less than 30 * characters long. We set the title maximum length to 50 to allow for some @@ -47,7 +47,8 @@ using std::string_view; * information displayed by pacman. Titles are stored in the `titles` array and * referenced by the following indices. */ -enum { +enum +{ T_ARCHITECTURE = 0, T_BACKUP_FILES, T_BUILD_DATE, @@ -82,10 +83,10 @@ enum { static char titles[_T_MAX][TITLE_MAXLEN * sizeof(wchar_t)]; -int getcols_fd(int fd); +int getcols_fd(int fd); unsigned short getcols(void); -void string_display(string_view title, string_view string, unsigned short cols = getcols()); -void list_display(const char *title, const alpm_list_t *list, unsigned short maxcols = getcols()); -void deplist_display(const char *title, alpm_list_t *deps, unsigned short cols = getcols()); -void indentprint(const char *str, unsigned short indent, unsigned short cols = getcols()); -void list_display_linebreak(const char *title, const alpm_list_t *list, unsigned short maxcols = getcols()); +void string_display(std::string_view title, std::string_view string, unsigned short cols = getcols()); +void list_display(const char* title, const alpm_list_t* list, unsigned short maxcols = getcols()); +void deplist_display(const char* title, alpm_list_t* deps, unsigned short cols = getcols()); +void indentprint(const char* str, unsigned short indent, unsigned short cols = getcols()); +void list_display_linebreak(const char* title, const alpm_list_t* list, unsigned short maxcols = getcols()); diff --git a/include/switch_fnv1a.hpp b/include/switch_fnv1a.hpp new file mode 100644 index 0000000..9d1266b --- /dev/null +++ b/include/switch_fnv1a.hpp @@ -0,0 +1,265 @@ +/** + * Switch/Case strings using constexpr Fnv1-a. + * @comment References: + * @comment References: + * @maintainer xavier dot roche at algolia.com + */ + +#pragma once + +#include +#include +#include +#include + +// Traits for FNV1a +template +struct fnv1a_traits +{ + static constexpr bool Supported = false; +}; + +// Traits for 16-bit FNV1a (added by myself for customfetch) +template<> +struct fnv1a_traits<16> +{ + static constexpr bool Supported = true; + using Type = uint16_t; + + static constexpr Type Prime = 0x389; + static constexpr Type Offset = 0x9DC5; +}; + +// Traits for 32-bit FNV1a +template<> +struct fnv1a_traits<32> +{ + static constexpr bool Supported = true; + using Type = uint32_t; + + static constexpr Type Prime = 0x1000193; + static constexpr Type Offset = 0x811c9dc5; +}; + +// Traits for 64-bit FNV1a +template<> +struct fnv1a_traits<64> +{ + static constexpr bool Supported = true; + using Type = uint64_t; + + static constexpr Type Prime = 0x100000001b3; + static constexpr Type Offset = 0xcbf29ce484222325; +}; + +static constexpr __uint128_t Pack128(uint64_t high, uint64_t low) +{ + return ((__uint128_t)high << 64) + (__uint128_t)low; +} + +// Traits for 128-bit FNV1a +template<> +struct fnv1a_traits<128> +{ + static constexpr bool Supported = true; + using Type = __uint128_t; + + static constexpr Type Prime = Pack128(0x1000000, 0x000000000000013b); + static constexpr Type Offset = Pack128(0x6c62272e07bb0142, 0x62b821756295c58d); +}; + +// Generic FNV1a implementation +template +struct fnv1a +{ + static_assert(fnv1a_traits::Supported); + using Type = typename fnv1a_traits::Type; + + /** + * Compute the Fowler–Noll–Vo hash + * @comment stop An optional stop character + * @param s The string + * @param l The string size + * @return The fnv-1a hash + */ + template + static constexpr Type hash_container(T s, + const std::size_t l, + L stopLen = nullptr, + Type hash = fnv1a_traits::Offset) + { + // See + std::size_t j = 0; + for (; j < l; j++) { + const uint8_t byte = static_cast(s[j]); + if constexpr (stop != 0) { + if (byte == stop) { + if constexpr (!std::is_same::value) { + *stopLen = j + 1; + } + break; + } + } + hash ^= byte; + hash *= fnv1a_traits::Prime; + } + + return hash; + } + + /** + * Compute the Fowler–Noll–Vo hash + * @comment stop An optional stop character + * @param s The string + * @param l The string size + * @return The fnv-1a hash + */ + template + static constexpr Type hash(const C* s, + const std::size_t l, + L stopLen = nullptr, + Type hash = fnv1a_traits::Offset) + { + // Accept [ unsigned | signed ] char + static_assert(std::is_integral::value); + static_assert(sizeof(C) == 1); + return hash_container(s, l, stopLen, hash); + } + + /** + * Compute the Fowler–Noll–Vo hash + * @param s The string + * @return The fnv-1a hash + */ + template + static constexpr Type hash(const char (&s)[l]) + { + return hash(&s[0], l - 1); + } + + // Do not infer length for char arrays + template + static constexpr Type hash(char (&s)[l]) + { + return hash(&s[0]); + } + + /** + * Compute the Fowler–Noll–Vo hash + * @param s The string + * @return The fnv-1a hash + */ + static constexpr Type hash(const char* s) { return hash(s, __builtin_strlen(s)); } + + /** + * Compute the Fowler–Noll–Vo hash + * @param str The string + * @return The fnv-1a hash + */ + static constexpr Type hash(const std::string& str) { return hash(str.c_str(), str.size()); } + + /** + * Compute the Fowler–Noll–Vo hash + * @param str The string view + * @return The fnv-1a hash + */ + template + static constexpr Type hash(const std::basic_string_view& str) + { + // Accept [ unsigned | signed ] char + static_assert(std::is_integral::value); + static_assert(sizeof(C) == 1); + return hash(str.data(), str.size()); + } +}; + +using fnv1a16 = fnv1a<16>; +using fnv1a32 = fnv1a<32>; +using fnv1a64 = fnv1a<64>; +using fnv1a128 = fnv1a<128>; + +constexpr fnv1a16::Type operator"" _fnv1a16(const char* s, const std::size_t l) +{ + return fnv1a16::hash(s, l); +} +constexpr fnv1a32::Type operator"" _fnv1a32(const char* s, const std::size_t l) +{ + return fnv1a32::hash(s, l); +} +constexpr fnv1a64::Type operator"" _fnv1a64(const char* s, const std::size_t l) +{ + return fnv1a64::hash(s, l); +} +constexpr fnv1a128::Type operator"" _fnv1a128(const char* s, const std::size_t l) +{ + return fnv1a128::hash(s, l); +} + +// Static unit tests: +//static_assert("hello"_fnv1a32 == 0x4f9f2cab); +//static_assert("hello"_fnv1a64 == 0xa430d84680aabd0b); +//static_assert("hello"_fnv1a128 == Pack128(0xe3e1efd54283d94f, 0x7081314b599d31b3)); + +using strhash = fnv1a128; +constexpr strhash::Type operator"" _strhash(const char* s, const std::size_t l) +{ + return strhash::hash(s, l); +} + +// Lowercase operator[] wrapper for any operator[]-aware types +namespace strhash_lower { +template +class _lowercase_container +{ +public: + constexpr _lowercase_container(const T& container) + : _container(container) + {} + + constexpr auto operator[](std::size_t index) const + { + const auto c = _container[index]; + static_assert(std::is_integral::value); + static_assert(sizeof(c) == 1); + return c >= 'A' && c <= 'Z' ? (c + 'a' - 'A') : c; + } + +private: + const T& _container; +}; + +/** + * Hash a generic container, using a lowercase modifier, providing 'size' 8-byte characters through operator[] + */ +template +constexpr strhash::Type hash(const T& container, std::size_t size) +{ + return strhash::hash_container(strhash_lower::_lowercase_container(container), size); +} + +/** + * Hash a std::string, using a lowercase modifier + */ +/*static constexpr strhash::Type hash(const std::string& str) +{ + return hash(str, str.size()); +}*/ + +/** + * Hash a std::string, using a lowercase modifier + */ +template +static constexpr strhash::Type hash(const std::basic_string_view& str) +{ + // Accept [ unsigned | signed ] char + static_assert(std::is_integral::value); + static_assert(sizeof(C) == 1); + return hash(str.data(), str.size()); +} +} // namespace strhash_lower + +// Case-insensitive version +constexpr strhash::Type operator"" _strhash_lower(const char* s, const std::size_t l) +{ + return strhash_lower::hash(s, l); +} diff --git a/include/taur.hpp b/include/taur.hpp index f6dcf4e..ec2b4a1 100644 --- a/include/taur.hpp +++ b/include/taur.hpp @@ -1,71 +1,72 @@ #ifndef TAUR_HPP #define TAUR_HPP -#include "cpr/cpr.h" -#include "util.hpp" #include #include -#include #include -#include #include #include #include #include #include +#include +#include + +#include "cpr/cpr.h" +#include "util.hpp" + class Config; -using std::string; -using std::string_view; using std::filesystem::path; -using std::vector; -using std::optional; -struct TaurPkg_t { - string name; - string version; - string aur_url; - string url; - string desc; - string arch; - string maintainer; - time_t last_modified; - time_t outofdate; - float popularity = 1; // normal - float votes = 0; // system packages have no votes - vector licenses; - vector makedepends; - vector depends; - vector totaldepends; - bool installed = false; - string db_name = "aur"; +struct TaurPkg_t +{ + std::string name; + std::string version; + std::string aur_url; + std::string url; + std::string desc; + std::string arch; + std::string maintainer; + time_t last_modified; + time_t outofdate; + float popularity = 1; // normal + float votes = 0; // system packages have no votes + std::vector licenses; + std::vector makedepends; + std::vector depends; + std::vector totaldepends; + bool installed = false; + std::string db_name = "aur"; - alpm_list_t *licenses_list; - alpm_list_t *depends_list; + alpm_list_t* licenses_list; + alpm_list_t* depends_list; }; -class TaurBackend { - public: +class TaurBackend +{ +public: Config& config; TaurBackend(Config& cfg); - // They are different because we found that fetching each AUR pkg is very time consuming, so we store the name and look it up later. - vector getPkgFromJson(rapidjson::Document& doc, bool useGit); - vector search_pac(string_view query); - vector search(string_view query, bool useGit, bool aurOnly, bool checkExactMatch = true); - bool download_tar(string_view url, path out_path); - bool download_git(string_view url, path out_path); - bool download_pkg(string_view url, path out_path); - optional fetch_pkg(string_view pkg, bool returnGit); - vector fetch_pkgs(vector const& pkgs, bool returnGit); - bool remove_pkgs(alpm_list_smart_pointer& pkgs); - bool remove_pkg(alpm_pkg_t *pkgs, bool ownTransaction = true); - bool handle_aur_depends(TaurPkg_t pkg, path out_path, vector const& localPkgs, bool useGit); - bool build_pkg(string_view pkg_name, string extracted_path, bool alreadyprepared); - bool update_all_aur_pkgs(path cacheDir, bool useGit); - vector get_all_local_pkgs(bool aurOnly); + // They are different because we found that fetching each AUR pkg is very time consuming, so we store the name and + // look it up later. + std::vector getPkgFromJson(rapidjson::Document& doc, bool useGit); + std::vector search_pac(std::string_view query); + std::vector search(std::string_view query, bool useGit, bool aurOnly, bool checkExactMatch = true); + bool download_tar(std::string_view url, path out_path); + bool download_git(std::string_view url, path out_path); + bool download_pkg(std::string_view url, path out_path); + std::optional fetch_pkg(std::string_view pkg, bool returnGit); + std::vector fetch_pkgs(std::vector const& pkgs, bool returnGit); + bool remove_pkgs(alpm_list_smart_pointer& pkgs); + bool remove_pkg(alpm_pkg_t* pkgs, bool ownTransaction = true); + bool handle_aur_depends(const TaurPkg_t& pkg, path out_path, std::vector const& localPkgs, bool useGit); + bool build_pkg(std::string_view pkg_name, std::string_view extracted_path, bool alreadyprepared); + bool update_all_aur_pkgs(path cacheDir, bool useGit); + std::vector get_all_local_pkgs(bool aurOnly); }; -inline string built_pkg, pkgs_to_install, pkgs_failed_to_build; +inline std::string built_pkg, pkgs_to_install, pkgs_failed_to_build; #endif diff --git a/include/toml++/toml.hpp b/include/toml++/toml.hpp index 0599bf5..1bb9d66 100644 --- a/include/toml++/toml.hpp +++ b/include/toml++/toml.hpp @@ -212,6 +212,14 @@ #endif #endif +#ifndef TOML_NVCC +#ifdef __NVCOMPILER_MAJOR__ +#define TOML_NVCC __NVCOMPILER_MAJOR__ +#else +#define TOML_NVCC 0 +#endif +#endif + #ifndef TOML_ARCH_ITANIUM #if defined(__ia64__) || defined(__ia64) || defined(_IA64) || defined(__IA64__) || defined(_M_IA64) #define TOML_ARCH_ITANIUM 1 @@ -420,6 +428,7 @@ #endif // TOML_ALWAYS_INLINE +#ifndef TOML_ALWAYS_INLINE #ifdef _MSC_VER #define TOML_ALWAYS_INLINE __forceinline #elif TOML_GCC || TOML_CLANG || TOML_HAS_ATTR(__always_inline__) @@ -429,8 +438,10 @@ #else #define TOML_ALWAYS_INLINE inline #endif +#endif // TOML_NEVER_INLINE +#ifndef TOML_NEVER_INLINE #ifdef _MSC_VER #define TOML_NEVER_INLINE TOML_DECLSPEC(noinline) #elif TOML_CUDA // https://gitlab.gnome.org/GNOME/glib/-/issues/2555 @@ -443,19 +454,27 @@ #ifndef TOML_NEVER_INLINE #define TOML_NEVER_INLINE #endif +#endif // MSVC attributes +#ifndef TOML_ABSTRACT_INTERFACE #define TOML_ABSTRACT_INTERFACE TOML_DECLSPEC(novtable) +#endif +#ifndef TOML_EMPTY_BASES #define TOML_EMPTY_BASES TOML_DECLSPEC(empty_bases) +#endif // TOML_TRIVIAL_ABI +#ifndef TOML_TRIVIAL_ABI #if TOML_CLANG || TOML_HAS_ATTR(__trivial_abi__) #define TOML_TRIVIAL_ABI TOML_ATTR(__trivial_abi__) #else #define TOML_TRIVIAL_ABI #endif +#endif // TOML_NODISCARD +#ifndef TOML_NODISCARD #if TOML_CPP >= 17 && TOML_HAS_CPP_ATTR(nodiscard) >= 201603 #define TOML_NODISCARD [[nodiscard]] #elif TOML_CLANG || TOML_GCC || TOML_HAS_ATTR(__warn_unused_result__) @@ -463,13 +482,16 @@ #else #define TOML_NODISCARD #endif +#endif // TOML_NODISCARD_CTOR +#ifndef TOML_NODISCARD_CTOR #if TOML_CPP >= 17 && TOML_HAS_CPP_ATTR(nodiscard) >= 201907 #define TOML_NODISCARD_CTOR [[nodiscard]] #else #define TOML_NODISCARD_CTOR #endif +#endif // pure + const #ifndef TOML_PURE @@ -519,6 +541,7 @@ #endif // TOML_ASSUME +#ifndef TOML_ASSUME #ifdef _MSC_VER #define TOML_ASSUME(expr) __assume(expr) #elif TOML_ICC || TOML_CLANG || TOML_HAS_BUILTIN(__builtin_assume) @@ -530,8 +553,10 @@ #else #define TOML_ASSUME(expr) static_cast(0) #endif +#endif // TOML_UNREACHABLE +#ifndef TOML_UNREACHABLE #ifdef _MSC_VER #define TOML_UNREACHABLE __assume(0) #elif TOML_ICC || TOML_CLANG || TOML_GCC || TOML_HAS_BUILTIN(__builtin_unreachable) @@ -539,6 +564,7 @@ #else #define TOML_UNREACHABLE static_cast(0) #endif +#endif // TOML_LIKELY #if TOML_CPP >= 20 && TOML_HAS_CPP_ATTR(likely) >= 201803 @@ -3614,6 +3640,16 @@ TOML_PUSH_WARNINGS; #undef max #endif +// workaround for this: https://github.com/marzer/tomlplusplus/issues/220 +#if TOML_NVCC +#define TOML_NVCC_WORKAROUND \ + { \ + return {}; \ + } +#else +#define TOML_NVCC_WORKAROUND = 0 +#endif + TOML_NAMESPACE_START { class TOML_ABSTRACT_INTERFACE TOML_EXPORTED_CLASS node @@ -3734,10 +3770,10 @@ TOML_NAMESPACE_START TOML_EXPORTED_MEMBER_FUNCTION virtual ~node() noexcept; - TOML_PURE_GETTER + TOML_NODISCARD virtual bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept = 0; - TOML_PURE_GETTER + TOML_NODISCARD virtual bool is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept = 0; TOML_PURE_GETTER @@ -3756,16 +3792,16 @@ TOML_NAMESPACE_START } TOML_PURE_GETTER - virtual node_type type() const noexcept = 0; + virtual node_type type() const noexcept TOML_NVCC_WORKAROUND; TOML_PURE_GETTER - virtual bool is_table() const noexcept = 0; + virtual bool is_table() const noexcept TOML_NVCC_WORKAROUND; TOML_PURE_GETTER virtual bool is_array() const noexcept = 0; TOML_PURE_GETTER - virtual bool is_array_of_tables() const noexcept = 0; + virtual bool is_array_of_tables() const noexcept TOML_NVCC_WORKAROUND; TOML_PURE_GETTER virtual bool is_value() const noexcept = 0; @@ -3820,6 +3856,8 @@ TOML_NAMESPACE_START return is_time(); else if constexpr (std::is_same_v) return is_date_time(); + + TOML_UNREACHABLE; } TOML_PURE_GETTER @@ -3902,6 +3940,8 @@ TOML_NAMESPACE_START return as_time(); else if constexpr (std::is_same_v) return as_date_time(); + + TOML_UNREACHABLE; } template @@ -3930,6 +3970,8 @@ TOML_NAMESPACE_START return as_time(); else if constexpr (std::is_same_v) return as_date_time(); + + TOML_UNREACHABLE; } template @@ -4207,6 +4249,8 @@ TOML_IMPL_NAMESPACE_START } TOML_IMPL_NAMESPACE_END; +#undef TOML_NVCC_WORKAROUND + #ifdef _MSC_VER #pragma pop_macro("min") #pragma pop_macro("max") @@ -4388,7 +4432,7 @@ TOML_NAMESPACE_START return node_->is_homogeneous(ntype, first_nonmatch); } - TOML_NODISCARD + TOML_PURE_GETTER bool is_homogeneous(node_type ntype) const noexcept { return node_ ? node_->is_homogeneous(ntype) : false; @@ -5074,7 +5118,7 @@ TOML_NAMESPACE_START return ntype == node_type::none || ntype == impl::node_type_of; } - TOML_PURE_GETTER + TOML_NODISCARD bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept final { if (ntype != node_type::none && ntype != impl::node_type_of) @@ -5085,7 +5129,7 @@ TOML_NAMESPACE_START return true; } - TOML_PURE_GETTER + TOML_NODISCARD bool is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept final { if (ntype != node_type::none && ntype != impl::node_type_of) @@ -5599,11 +5643,11 @@ TOML_NAMESPACE_START "Retrieving values as wide-character strings with node::value_exact() is only " "supported on Windows with TOML_ENABLE_WINDOWS_COMPAT enabled."); - static_assert((is_native || can_represent_native)&&!is_cvref, + static_assert((is_native || can_represent_native) && !is_cvref, TOML_SA_VALUE_EXACT_FUNC_MESSAGE("return type of node::value_exact()")); // prevent additional compiler error spam when the static_assert fails by gating behind if constexpr - if constexpr ((is_native || can_represent_native)&&!is_cvref) + if constexpr ((is_native || can_represent_native) && !is_cvref) { if (type() == node_type_of) return { this->get_value_exact() }; @@ -5621,7 +5665,7 @@ TOML_NAMESPACE_START static_assert(!is_wide_string || TOML_ENABLE_WINDOWS_COMPAT, "Retrieving values as wide-character strings with node::value() is only " "supported on Windows with TOML_ENABLE_WINDOWS_COMPAT enabled."); - static_assert((is_native || can_represent_native || can_partially_represent_native)&&!is_cvref, + static_assert((is_native || can_represent_native || can_partially_represent_native) && !is_cvref, TOML_SA_VALUE_FUNC_MESSAGE("return type of node::value()")); // when asking for strings, dates, times and date_times there's no 'fuzzy' conversion @@ -6334,11 +6378,11 @@ TOML_NAMESPACE_START TOML_EXPORTED_MEMBER_FUNCTION bool is_homogeneous(node_type ntype) const noexcept final; - TOML_PURE_GETTER + TOML_NODISCARD TOML_EXPORTED_MEMBER_FUNCTION bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept final; - TOML_PURE_GETTER + TOML_NODISCARD TOML_EXPORTED_MEMBER_FUNCTION bool is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept final; @@ -7023,8 +7067,8 @@ TOML_NAMESPACE_START { using raw_elem_type = impl::remove_cvref; using elem_type = std::conditional_t, // - impl::emplaced_type_of, - raw_elem_type>; + impl::emplaced_type_of, + raw_elem_type>; using type = impl::remove_cvref>; static_assert(impl::is_native || impl::is_one_of, @@ -7061,8 +7105,8 @@ TOML_NAMESPACE_START { using raw_elem_type = impl::remove_cvref; using elem_type = std::conditional_t, // - impl::emplaced_type_of, - raw_elem_type>; + impl::emplaced_type_of, + raw_elem_type>; static constexpr auto moving_node_ptr = std::is_same_v // && sizeof...(Args) == 1u // @@ -7677,11 +7721,11 @@ TOML_NAMESPACE_START TOML_EXPORTED_MEMBER_FUNCTION bool is_homogeneous(node_type ntype) const noexcept final; - TOML_PURE_GETTER + TOML_NODISCARD TOML_EXPORTED_MEMBER_FUNCTION bool is_homogeneous(node_type ntype, node*& first_nonmatch) noexcept final; - TOML_PURE_GETTER + TOML_NODISCARD TOML_EXPORTED_MEMBER_FUNCTION bool is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept final; @@ -8416,6 +8460,8 @@ TOML_NAMESPACE_START } return iterator{ ipos }; } + + TOML_UNREACHABLE; } TOML_CONSTRAINED_TEMPLATE((is_key_or_convertible || impl::is_wide_string), @@ -11813,7 +11859,7 @@ TOML_NAMESPACE_START return true; } - TOML_PURE_GETTER + TOML_NODISCARD TOML_EXTERNAL_LINKAGE bool array::is_homogeneous(node_type ntype, node * &first_nonmatch) noexcept { @@ -11835,7 +11881,7 @@ TOML_NAMESPACE_START return true; } - TOML_PURE_GETTER + TOML_NODISCARD TOML_EXTERNAL_LINKAGE bool array::is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept { @@ -12168,7 +12214,7 @@ TOML_NAMESPACE_START return true; } - TOML_PURE_GETTER + TOML_NODISCARD TOML_EXTERNAL_LINKAGE bool table::is_homogeneous(node_type ntype, node * &first_nonmatch) noexcept { @@ -12191,7 +12237,7 @@ TOML_NAMESPACE_START return true; } - TOML_PURE_GETTER + TOML_NODISCARD TOML_EXTERNAL_LINKAGE bool table::is_homogeneous(node_type ntype, const node*& first_nonmatch) const noexcept { @@ -14688,7 +14734,7 @@ TOML_IMPL_NAMESPACE_START set_error_and_return_default("'"sv, traits::full_prefix, std::string_view{ digits, length }, - "' is not representable in 64 bits"sv); + "' is not representable as a signed 64-bit integer"sv); // do the thing { @@ -14712,7 +14758,7 @@ TOML_IMPL_NAMESPACE_START set_error_and_return_default("'"sv, traits::full_prefix, std::string_view{ digits, length }, - "' is not representable in 64 bits"sv); + "' is not representable as a signed 64-bit integer"sv); if constexpr (traits::is_signed) { @@ -16205,16 +16251,10 @@ TOML_ANON_NAMESPACE_START { #if TOML_EXCEPTIONS #define TOML_PARSE_FILE_ERROR(msg, path) \ - throw parse_error{ msg, source_position{}, std::make_shared(std::move(path)) } + throw parse_error(msg, source_position{}, std::make_shared(std::move(path))) #else #define TOML_PARSE_FILE_ERROR(msg, path) \ - return parse_result \ - { \ - parse_error \ - { \ - msg, source_position{}, std::make_shared(std::move(path)) \ - } \ - } + return parse_result(parse_error(msg, source_position{}, std::make_shared(std::move(path)))) #endif std::string file_path_str(file_path); @@ -16976,7 +17016,6 @@ TOML_ANON_NAMESPACE_START val *= -1.0; } return weight + static_cast(log10(val)) + 1u; - break; } case node_type::boolean: return 5u; @@ -17701,6 +17740,7 @@ TOML_POP_WARNINGS; #undef TOML_NEVER_INLINE #undef TOML_NODISCARD #undef TOML_NODISCARD_CTOR +#undef TOML_NVCC #undef TOML_OPEN_ENUM #undef TOML_OPEN_FLAGS_ENUM #undef TOML_PARSER_TYPENAME diff --git a/include/util.hpp b/include/util.hpp index 77131fc..6d8cf79 100644 --- a/include/util.hpp +++ b/include/util.hpp @@ -1,11 +1,13 @@ #ifndef UTIL_HPP #define UTIL_HPP +#include #include #include #include #include #include +#include #include #include "config.hpp" @@ -13,51 +15,50 @@ #include "fmt/color.h" #include "fmt/format.h" #include "fmt/ranges.h" -#include - -using std::string; -using std::string_view; -using std::vector; -using std::unique_ptr; #ifdef ENABLE_NLS /* here so it doesn't need to be included elsewhere */ -#include +#include #include /* define _() as shortcut for gettext() */ #define _(str) gettext(str) #else -#define _(s) (char *)s +#define _(s) (char*)s #endif struct TaurPkg_t; class TaurBackend; -#define BOLD fmt::emphasis::bold -#define BOLD_TEXT(x) (fmt::emphasis::bold | fmt::fg(x)) -#define NOCOLOR "\033[0m" -#define AUR_URL "https://aur.archlinux.org" +#define BOLD fmt::emphasis::bold +#define BOLD_COLOR(x) (fmt::emphasis::bold | fmt::fg(x)) +#define NOCOLOR "\033[0m" +#define AUR_URL "https://aur.archlinux.org" #define AUR_URL_GIT(x) fmt::format("https://aur.archlinux.org/{}.git", x) #define AUR_URL_TAR(x) fmt::format("https://aur.archlinux.org/cgit/aur.git/snapshot/{}.tar.gz", x) -#define alpm_list_smart_pointer unique_ptr -#define make_list_smart_pointer(pointer) (unique_ptr(pointer, alpm_list_free)) +#define alpm_list_smart_pointer std::unique_ptr +#define make_list_smart_pointer(pointer) \ + (std::unique_ptr(pointer, alpm_list_free)) -#define alpm_list_smart_deleter unique_ptr -#define make_list_smart_deleter(pointer) (unique_ptr(pointer, free_list_and_internals)) +#define alpm_list_smart_deleter std::unique_ptr +#define make_list_smart_deleter(pointer) \ + (std::unique_ptr(pointer, free_list_and_internals)) -#define NOCONFIRMREQ(x) \ - if (config->noconfirm && !required) { \ - fmt::print("\n"); \ - return x; \ +#define NOCONFIRMREQ(x) \ + if (config->noconfirm && !required) \ + { \ + fmt::print("\n"); \ + return x; \ } -#define NOCONFIRM(x) \ - if (config->noconfirm) { \ - fmt::print("\n"); \ - return x; \ +#define NOCONFIRM(x) \ + if (config->noconfirm) \ + { \ + fmt::print("\n"); \ + return x; \ } -enum prompt_yn { +enum prompt_yn +{ PROMPT_YN_DIFF, PROMPT_YN_CONTINUE_WITHOUT_DIFF, PROMPT_YN_EDIT_PKGBUILD, @@ -66,91 +67,107 @@ enum prompt_yn { PROMPT_YN_PROCEED_TRANSACTION, PROMPT_YN_CLEANBUILD, }; -enum prompt_list { +enum prompt_list +{ PROMPT_LIST_CLEANBUILDS, PROMPT_LIST_REVIEWS, PROMPT_LIST_REMOVE_PKGS, }; -enum { +enum +{ YES = 1, NO = 0, }; -enum log_level { +enum log_level +{ ERROR, WARN, INFO, DEBUG, - NONE // display no prefix for this. + NONE // display no prefix for this. }; -bool hasEnding(string_view fullString, string_view ending); -bool hasStart(string_view fullString, string_view start); -string expandVar(string& str); -bool is_numerical(string_view s, bool allowSpace = false); -bool taur_read_exec(vector cmd, string& output, bool exitOnFailure = true); -void interruptHandler(int); -bool taur_exec(vector cmd, bool exitOnFailure = true); -void sanitizeStr(string& str); -bool is_package_from_syncdb(const char *name, alpm_list_t *syncdbs); -bool commitTransactionAndRelease(bool soft = false); -void printPkgInfo(TaurPkg_t& pkg, string_view db_name); -void printLocalFullPkgInfo(alpm_pkg_t *pkg); -string makepkg_list(string_view pkg_name, string path); -void free_list_and_internals(alpm_list_t *list); -fmt::text_style getColorFromDBName(string_view db_name); -vector filterAURPkgs(vector pkgs, alpm_list_t *syncdbs, bool inverse); -vector filterAURPkgsNames(vector pkgs, alpm_list_t *syncdbs, bool inverse); -string shell_exec(string_view cmd); -vector split(string_view text, char delim); -fmt::rgb hexStringToColor(string_view hexstr); -void ctrl_d_handler(); -string getTitleFromVotes(float votes); -string getHomeCacheDir(); -string getHomeConfigDir(); -string getConfigDir(); -string getCacheDir(); -bool makepkg_exec(vector const& args, bool exitOnFailure = true); -bool pacman_exec(string_view op, vector const& args, bool exitOnFailure = true, bool root = true); -bool util_db_search(alpm_db_t *db, alpm_list_t *needles, alpm_list_t **ret); -std::optional> askUserForPkg(vector pkgs, TaurBackend& backend, bool useGit); -string_view binarySearch(const vector& arr, string_view target); -vector load_aur_list(); -bool update_aur_cache(bool recursiveCall = false); +bool hasEnding(std::string_view fullString, std::string_view ending); +bool hasStart(std::string_view fullString, std::string_view start); +std::string expandVar(std::string& str); +bool is_numerical(std::string_view s, bool allowSpace = false); +bool taur_read_exec(std::vector cmd, std::string& output, bool exitOnFailure = true); +void interruptHandler(int); +bool taur_exec(std::vector cmd, bool exitOnFailure = true); +void sanitizeStr(std::string& str); +bool is_package_from_syncdb(const char* name, alpm_list_t* syncdbs); +bool commitTransactionAndRelease(bool soft = false); +void printPkgInfo(TaurPkg_t& pkg, std::string_view db_name); +void printLocalFullPkgInfo(alpm_pkg_t* pkg); +std::string makepkg_list(std::string_view pkg_name, std::string_view path); +void getFileValue(u_short& iterIndex, const std::string& line, std::string& str, const size_t& amount); +void free_list_and_internals(alpm_list_t* list); +fmt::text_style getColorFromDBName(std::string_view db_name); +std::vector filterAURPkgs(std::vector pkgs, alpm_list_t* syncdbs, bool inverse); +std::vector filterAURPkgsNames(std::vector pkgs, alpm_list_t* syncdbs, + bool inverse); +std::string shell_exec(std::string_view cmd); +std::vector split(std::string_view text, char delim); +fmt::rgb hexStringToColor(std::string_view hexstr); +void ctrl_d_handler(const std::istream& cin); +std::string getTitleFromVotes(float votes); +std::string getHomeCacheDir(); +std::string getHomeConfigDir(); +std::string getConfigDir(); +std::string getCacheDir(); +bool makepkg_exec(std::vector const& args, bool exitOnFailure = true); +bool pacman_exec(std::string_view op, std::vector const& args, bool exitOnFailure = true, + bool root = true); +bool util_db_search(alpm_db_t* db, alpm_list_t* needles, alpm_list_t** ret); + +std::optional> askUserForPkg(std::vector pkgs, TaurBackend& backend, bool useGit); +std::string_view binarySearch(const std::vector& arr, std::string_view target); +std::vector load_aur_list(); +bool update_aur_cache(bool recursiveCall = false); template -struct is_fmt_convertible { -private: +struct is_fmt_convertible +{ + private: template static auto test(int) -> decltype(fmt::to_string(std::declval()), std::true_type{}); template static auto test(...) -> std::false_type; -public: + public: static constexpr bool value = decltype(test(0))::value; }; template constexpr bool is_fmt_convertible_v = is_fmt_convertible::value; +constexpr std::size_t operator""_len(const char*,std::size_t ln) noexcept +{ + return ln; +} + +// clang-format off template -void _log_println(log_level log, const fmt::text_style ts, fmt::runtime_format_string<> fmt, Args&&... args) { - switch (log) { +void _log_println(log_level log, const fmt::text_style ts, fmt::runtime_format_string<> fmt, Args&&... args) +{ + switch (log) + { case ERROR: - fmt::print(stderr, BOLD_TEXT(color.red), fmt::runtime(_("ERROR: "))); + fmt::print(stderr, BOLD_COLOR(color.red), fmt::runtime(_("ERROR: "))); break; case WARN: - fmt::print(BOLD_TEXT(color.yellow), fmt::runtime(_("Warning: "))); + fmt::print(BOLD_COLOR(color.yellow), fmt::runtime(_("Warning: "))); break; case INFO: - fmt::print(BOLD_TEXT(color.cyan), fmt::runtime(_("Info: "))); + fmt::print(BOLD_COLOR(color.cyan), fmt::runtime(_("Info: "))); break; case DEBUG: if (!config->debug) return; - fmt::print(BOLD_TEXT(color.magenta), "[DEBUG]: "); + fmt::print(BOLD_COLOR(color.magenta), "[DEBUG]: "); break; default: break; @@ -159,47 +176,42 @@ void _log_println(log_level log, const fmt::text_style ts, fmt::runtime_format_s } template -void log_println(log_level log, const char *fmt, Args&&... args) { - _log_println(log, fmt::text_style(), fmt::runtime(fmt), std::forward(args)...); -} - -template -void log_println(log_level log, string_view fmt, Args&&... args) { +void log_println(log_level log, std::string_view fmt, Args&&... args) +{ _log_println(log, fmt::text_style(), fmt::runtime(fmt), std::forward(args)...); } template -void log_println(log_level log, const fmt::text_style ts, string_view fmt, Args&&... args) { +void log_println(log_level log, const fmt::text_style ts, std::string_view fmt, Args&&... args) +{ _log_println(log, ts, fmt::runtime(fmt), std::forward(args)...); } template -void log_println(log_level log, const fmt::text_style ts, const char *fmt, Args&&... args) { - _log_println(log, ts, fmt::runtime(fmt), std::forward(args)...); -} - -template -void die(const char *fmt, Args&&... args) { +void die(const std::string_view fmt, Args&&... args) +{ _log_println(ERROR, fmt::text_style(), fmt::runtime(fmt), std::forward(args)...); std::exit(1); } template -void _log_printf(log_level log, const fmt::text_style ts, fmt::runtime_format_string<> fmt, Args&&... args) { - switch (log) { +void _log_printf(log_level log, const fmt::text_style ts, fmt::runtime_format_string<> fmt, Args&&... args) +{ + switch (log) + { case ERROR: - fmt::print(stderr, BOLD_TEXT(color.red), fmt::runtime(_("ERROR: "))); + fmt::print(stderr, BOLD_COLOR(color.red), fmt::runtime(_("ERROR: "))); break; case WARN: - fmt::print(BOLD_TEXT(color.yellow), fmt::runtime(_("Warning: "))); + fmt::print(BOLD_COLOR(color.yellow), fmt::runtime(_("Warning: "))); break; case INFO: - fmt::print(BOLD_TEXT(color.cyan), fmt::runtime(_("Info: "))); + fmt::print(BOLD_COLOR(color.cyan), fmt::runtime(_("Info: "))); break; case DEBUG: if (!config->debug) return; - fmt::print(BOLD_TEXT(color.magenta), "[DEBUG]: "); + fmt::print(BOLD_COLOR(color.magenta), "[DEBUG]: "); break; default: break; @@ -208,76 +220,78 @@ void _log_printf(log_level log, const fmt::text_style ts, fmt::runtime_format_st } template -void log_printf(log_level log, const char *fmt, Args&&... args) { +void log_printf(log_level log, std::string_view fmt, Args&&... args) +{ _log_printf(log, fmt::text_style(), fmt::runtime(fmt), std::forward(args)...); } template -void log_printf(log_level log, string_view fmt, Args&&... args) { - _log_printf(log, fmt::text_style(), fmt::runtime(fmt), std::forward(args)...); -} - -template -void log_printf(log_level log, const fmt::text_style ts, string_view fmt, Args&&... args) { - _log_printf(log, ts, fmt::runtime(fmt), std::forward(args)...); -} - -template -void log_printf(log_level log, const fmt::text_style ts, const char *fmt, Args&&... args) { +void log_printf(log_level log, const fmt::text_style ts, std::string_view fmt, Args&&... args) +{ _log_printf(log, ts, fmt::runtime(fmt), std::forward(args)...); } +// clang-format on /** Ask the user a yes or no question. * @param def the default result * @param pr the prompt enum * @param args arguments, required with some prompts, like package names etc. * @returns the result, y = true, f = false, only returns def if the result is def -*/ + */ template -bool askUserYorN(bool def, prompt_yn pr, Args&&... args) { - string inputs_str = "[" + (string)(def ? "Y" : "y") + "/" + (string)(!def ? "N" : "n") + "] "; - string result; +bool askUserYorN(bool def, prompt_yn pr, Args&&... args) +{ + std::string inputs_str = "[" + (std::string)(def ? "Y" : "y") + "/" + (std::string)(!def ? "N" : "n") + "] "; + std::string result; - switch (pr) { + switch (pr) + { case PROMPT_YN_DIFF: log_printf(INFO, BOLD, _("View the diffs for {}? {}"), std::forward(args)..., inputs_str); NOCONFIRM(NO); break; + case PROMPT_YN_CONTINUE_WITHOUT_DIFF: - log_printf(WARN, BOLD, _("With your current settings, viewing PKGBUILD diffs is unsupported (maybe useGit is false?), continue with the installation anyway? {}"), + log_printf(WARN, BOLD, + _("With your current settings, viewing PKGBUILD diffs is unsupported (maybe useGit is false?), " + "continue with the installation anyway? {}"), inputs_str); NOCONFIRM(YES); break; + case PROMPT_YN_EDIT_PKGBUILD: log_printf(INFO, BOLD, _("Review PKGBUILD for {}? {}"), std::forward(args)..., inputs_str); NOCONFIRM(NO); break; + case PROMPT_YN_PROCEED_INSTALL: log_printf(INFO, BOLD, _("Proceed with the installation? {}"), inputs_str); NOCONFIRM(YES); break; + case PROMPT_YN_PROCEED_UPGRADE: log_printf(INFO, BOLD, _("Would you like to upgrade the above packages? {}"), inputs_str); NOCONFIRM(YES); break; + case PROMPT_YN_PROCEED_TRANSACTION: log_printf(INFO, BOLD, _("Would you like to proceed with this transaction? {}"), inputs_str); NOCONFIRM(YES); break; + case PROMPT_YN_CLEANBUILD: log_printf(INFO, BOLD, "Would you like to cleanbuild {}? {}", std::forward(args)..., inputs_str); NOCONFIRM(NO); break; - default: - return def; + default: return def; } - //std::cin.sync(); - // while the getline function works, and the result is not 1 character long, keep reminding the user. + // std::cin.sync(); + // while the getline function works, and the result is not 1 character long, keep reminding the user. while (std::getline(std::cin, result) && (result.length() > 1)) log_printf(WARN, _("Please provide a valid response {}"), inputs_str); - ctrl_d_handler(); + ctrl_d_handler(std::cin); if (result.empty()) return def; @@ -293,42 +307,47 @@ bool askUserYorN(bool def, prompt_yn pr, Args&&... args) { * @param pr the prompt enum * @param required if this prompt can be skipped manually or by noconfirm. * @returns the resulting list, empty if anything bad happens. -*/ + */ template >> -vector askUserForList(vector& list, prompt_list pr, bool required = false) { - - string sep_str = _("Type the index of each package (eg: \"0 1 2\", \"0-2\", \"a\" for all, \"n\" or enter for none)"); - string result_str; +std::vector askUserForList(std::vector& list, prompt_list pr, bool required = false) +{ + std::string sep_str = + _("Type the index of each package (eg: \"0 1 2\", \"0-2\", \"a\" for all, \"n\" or enter for none)"); + std::string result_str; for (size_t i = 0; i < list.size(); i++) - fmt::println(fmt::fg(color.index), "[{}] {}", i, fmt::format(BOLD_TEXT(fmt::color::white), "{}", list[i])); + fmt::println(fmt::fg(color.index), "[{}] {}", i, fmt::format(BOLD_COLOR(fmt::color::white), "{}", list[i])); log_println(INFO, _("{}"), sep_str); - switch (pr) { + switch (pr) + { case PROMPT_LIST_CLEANBUILDS: log_printf(INFO, BOLD, _("Packages to completely rebuild: ")); NOCONFIRMREQ({}); break; + case PROMPT_LIST_REVIEWS: log_printf(INFO, BOLD, _("Packages you'd like to review: ")); NOCONFIRMREQ({}); break; + case PROMPT_LIST_REMOVE_PKGS: log_printf(INFO, BOLD, _("Packages you'd like to remove: ")); NOCONFIRMREQ({}); break; - default: - return {}; - } - //std::cin.sync(); - // while the getline function works, and the result is not 1 character long, keep reminding the user. - vector result; + default: return {}; + } - while (std::getline(std::cin, result_str)) { + // std::cin.sync(); + // while the getline function works, and the result is not 1 character long, keep reminding the user. + std::vector result; - if ((result_str.empty() && !required) || result_str == "n") { + while (std::getline(std::cin, result_str)) + { + if ((result_str.empty() && !required) || result_str == "n") + { std::cout << std::endl; return {}; } @@ -336,15 +355,18 @@ vector askUserForList(vector& list, prompt_list pr, bool required = false) if (result_str == "a") return list; - vector input_indices = split(result_str, ' '); + std::vector input_indices = split(result_str, ' '); - int added_elements = 0; + int added_elements = 0; bool breakandcontinue = false; - for (size_t i = 0; i < input_indices.size() && !breakandcontinue; i++) { + for (size_t i = 0; i < input_indices.size() && !breakandcontinue; i++) + { // 1-5 means 1 through 5 - if (input_indices[i].find('-') != string::npos) { - vector loop_bounds = split(input_indices[i], '-'); - if (loop_bounds.size() != 2 || !is_numerical(loop_bounds[0]) || !is_numerical(loop_bounds[1])) { + if (input_indices[i].find('-') != std::string::npos) + { + std::vector loop_bounds = split(input_indices[i], '-'); + if (loop_bounds.size() != 2 || !is_numerical(loop_bounds[0]) || !is_numerical(loop_bounds[1])) + { log_printf(WARN, _("Invalid loop range! (loop ranges look like \"0-5\"): ")); breakandcontinue = true; break; @@ -353,13 +375,16 @@ vector askUserForList(vector& list, prompt_list pr, bool required = false) int lowerbound = std::stoi(loop_bounds[0]); int higherbound = std::stoi(loop_bounds[1]); - if ((0 > lowerbound || lowerbound > list.size()) || (lowerbound > higherbound || higherbound >= list.size())) { + if ((0 > lowerbound || lowerbound > list.size()) || + (lowerbound > higherbound || higherbound >= list.size())) + { log_printf(WARN, _("Invalid loop range! (loop ranges must stay in bounds and in order): ")); breakandcontinue = true; break; } - for (int i = lowerbound; i <= higherbound; i++) { + for (int i = lowerbound; i <= higherbound; i++) + { result.push_back(list[i]); added_elements++; } @@ -367,13 +392,15 @@ vector askUserForList(vector& list, prompt_list pr, bool required = false) continue; } - if (!is_numerical(input_indices[i])) { + if (!is_numerical(input_indices[i])) + { log_printf(WARN, "{}: ", sep_str); continue; } int index = std::stoi(input_indices[i]); - if (0 > index || index >= list.size()) { + if (0 > index || index >= list.size()) + { log_println(WARN, _("Invalid index! Ignoring index #{}."), input_indices[i]); continue; } @@ -384,20 +411,22 @@ vector askUserForList(vector& list, prompt_list pr, bool required = false) if (breakandcontinue) continue; - if (added_elements == 0) { + if (added_elements == 0) + { log_printf(WARN, "{}: ", sep_str); continue; } break; } - ctrl_d_handler(); + ctrl_d_handler(std::cin); return result; } template -T sanitize(T beg, T end) { +T sanitize(T beg, T end) +{ T dest = beg; for (T itr = beg; itr != end; ++itr) // if its: @@ -409,9 +438,9 @@ T sanitize(T beg, T end) { return dest; } -static inline std::vector secret = { - {"Ingredients:"}, - {R"#( +inline constexpr std::array secret = { + "Ingredients:", + { R"#( 3/4 cup milk 1/2 cup vegetable oil 1 large egg @@ -421,27 +450,28 @@ static inline std::vector secret = { 1/2 teaspoon salt 3/4 cup mini semi-sweet chocolate chips 1 and 1/2 tablespoons white sugar - 1 tablespoon brown sugar)#"}, - {R"#( + 1 tablespoon brown sugar)#" }, + { R"#( Step 1: Preheat the oven to 400 degrees F (200 degrees C). - Grease a 12-cup muffin tin or line cups with paper liners.)#"}, - {R"#( + Grease a 12-cup muffin tin or line cups with paper liners.)#" }, + { R"#( Step 2: Combine milk, oil, and egg in a small bowl until well blended. Combine flour, 1/2 cup sugar, baking powder, and salt together in a large bowl, making a well in the center. - Pour milk mixture into well and stir until batter is just combined; fold in chocolate chips.)#"}, - {R"#( + Pour milk mixture into well and stir until batter is just combined; fold in chocolate chips.)#" }, + { R"#( Step 3: Spoon batter into the prepared muffin cups, filling each 2/3 full. - Combine 1 and 1/2 tablespoons white sugar and 1 tablespoon brown sugar in a small bowl; sprinkle on tops of muffins.)#"}, - {R"#( + Combine 1 and 1/2 tablespoons white sugar and 1 tablespoon brown sugar in a small bowl; sprinkle on tops of muffins.)#" }, + { R"#( Step 4: Bake in the preheated oven until tops spring back when lightly pressed, about 18 to 20 minutes. Cool in the tin briefly, then transfer to a wire rack. Serve warm or cool completely. - )#"}, - {"Credits to: https://www.allrecipes.com/recipe/7906/chocolate-chip-muffins/"}}; + )#" }, + { "Credits to: https://www.allrecipes.com/recipe/7906/chocolate-chip-muffins/" } +}; #endif diff --git a/src/args.cpp b/src/args.cpp index a6af628..56d20e2 100644 --- a/src/args.cpp +++ b/src/args.cpp @@ -20,14 +20,18 @@ // Why re-invent the wheel when there is already one working well? // All credits goes to the pacman devs +// clang-format off #include "args.hpp" -#include "util.hpp" -#include "config.hpp" + #include +#include "config.hpp" +#include "util.hpp" + alpm_list_smart_deleter taur_targets(nullptr, free_list_and_internals); -void invalid_opt(int used, string_view opt1, string_view opt2) { +void invalid_opt(int used, std::string_view opt1, std::string_view opt2) +{ if (used) log_println(ERROR, _("invalid option: '{}' and '{}' may not be used together"), opt1, opt2); } @@ -37,8 +41,10 @@ void invalid_opt(int used, string_view opt1, string_view opt2) { * @param dryrun If nonzero, application state is NOT changed * @return 0 if opt was handled, 1 if it was not handled */ -int parsearg_op(int opt, int dryrun) { - switch (opt) { +int parsearg_op(int opt, int dryrun) +{ + switch (opt) + { /* operations */ case 'D': if(dryrun) break; @@ -87,90 +93,114 @@ int parsearg_op(int opt, int dryrun) { * @param opt Keycode returned by getopt_long * @return 0 on success, 1 on unkown option, 2 on invalid argument */ -int parsearg_global(int opt) { - switch (opt) { +int parsearg_global(int opt) +{ + switch (opt) + { case OP_CACHEDIR: config->cacheDir = strndup(optarg, PATH_MAX); break; + case OP_COLORS: fmt::disable_colors = !((bool)std::atoi(optarg)); config->colors = (bool)std::atoi(optarg); break; + case OP_DEBUG: config->debug = true; break; + case OP_AURONLY: case 'a': config->aurOnly = true; break; + case OP_SUDO: config->sudo = strndup(optarg, PATH_MAX); break; + case OP_NOCONFIRM: config->noconfirm = true; break; + case OP_USEGIT: case 'g': config->useGit = true; break; + case OP_CONFIG: case OP_THEME: break; + default: return 1; } return 0; } -int parsearg_query(int opt) { - switch(opt) { +int parsearg_query(int opt) +{ + switch(opt) + { case OP_QUIET: case 'q': config->quiet = true; break; + case OP_SEARCH: case 's': op.op_q_search = 1; break; + case OP_INFO: case 'i': op.op_q_info = 1; break; + default: return 1; } return 0; } -int parsearg_sync(int opt) { - switch (opt) { +int parsearg_sync(int opt) +{ + switch (opt) + { case OP_SYSUPGRADE: case 'u': op.op_s_upgrade = 1; break; + case OP_REFRESH: case 'y': op.op_s_sync = 1; break; + case OP_SEARCH: case 's': op.op_s_search = 1; break; + case OP_CLEANBUILD: op.op_s_cleanbuild = 1; break; + default: return 1; } return 0; } -int parsearg_remove(int opt) { - switch (opt) { +int parsearg_remove(int opt) +{ + switch (opt) + { case OP_NOSAVE: case 'n': config->flags |= ALPM_TRANS_FLAG_NOSAVE; break; + case OP_RECURSIVE: case 's': if (config->flags & ALPM_TRANS_FLAG_RECURSE) @@ -178,8 +208,8 @@ int parsearg_remove(int opt) { else config->flags |= ALPM_TRANS_FLAG_RECURSE; break; - default: - return 1; + + default: return 1; } return 0; } diff --git a/src/config.cpp b/src/config.cpp index ae0f8ab..3b3ccf3 100644 --- a/src/config.cpp +++ b/src/config.cpp @@ -1,15 +1,16 @@ #define TOML_HEADER_ONLY 0 #include "config.hpp" -#include "ini.h" -#include "util.hpp" + #include #include -using std::ofstream; -using std::ifstream; +#include "ini.h" +#include "util.hpp" -Config::~Config() { - if (this->handle) { +Config::~Config() +{ + if (this->handle) + { alpm_trans_release(this->handle); alpm_release(this->handle); } @@ -19,25 +20,29 @@ Config::~Config() { } // initialize Config, can only be ran once for each Config instance. -Config::Config(string_view configFile, string_view themeFile, string_view configDir) { - bool newUser = false; +Config::Config(std::string_view configFile, std::string_view themeFile, std::string_view configDir) +{ + bool newUser = false; - if (!std::filesystem::exists(configDir)) { + if (!std::filesystem::exists(configDir)) + { log_println(WARN, _("TabAUR config folder was not found, Creating folders at {}!"), configDir); std::filesystem::create_directories(configDir); newUser = true; } - if (!std::filesystem::exists(configFile)) { + if (!std::filesystem::exists(configFile)) + { log_println(WARN, _("{} not found, generating new one"), configFile); // https://github.com/hyprwm/Hyprland/blob/main/src/config/ConfigManager.cpp#L681 - ofstream f(configFile.data(), std::ios::trunc); + std::ofstream f(configFile.data(), std::ios::trunc); f << AUTOCONFIG; f.close(); } - if (!std::filesystem::exists(themeFile)) { + if (!std::filesystem::exists(themeFile)) + { log_println(WARN, _("{} not found, generating new one"), themeFile); - ofstream f(themeFile.data(), std::ios::trunc); + std::ofstream f(themeFile.data(), std::ios::trunc); f << AUTOTHEME; f.close(); } @@ -45,32 +50,37 @@ Config::Config(string_view configFile, string_view themeFile, string_view config this->loadConfigFile(configFile); this->loadThemeFile(themeFile); - if (!std::filesystem::exists(this->cacheDir)) { + if (!std::filesystem::exists(this->cacheDir)) + { log_println(WARN, _("TabAUR cache folder was not found, Creating folders at {}!"), this->cacheDir.string()); std::filesystem::create_directories(this->cacheDir); } if (newUser) // ye i'm sorry if it's too wide - log_println(NONE, fg(color.blue), - _("I see you're a new user, Welcome!\n" - "Even though the AUR is very convenient, it could contain packages that are unmoderated and could be unsafe.\n" - "You should always read the sources, popularity, and votes to judge by yourself whether the package is trustable.\n" - "This project is in no way liable for any damage done to your system as a result of AUR packages.\n" - "Thank you!\n")); + log_println( + NONE, fg(color.blue), + _("I see you're a new user, Welcome!\n" + "Even though the AUR is very convenient, it could contain packages that are unmoderated and could be " + "unsafe.\n" + "You should always read the sources, popularity, and votes to judge by yourself whether the package is " + "trustable.\n" + "This project is in no way liable for any damage done to your system as a result of AUR packages.\n" + "Thank you!\n")); } /* -* initialize all the "config.toml" variables -* and sanitize them (never trust user's input) -*/ -void Config::initVars() { - this->cacheDir = path(this->getConfigValue("general.cacheDir", string(getHomeCacheDir()) + "/TabAUR")); - this->pmConfig = this->getConfigValue("pacman.ConfigFile", "/etc/pacman.conf"); - this->makepkgConf = this->getConfigValue("pacman.MakepkgConf", "/etc/makepkg.conf"); - this->makepkgBin = this->getConfigValue("bins.makepkg", "makepkg"); - this->git = this->getConfigValue("bins.git", "git"); - this->sudo = this->getConfigValue("general.sudo", "sudo"); + * initialize all the "config.toml" variables + * and sanitize them (never trust user's input) + */ +void Config::initVars() +{ + this->cacheDir = path(this->getConfigValue("general.cacheDir", std::string(getHomeCacheDir()) + "/TabAUR")); + this->pmConfig = this->getConfigValue("pacman.ConfigFile", "/etc/pacman.conf"); + this->makepkgConf = this->getConfigValue("pacman.MakepkgConf", "/etc/makepkg.conf"); + this->makepkgBin = this->getConfigValue("bins.makepkg", "makepkg"); + this->git = this->getConfigValue("bins.git", "git"); + this->sudo = this->getConfigValue("general.sudo", "sudo"); this->useGit = this->getConfigValue("general.useGit", true); this->aurOnly = this->getConfigValue("general.aurOnly", false); this->debug = this->getConfigValue("general.debug", true); @@ -82,14 +92,16 @@ void Config::initVars() { sanitizeStr(this->makepkgBin); sanitizeStr(this->makepkgConf); sanitizeStr(this->git); - - for (auto& str : split(this->getConfigValue("general.editor", "nano"), ' ')) { + + for (auto& str : split(this->getConfigValue("general.editor", "nano"), ' ')) + { sanitizeStr(str); this->editor.push_back(str); } - char *no_color = getenv("NO_COLOR"); - if (no_color != NULL && no_color[0] != '\0') { + char* no_color = getenv("NO_COLOR"); + if (no_color != NULL && no_color[0] != '\0') + { fmt::disable_colors = true; this->colors = false; } @@ -99,11 +111,15 @@ void Config::initVars() { * and initialize libalpm * using the variables under the [pacman] table in "config.toml" * @param the directory of the config file -*/ -void Config::loadConfigFile(string_view filename) { - try { + */ +void Config::loadConfigFile(std::string_view filename) +{ + try + { this->tbl = toml::parse_file(filename); - } catch (const toml::parse_error& err) { + } + catch (const toml::parse_error& err) + { log_println(ERROR, _("Parsing config file {} failed:"), filename); std::cerr << err << std::endl; exit(-1); @@ -111,7 +127,8 @@ void Config::loadConfigFile(string_view filename) { this->initVars(); alpm_errno_t err; - this->handle = alpm_initialize(this->getConfigValue("pacman.RootDir", "/").c_str(), this->getConfigValue("pacman.DBPath", "/var/lib/pacman").c_str(), &err); + this->handle = alpm_initialize(this->getConfigValue("pacman.RootDir", "/").c_str(), + this->getConfigValue("pacman.DBPath", "/var/lib/pacman").c_str(), &err); if (!(this->handle)) die(_("Failed to get an alpm handle! Error: {}"), alpm_strerror(err)); @@ -122,10 +139,14 @@ void Config::loadConfigFile(string_view filename) { /** parse the theme file (aka "theme.toml") * @param filename The directory of the theme file */ -void Config::loadThemeFile(string_view filename) { - try { +void Config::loadThemeFile(std::string_view filename) +{ + try + { this->theme_tbl = toml::parse_file(filename); - } catch (const toml::parse_error& err) { + } + catch (const toml::parse_error& err) + { log_println(ERROR, _("Parsing theme file {} failed:"), filename); std::cerr << err << std::endl; exit(-1); @@ -138,10 +159,11 @@ void Config::loadThemeFile(string_view filename) { * Which can be used for colorize the text (useful for functions like log_println()) * @param value The value we want * @param fallback The default value if it doesn't exists - * @return fmt::rgb type variable + * @return fmt::rgb type variable */ -fmt::rgb Config::getThemeValue(const string& value, const string& fallback) { - return hexStringToColor(this->theme_tbl["theme"][value].value().value_or(fallback)); +fmt::rgb Config::getThemeValue(const std::string& value, const std::string& fallback) +{ + return hexStringToColor(this->theme_tbl["theme"][value].value().value_or(fallback)); } /** Get the theme color variable and return its hexcode @@ -149,11 +171,14 @@ fmt::rgb Config::getThemeValue(const string& value, const string& fallback) { * @param fallback The default value if it doesn't exists * @return color hexcode value */ -string Config::getThemeHexValue(const string& value, const string& fallback) { - return this->theme_tbl["theme"][value].value().value_or(fallback); +std::string Config::getThemeHexValue(const std::string& value, const std::string& fallback) +{ + return this->theme_tbl["theme"][value].value().value_or(fallback); } -void Config::initColors() { +// clang-format off +void Config::initColors() +{ color.red = this->getThemeValue("red", "#ff2000"); color.green = this->getThemeValue("green", "#00ff00"); color.blue = this->getThemeValue("blue", "#00aaff"); @@ -176,25 +201,30 @@ void Config::initColors() { color.index = this->getThemeValue("index", this->getThemeHexValue("magenta", "#ff11cc")); } -bool addServers(alpm_db_t *db, const string& includeFilename, string_view repoName) { - ifstream includeFile(includeFilename); +// clang-format on +bool addServers(alpm_db_t* db, const std::string& includeFilename, std::string_view repoName) +{ + std::ifstream includeFile(includeFilename); if (!includeFile.is_open()) return false; - string line; + std::string line; - while (std::getline(includeFile, line)) { + while (std::getline(includeFile, line)) + { if (hasStart(line, "#") || line.length() < 20) continue; size_t repo_pos = line.find("$repo"); - if (repo_pos != string::npos) - line.replace(repo_pos, 5, repoName); + if (repo_pos != std::string::npos) + line.replace(repo_pos, "$repo"_len, repoName); + size_t arch_pos = line.find("$arch"); - if (arch_pos != string::npos) - line.replace(arch_pos, 5, "x86_64"); + if (arch_pos != std::string::npos) + line.replace(arch_pos, "$arch"_len, "x86_64"); + // Each line that has a server starts with a 9 letter header line = line.substr(9); alpm_db_add_server(db, line.c_str()); @@ -203,18 +233,20 @@ bool addServers(alpm_db_t *db, const string& includeFilename, string_view repoNa return true; } -void Config::loadPacmanConfigFile(string filename) { +void Config::loadPacmanConfigFile(std::string filename) +{ mINI::INIFile file(filename); mINI::INIStructure ini; file.read(ini); - for (auto const& it : ini) { - string_view section = it.first; + for (auto const& it : ini) + { + std::string_view section = it.first; if (section == "options") continue; - alpm_db_t *db = alpm_register_syncdb(this->handle, section.data(), ALPM_SIG_USE_DEFAULT); + alpm_db_t* db = alpm_register_syncdb(this->handle, section.data(), ALPM_SIG_USE_DEFAULT); if (db == NULL) continue; diff --git a/src/fmt/format.cc b/src/fmt/format.cc index 391d3a2..70d12d6 100644 --- a/src/fmt/format.cc +++ b/src/fmt/format.cc @@ -26,6 +26,7 @@ template FMT_API auto thousands_sep_impl(locale_ref) -> thousands_sep_result; template FMT_API auto decimal_point_impl(locale_ref) -> char; +// DEPRECATED! template FMT_API void buffer::append(const char*, const char*); template FMT_API void vformat_to(buffer&, string_view, diff --git a/src/fmt/os.cc b/src/fmt/os.cc index 81177b2..2736649 100644 --- a/src/fmt/os.cc +++ b/src/fmt/os.cc @@ -12,47 +12,51 @@ #include "fmt/os.h" -#include +#ifndef FMT_MODULE +# include -#if FMT_USE_FCNTL -# include -# include - -# ifdef _WRS_KERNEL // VxWorks7 kernel -# include // getpagesize -# endif +# if FMT_USE_FCNTL +# include +# include -# ifndef _WIN32 -# include -# else -# ifndef WIN32_LEAN_AND_MEAN -# define WIN32_LEAN_AND_MEAN +# ifdef _WRS_KERNEL // VxWorks7 kernel +# include // getpagesize # endif -# include -# ifndef S_IRUSR -# define S_IRUSR _S_IREAD -# endif -# ifndef S_IWUSR -# define S_IWUSR _S_IWRITE -# endif -# ifndef S_IRGRP -# define S_IRGRP 0 -# endif -# ifndef S_IWGRP -# define S_IWGRP 0 -# endif -# ifndef S_IROTH -# define S_IROTH 0 -# endif -# ifndef S_IWOTH -# define S_IWOTH 0 -# endif -# endif // _WIN32 -#endif // FMT_USE_FCNTL +# ifndef _WIN32 +# include +# else +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# endif // _WIN32 +# endif // FMT_USE_FCNTL + +# ifdef _WIN32 +# include +# endif +#endif #ifdef _WIN32 -# include +# ifndef S_IRUSR +# define S_IRUSR _S_IREAD +# endif +# ifndef S_IWUSR +# define S_IWUSR _S_IWRITE +# endif +# ifndef S_IRGRP +# define S_IRGRP 0 +# endif +# ifndef S_IWGRP +# define S_IWGRP 0 +# endif +# ifndef S_IROTH +# define S_IROTH 0 +# endif +# ifndef S_IWOTH +# define S_IWOTH 0 +# endif #endif namespace { diff --git a/src/main.cpp b/src/main.cpp index 1569260..cafd047 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,13 +18,15 @@ */ #include + #include #pragma GCC diagnostic ignored "-Wvla" +#include + #include "args.hpp" #include "taur.hpp" #include "util.hpp" -#include using namespace std::string_view_literals; @@ -32,8 +34,10 @@ std::unique_ptr config; std::unique_ptr backend; // this may be hard to read, but better than calling fmt::println multiple times -void usage(int op) { - if (op == OP_MAIN) { +void usage(int op) +{ + if (op == OP_MAIN) + { fmt::println("TabAUR usage: taur [...]"sv); fmt::print("options:\n{}", R"( taur {-h --help} @@ -47,15 +51,20 @@ void usage(int op) { taur {-T --deptest} [options] [package(s)] taur {-U --upgrade} [options] )"sv); - } else { - if (op == OP_SYNC) { + } + else + { + if (op == OP_SYNC) + { fmt::println("usage: taur {{-S --sync}} [options] [package(s)]"); fmt::println("options:{}", R"( -s, --search search remote repositories for matching strings -u, --sysupgrade upgrade installed packages (-uu enables downgrades) -y, --refresh download fresh package databases from the server )"sv); - } else if (op == OP_QUERY) { + } + else if (op == OP_QUERY) + { fmt::println("usage: taur {{-Q --query}} [options] [package(s)]"); fmt::print("options:{}", R"( -q, --quiet show less information for query and search @@ -76,9 +85,10 @@ void usage(int op) { )"sv); } -void test_colors() { +void test_colors() +{ std::time_t current_time = std::time(nullptr); - string timestr = std::ctime(¤t_time); + std::string timestr = std::ctime(¤t_time); timestr.erase(timestr.length() - 1); TaurPkg_t pkg = { @@ -87,7 +97,7 @@ void test_colors() { .desc = "A customizable and lightweight AUR helper, designed to be simple but powerful.", .maintainer = "\1", .outofdate = current_time, - .popularity = 'R', // if you know, you know :P + .popularity = 'R', // if you know, you know :P .votes = 34, .installed = true, .db_name = "aur", @@ -95,10 +105,10 @@ void test_colors() { if (fmt::disable_colors) fmt::println("Colors are disabled"); - log_println(DEBUG, _("Debug color: {}"), fmt::format(BOLD_TEXT(color.magenta), "(bold) magenta")); - log_println(INFO, _("Info color: {}"), fmt::format(BOLD_TEXT(color.cyan), "(bold) cyan")); - log_println(WARN, _("Warning color: {}"), fmt::format(BOLD_TEXT(color.yellow), "(bold) yellow")); - log_println(ERROR, _("Error color: {}"), fmt::format(BOLD_TEXT(color.red), "(bold) red")); + log_println(DEBUG, _("Debug color: {}"), fmt::format(BOLD_COLOR(color.magenta), "(bold) magenta")); + log_println(INFO, _("Info color: {}"), fmt::format(BOLD_COLOR(color.cyan), "(bold) cyan")); + log_println(WARN, _("Warning color: {}"), fmt::format(BOLD_COLOR(color.yellow), "(bold) yellow")); + log_println(ERROR, _("Error color: {}"), fmt::format(BOLD_COLOR(color.red), "(bold) red")); fmt::println(fg(color.red), "red"); fmt::println(fg(color.blue), "blue"); fmt::println(fg(color.yellow), "yellow"); @@ -114,65 +124,72 @@ void test_colors() { fmt::println(getColorFromDBName("multilib"), "(bold) multilib"); fmt::println(getColorFromDBName("others"), "(bold) others"); - fmt::println(BOLD_TEXT(color.version), "\n(bold) version " VERSION); + fmt::println(BOLD_COLOR(color.version), "\n(bold) version " VERSION); fmt::println(fg(color.popularity), "Popularity: {}", pkg.popularity); fmt::println(fg(color.votes), "Votes: {} ({})", pkg.votes, getTitleFromVotes(pkg.votes)); fmt::println(fg(color.index), "index [1]"); - fmt::println(BOLD_TEXT(color.installed), "(bold) indicator [Installed]"); - fmt::println(BOLD_TEXT(color.orphan), "(bold) (un-maintained)"); - fmt::println(BOLD_TEXT(color.outofdate), "(bold) (Outdated: {})", timestr); + fmt::println(BOLD_COLOR(color.installed), "(bold) indicator [Installed]"); + fmt::println(BOLD_COLOR(color.orphan), "(bold) (un-maintained)"); + fmt::println(BOLD_COLOR(color.outofdate), "(bold) (Outdated: {})", timestr); fmt::println("\nexamples package search preview:"); printPkgInfo(pkg, pkg.db_name); } -void execPacman(int argc, char *argv[]) { +void execPacman(int argc, char* argv[]) +{ if (argc > (_POSIX_ARG_MAX - 1)) die(_("argc is invalid! (argc > {})"), _POSIX_ARG_MAX - 1); - vector args; + std::vector args; args.push_back("pacman"); for (int i = 1; i < argc; ++i) args.push_back(argv[i]); args.push_back(nullptr); - if (execvp(args[0], const_cast(args.data())) == -1) + if (execvp(args[0], const_cast(args.data())) == -1) perror("execvp"); - + exit(1); } -int installPkg(alpm_list_t *pkgNames) { - if (!pkgNames) +int installPkg(alpm_list_t* pkgNames) +{ + if (!pkgNames && !op.op_s_upgrade) return false; - bool useGit = config->useGit; - path cacheDir = config->cacheDir; + bool useGit = config->useGit; + path cacheDir = config->cacheDir; - bool returnStatus = true; - bool stat; + bool returnStatus = true; + bool stat; - vector pacmanPkgs; // list of pacman packages to install, to avoid spamming pacman. - vector pkgNamesVec; - vector pkgsToCleanBuild, pkgsToReview; + std::vector pacmanPkgs; // list of pacman packages to install, to avoid spamming pacman. + std::vector pkgNamesVec; + std::vector pkgsToCleanBuild, pkgsToReview; // move them into a vector for askUserForList for (; pkgNames; pkgNames = pkgNames->next) - pkgNamesVec.push_back((const char *)(pkgNames->data)); + pkgNamesVec.push_back(reinterpret_cast(pkgNames->data)); - if (pkgNamesVec.empty()) { - if (op.op_s_search) { + if (pkgNamesVec.empty()) + { + if (op.op_s_search) + { log_println(WARN, _("Please specify a target")); return false; } } - - if (op.op_s_search) { - for (size_t i = 0; i < pkgNamesVec.size(); i++) { - vector pkgs = backend->search(pkgNamesVec[i], useGit, config->aurOnly, false); - if (pkgs.empty()) { + if (op.op_s_search) + { + for (size_t i = 0; i < pkgNamesVec.size(); i++) + { + std::vector pkgs = backend->search(pkgNamesVec[i], useGit, config->aurOnly, false); + + if (pkgs.empty()) + { log_println(WARN, _("No results found for {}!"), pkgNamesVec[i]); returnStatus = false; continue; @@ -187,55 +204,60 @@ int installPkg(alpm_list_t *pkgNames) { } if (!update_aur_cache()) - log_println(ERROR, _("Failed to get information about {}"), (config->cacheDir / "packages.aur").string()); + log_println(ERROR, _("Failed to get information about {}"), (config->cacheDir / "packages.aur").string()); // I swear there was a comment here.. - vector AURPkgs = filterAURPkgsNames(pkgNamesVec, alpm_get_syncdbs(config->handle), true); + std::vector AURPkgs = filterAURPkgsNames(pkgNamesVec, alpm_get_syncdbs(config->handle), true); - for (const auto& pkg : pkgNamesVec) + for (const auto& pkg : pkgNamesVec) { if (std::find(AURPkgs.begin(), AURPkgs.end(), pkg) == AURPkgs.end()) pacmanPkgs.push_back(pkg.data()); } if (!op.op_s_cleanbuild && !AURPkgs.empty()) - pkgsToCleanBuild = askUserForList(AURPkgs, PROMPT_LIST_CLEANBUILDS); + pkgsToCleanBuild = askUserForList(AURPkgs, PROMPT_LIST_CLEANBUILDS); if (!config->noconfirm && !AURPkgs.empty()) - pkgsToReview = askUserForList(AURPkgs, PROMPT_LIST_REVIEWS); - - for (auto& pkg_name : AURPkgs) { + pkgsToReview = askUserForList(AURPkgs, PROMPT_LIST_REVIEWS); + for (auto& pkg_name : AURPkgs) + { path pkgDir = path(cacheDir) / pkg_name; - stat = useGit ? backend->download_git(AUR_URL_GIT(pkg_name), pkgDir) : backend->download_tar(AUR_URL_TAR(pkg_name), pkgDir); - if (!stat) { + stat = useGit ? backend->download_git(AUR_URL_GIT(pkg_name), pkgDir) + : backend->download_tar(AUR_URL_TAR(pkg_name), pkgDir); + if (!stat) + { log_println(ERROR, _("Failed to download {}"), pkg_name); returnStatus = false; continue; } - } - for (string_view& pkg : pkgsToCleanBuild) { + for (std::string_view& pkg : pkgsToCleanBuild) + { path pkgDir = path(cacheDir) / pkg; - if (!useGit) { + if (!useGit) + { log_println(INFO, _("Removing {}"), pkgDir.c_str()); std::filesystem::remove_all(pkgDir); - } - else { + } + else + { log_println(INFO, _("Cleaning {}"), pkgDir.c_str()); - taur_exec({config->git, "-C", pkgDir.c_str(), "clean", "-xffd"}); + taur_exec({ config->git, "-C", pkgDir.c_str(), "clean", "-xffd" }); } } // cmd is just a workaround for making it possible // to pass flags to the editor, e.g nano --modernbindings // instead of creating another config variable - for (string_view& pkg : pkgsToReview) { + for (std::string_view& pkg : pkgsToReview) + { path pkgDir = path(cacheDir) / pkg; - - vector cmd; + + std::vector cmd; for (auto& str : config->editor) cmd.push_back(str); cmd.push_back((pkgDir / "PKGBUILD").string()); @@ -246,13 +268,14 @@ int installPkg(alpm_list_t *pkgNames) { if (!pkgsToReview.empty() && !askUserYorN(YES, PROMPT_YN_PROCEED_INSTALL)) return false; - if (!config->aurOnly && (op.op_s_upgrade || !pacmanPkgs.empty())) { + if (!config->aurOnly && (op.op_s_upgrade || !pacmanPkgs.empty())) + { if (op.op_s_upgrade) log_println(INFO, _("Upgrading system packages!")); else log_println(INFO, _("Installing system packages!")); - string op_s = "-S"; + std::string op_s = "-S"; if (op.op_s_sync) op_s += 'y'; @@ -263,39 +286,45 @@ int installPkg(alpm_list_t *pkgNames) { pacman_exec(op_s, pacmanPkgs); } - if (op.op_s_upgrade) { + if (op.op_s_upgrade) + { log_println(INFO, _("Upgrading AUR packages!")); backend->update_all_aur_pkgs(cacheDir, useGit); - pkgs_to_install = ""; // Reset the list. + pkgs_to_install = ""; // Reset the list. } - for (size_t i = 0; i < AURPkgs.size(); i++) { - vector pkgs = backend->search(AURPkgs[i], useGit, config->aurOnly, true); + for (size_t i = 0; i < AURPkgs.size(); i++) + { + std::vector pkgs = backend->search(AURPkgs[i], useGit, config->aurOnly, true); - optional> oSelectedPkgs = askUserForPkg(pkgs, *backend, useGit); + std::optional> oSelectedPkgs = askUserForPkg(pkgs, *backend, useGit); - if (!oSelectedPkgs) { + if (!oSelectedPkgs) + { returnStatus = false; continue; } - vector selectedPkgs = oSelectedPkgs.value(); + std::vector selectedPkgs = oSelectedPkgs.value(); - for (TaurPkg_t &pkg : selectedPkgs) { + for (TaurPkg_t& pkg : selectedPkgs) + { path pkgDir = path(cacheDir) / pkg.name; stat = backend->handle_aur_depends(pkg, cacheDir, backend->get_all_local_pkgs(true), useGit); - if (!stat) { + if (!stat) + { log_println(ERROR, _("Installing AUR dependencies for your package has failed.")); returnStatus = false; continue; } - stat = backend->build_pkg(pkg.name, pkgDir, false); + stat = backend->build_pkg(pkg.name, pkgDir.string(), false); - if (!stat) { + if (!stat) + { log_println(ERROR, _("Building {} has failed."), pkg.name); returnStatus = false; pkgs_failed_to_build += pkg.name + ' '; @@ -307,41 +336,48 @@ int installPkg(alpm_list_t *pkgNames) { } } - if (!pkgs_to_install.empty()) { + if (!pkgs_to_install.empty()) + { log_println(DEBUG, _("Installing {}"), fmt::join(pkgNamesVec, " ")); pkgs_to_install.erase(pkgs_to_install.length() - 1); - if (!pacman_exec("-U", split(pkgs_to_install, ' '), false)) { + if (!pacman_exec("-U", split(pkgs_to_install, ' '), false)) + { log_println(ERROR, _("Failed to install {}"), fmt::join(pkgNamesVec, " ")); returnStatus = false; } } - - if (!pkgs_failed_to_build.empty()) { + + if (!pkgs_failed_to_build.empty()) + { pkgs_failed_to_build.erase(pkgs_failed_to_build.end() - 1); log_println(WARN, fg(color.red), _("Failed to upgrade: {}"), pkgs_failed_to_build); - log_println(INFO, fg(color.cyan), _("Tip: try to run taur with \"-S {}\" (e.g \"taur -S {}\")"), pkgs_failed_to_build, pkgs_failed_to_build); + log_println(INFO, fg(color.cyan), _("Tip: try to run taur with \"-S {}\" (e.g \"taur -S {}\")"), + pkgs_failed_to_build, pkgs_failed_to_build); } return returnStatus; } -bool removePkg(alpm_list_t *pkgNames) { +bool removePkg(alpm_list_t* pkgNames) +{ if (!pkgNames) return false; - alpm_list_t *exactMatches = nullptr; - alpm_list_t *searchResults = nullptr; - alpm_list_t *filteredPkgNames = nullptr; - alpm_db_t *localdb = alpm_get_localdb(config->handle); + alpm_list_t* exactMatches = nullptr; + alpm_list_t* searchResults = nullptr; + alpm_list_t* filteredPkgNames = nullptr; + alpm_db_t* localdb = alpm_get_localdb(config->handle); - for (alpm_list_t *i = pkgNames; i; i = i->next) { - alpm_pkg_t *result = alpm_db_get_pkg(localdb, (const char *)i->data); + for (alpm_list_t* i = pkgNames; i; i = i->next) + { + alpm_pkg_t* result = alpm_db_get_pkg(localdb, reinterpret_cast(i->data)); if (result != NULL) exactMatches = alpm_list_add(exactMatches, result); else filteredPkgNames = alpm_list_add(filteredPkgNames, i->data); } - if (filteredPkgNames && util_db_search(localdb, filteredPkgNames, &searchResults) != 0) { + if (filteredPkgNames && util_db_search(localdb, filteredPkgNames, &searchResults) != 0) + { if (exactMatches) alpm_list_free(exactMatches); alpm_list_free(filteredPkgNames); @@ -353,28 +389,31 @@ bool removePkg(alpm_list_t *pkgNames) { size_t ret_length = alpm_list_count(ret.get()); - if (ret_length == 0) { + if (ret_length == 0) + { log_println(ERROR, _("No packages found!")); return false; } if (ret_length == 1) - return backend->remove_pkg((alpm_pkg_t *)ret->data); + return backend->remove_pkg(reinterpret_cast(ret->data)); - vector pkgs; + std::vector pkgs; - //fmt::println("Choose packages to remove, (Seperate by spaces, type * to remove all):"); + // fmt::println("Choose packages to remove, (Seperate by spaces, type * to remove all):"); for (size_t i = 0; i < ret_length; i++) - pkgs.push_back(alpm_pkg_get_name((alpm_pkg_t *)(alpm_list_nth(ret.get(), i)->data))); + pkgs.push_back(alpm_pkg_get_name(reinterpret_cast(alpm_list_nth(ret.get(), i)->data))); - vector includedPkgs = askUserForList(pkgs, PROMPT_LIST_REMOVE_PKGS, true); + std::vector includedPkgs = askUserForList(pkgs, PROMPT_LIST_REMOVE_PKGS, true); - alpm_list_t *finalPackageList = nullptr; - alpm_list_t *finalPackageListStart = nullptr; + alpm_list_t* finalPackageList = nullptr; + alpm_list_t* finalPackageListStart = nullptr; - for (size_t i = 0; i < includedPkgs.size(); i++) { - try { + for (size_t i = 0; i < includedPkgs.size(); i++) + { + try + { auto pkg = std::find(pkgs.begin(), pkgs.end(), includedPkgs[i]); if (pkg >= pkgs.end()) @@ -388,8 +427,9 @@ bool removePkg(alpm_list_t *pkgNames) { if (finalPackageList != nullptr && finalPackageListStart == nullptr) finalPackageListStart = finalPackageList; - - } catch (std::invalid_argument const&) { + } + catch (std::invalid_argument const&) + { log_println(WARN, _("Invalid argument! Assuming all.")); if (finalPackageListStart != nullptr) @@ -407,161 +447,135 @@ bool removePkg(alpm_list_t *pkgNames) { return backend->remove_pkgs(finalList); } -bool updateAll() { - return backend->update_all_aur_pkgs(config->cacheDir, config->useGit); -} - -bool queryPkgs(alpm_list_t *pkgNames) { +bool queryPkgs(alpm_list_t* pkgNames) +{ log_println(DEBUG, "AUR Only: {}", config->aurOnly); - // we seperate pkgs that needs to be searched, like they'll include a description, to the ones that we just wanna query out - // pkgs will be used for -Qs, for then using it on printPkgInfo() - // pkgs_name and pkgs_ver will be used for bare operations (only -Q) - vector pkgs_name, pkgs_ver; - vector pkgs; - alpm_db_t *localdb = alpm_get_localdb(config->handle); - - if (op.op_q_search) { - if (!pkgNames) { - alpm_list_t *pkg; - for (pkg = alpm_db_get_pkgcache(localdb); pkg; pkg = pkg->next) { - pkgs.push_back({.name = alpm_pkg_get_name((alpm_pkg_t *)(pkg->data)), - .version = alpm_pkg_get_version((alpm_pkg_t *)(pkg->data)), - .desc = alpm_pkg_get_desc((alpm_pkg_t *)(pkg->data)), - .votes = -1}); // push back a negative votes value so we can disable it on printPkgInfo() - } - } else { - alpm_list_t *result = nullptr; - util_db_search(localdb, pkgNames, &result); - - for (; result; result = result->next) { - pkgs.push_back({.name = alpm_pkg_get_name((alpm_pkg_t *)(result->data)), - .version = alpm_pkg_get_version((alpm_pkg_t *)(result->data)), - .desc = alpm_pkg_get_desc((alpm_pkg_t *)(result->data)), - .votes = -1}); - } - } - - for (size_t i = 0; i < pkgs.size(); i++) - printPkgInfo(pkgs[i], "local"); - - return true; - } - - if (op.op_q_info) { - alpm_pkg_t *pkg; - - if (!pkgNames) { - alpm_list_t *local_pkg; - for (local_pkg = alpm_db_get_pkgcache(localdb); local_pkg; local_pkg = local_pkg->next) { - pkg = (alpm_pkg_t *)(local_pkg->data); - printLocalFullPkgInfo(pkg); - } - } else { - for (alpm_list_t *i = pkgNames; i; i = i->next) { - const char *strname = (const char*)i->data; - pkg = alpm_db_get_pkg(localdb, strname); - - if (pkg == nullptr) - pkg = alpm_find_satisfier(alpm_db_get_pkgcache(localdb), strname); - - // why double check the same thing? because: - // the 1st is to see if pkg "foo" exists, and if not, it will search for (aliases?) - // this one is if no aliases has been found, print the error and continue for the next target - if (pkg == nullptr) { - log_println(ERROR, _("package \"{}\" was not found"), strname); - continue; - } + // we seperate pkgs that needs to be searched, like they'll include a description, to the ones that we just wanna + // query out pkgs will be used for -Qs, for then using it on printPkgInfo() pkgs_name and pkgs_ver will be used for + // bare operations (only -Q) + std::vector pkgs_name, pkgs_ver; + std::vector pkgs; + alpm_db_t* localdb = alpm_get_localdb(config->handle); - printLocalFullPkgInfo(pkg); - } + // just -Q, no options other than --quiet and global ones + if (!pkgNames) + { + alpm_list_t* pkg; + for (pkg = alpm_db_get_pkgcache(localdb); pkg; pkg = alpm_list_next(pkg)) + { + pkgs.push_back(reinterpret_cast(pkg->data)); + pkgs_name.push_back(alpm_pkg_get_name(reinterpret_cast(pkg->data))); + pkgs_ver.push_back(alpm_pkg_get_version(reinterpret_cast(pkg->data))); } - - return true; } - - // just -Q, no options other than --quiet and global ones - if (!pkgNames) { - alpm_list_t *pkg; - for (pkg = alpm_db_get_pkgcache(localdb); pkg; pkg = alpm_list_next(pkg)) { - pkgs_name.push_back(alpm_pkg_get_name((alpm_pkg_t *)(pkg->data))); - pkgs_ver.push_back(alpm_pkg_get_version((alpm_pkg_t *)(pkg->data))); + else if (op.op_q_search) + { + alpm_list_t* result = nullptr; + util_db_search(localdb, pkgNames, &result); + + for (; result; result = result->next) + { + pkgs.push_back(reinterpret_cast(result->data)); + pkgs_name.push_back(alpm_pkg_get_name(reinterpret_cast(result->data))); + pkgs_ver.push_back(alpm_pkg_get_version(reinterpret_cast(result->data))); } } else { - alpm_pkg_t *pkg; - for (; pkgNames; pkgNames = pkgNames->next) { - const char *strname = (const char *)pkgNames->data; - pkg = alpm_db_get_pkg(localdb, strname); + alpm_pkg_t* pkg; + for (; pkgNames; pkgNames = pkgNames->next) + { + const char* strname = reinterpret_cast(pkgNames->data); + pkg = alpm_db_get_pkg(localdb, strname); if (pkg == nullptr) pkg = alpm_find_satisfier(alpm_db_get_pkgcache(localdb), strname); - if (pkg == nullptr) { + if (pkg == nullptr) + { log_println(ERROR, _("package \"{}\" was not found"), strname); continue; } + pkgs.push_back(pkg); pkgs_name.push_back(alpm_pkg_get_name(pkg)); pkgs_ver.push_back(alpm_pkg_get_version(pkg)); } } - if (config->aurOnly) { - alpm_list_t *syncdbs = alpm_get_syncdbs(config->handle); + if (config->aurOnly) + { + alpm_list_t* syncdbs = alpm_get_syncdbs(config->handle); - if (!syncdbs) { + if (!syncdbs) + { log_println(ERROR, _("Failed to get syncdbs!")); return false; } for (; syncdbs; syncdbs = syncdbs->next) for (size_t i = 0; i < pkgs_name.size(); i++) - if (alpm_db_get_pkg((alpm_db_t *)(syncdbs->data), pkgs_name[i])) - pkgs_name[i] = NULL; // wont be printed + if (alpm_db_get_pkg((alpm_db_t*)(syncdbs->data), pkgs_name[i])) + pkgs_name[i] = NULL; // wont be printed } - if (config->quiet) { - for (size_t i = 0; i < pkgs_name.size(); i++) { + if (config->quiet) + { + for (size_t i = 0; i < pkgs_name.size(); i++) + { if (!pkgs_name[i]) continue; fmt::println("{}", pkgs_name[i]); } - } else { - for (size_t i = 0; i < pkgs_name.size(); i++) { + } + else + { + for (size_t i = 0; i < pkgs_name.size(); i++) + { if (!pkgs_name[i]) continue; - fmt::print(BOLD, "{} ", pkgs_name[i]); - fmt::println(BOLD_TEXT(color.green), "{}", pkgs_ver[i]); + + if (!op.op_q_info) { + fmt::print(BOLD, "{} ", pkgs_name[i]); + fmt::println(BOLD_COLOR(color.green), "{}", pkgs_ver[i]); + } else { + printLocalFullPkgInfo(pkgs[i]); + } } } return true; } -bool upgradePkgs(alpm_list_t *pkgNames) { - if (!pkgNames) { +bool upgradePkgs(alpm_list_t* pkgNames) +{ + if (!pkgNames) return false; - } - if (alpm_trans_init(config->handle, config->flags)) { + if (alpm_trans_init(config->handle, config->flags)) + { log_println(ERROR, _("Failed to initialize transaction ({})"), alpm_strerror(alpm_errno(config->handle))); return false; } - for (; pkgNames; pkgNames = pkgNames->next) { - alpm_pkg_t *pkg; - alpm_pkg_load(config->handle, (const char *)pkgNames->data, 1, alpm_option_get_local_file_siglevel(config->handle), &pkg); - - if (!pkg) { - log_println(ERROR, _("Failed to load package {}! ({})"), (const char *)(pkgNames->data), alpm_strerror(alpm_errno(config->handle))); - return false; // Yes, I am ignoring the transaction we just created. + for (; pkgNames; pkgNames = pkgNames->next) + { + alpm_pkg_t* pkg; + alpm_pkg_load(config->handle, reinterpret_cast(pkgNames->data), 1, + alpm_option_get_local_file_siglevel(config->handle), &pkg); + + if (!pkg) + { + log_println(ERROR, _("Failed to load package {}! ({})"), reinterpret_cast(pkgNames->data), + alpm_strerror(alpm_errno(config->handle))); + return false; // Yes, I am ignoring the transaction we just created. } - if (alpm_add_pkg(config->handle, pkg)) { - log_println(ERROR, _("Failed to add package {} to transaction! ({})"), (const char *)(pkgNames->data), alpm_strerror(alpm_errno(config->handle))); + if (alpm_add_pkg(config->handle, pkg)) + { + log_println(ERROR, _("Failed to add package {} to transaction! ({})"), reinterpret_cast(pkgNames->data), + alpm_strerror(alpm_errno(config->handle))); return false; } - } return commitTransactionAndRelease(true); @@ -572,9 +586,11 @@ bool upgradePkgs(alpm_list_t *pkgNames) { /* Inspired by the monotone function localize_monotone. */ // taken from pacman #if defined(ENABLE_NLS) -static void localize(void) { +static void localize(void) +{ static int init = 0; - if (!init) { + if (!init) + { setlocale(LC_ALL, ""); bindtextdomain("taur", LOCALEDIR); textdomain("taur"); @@ -583,13 +599,15 @@ static void localize(void) { } #endif +// clang-format off // parseargs() but only for parsing the user config path trough args // and so we can directly construct Config -static bool parse_config_path(int argc, char* argv[], string& configFile, string& themeFile) { +static bool parse_config_path(int argc, char* argv[], std::string& configFile, std::string& themeFile) +{ int opt = 0; int option_index = 0; opterr = 0; - const char *optstring = "C:"; + const char *optstring = "-C:"; static const struct option opts[] = { {"config", required_argument, 0, OP_CONFIG}, @@ -597,17 +615,20 @@ static bool parse_config_path(int argc, char* argv[], string& configFile, string {0,0,0,0} }; - while ((opt = getopt_long(argc, argv, optstring, opts, &option_index)) != -1) { + while ((opt = getopt_long(argc, argv, optstring, opts, &option_index)) != -1) + { if (opt == 0 || opt == '?') continue; - switch (opt) { + switch (opt) + { case OP_CONFIG: configFile = strndup(optarg, PATH_MAX); if (!std::filesystem::exists(configFile)) die("config file '{}' doesn't exist", configFile); break; + case OP_THEME: themeFile = strndup(optarg, PATH_MAX); if (!std::filesystem::exists(themeFile)) @@ -624,7 +645,8 @@ static bool parse_config_path(int argc, char* argv[], string& configFile, string } // function taken from pacman -int parseargs(int argc, char* argv[]) { +int parseargs(int argc, char* argv[]) +{ // default op.op = OP_MAIN; @@ -668,8 +690,10 @@ int parseargs(int argc, char* argv[]) { {0,0,0,0} }; + // clang-format on /* parse operation */ - while ((opt = getopt_long(argc, argv, optstring, opts, &option_index)) != -1) { + while ((opt = getopt_long(argc, argv, optstring, opts, &option_index)) != -1) + { if (opt == 0) continue; else if (opt == '?') @@ -677,26 +701,31 @@ int parseargs(int argc, char* argv[]) { parsearg_op(opt, 0); } - if (op.op == OP_PACMAN) { + if (op.op == OP_PACMAN) + { log_println(NONE, _("Please use pacman for this command (may need root too)")); execPacman(argc, argv); } - if (op.op == 0) { + if (op.op == 0) + { log_println(ERROR, _("only one operation may be used at a time")); return 1; } - if (op.version) { + if (op.version) + { fmt::println("TabAUR version {}, branch {}", VERSION, BRANCH); exit(0); } - if (op.help) { + if (op.help) + { usage(op.op); exit(1); } optind = 1; - while ((opt = getopt_long(argc, argv, optstring, opts, &option_index)) != -1) { + while ((opt = getopt_long(argc, argv, optstring, opts, &option_index)) != -1) + { if (opt == 0) continue; /* this should have failed during first pass already */ @@ -707,33 +736,32 @@ int parseargs(int argc, char* argv[]) { continue; /* parse all other options */ - switch (op.op) { - case OP_SYNC: - result = parsearg_sync(opt); - break; - case OP_QUERY: - result = parsearg_query(opt); - break; - case OP_REM: - result = parsearg_remove(opt); - break; - default: - result = 1; - break; + switch (op.op) + { + case OP_SYNC: result = parsearg_sync(opt); break; + case OP_QUERY: result = parsearg_query(opt); break; + case OP_REM: result = parsearg_remove(opt); break; + default: result = 1; break; } - if (result == 0) { + if (result == 0) + { continue; } /* fall back to global options */ result = parsearg_global(opt); - if (result != 0) { - if (result == 1) { + if (result != 0) + { + if (result == 1) + { /* global option parsing failed, abort */ - if (opt < 1000) { + if (opt < 1000) + { log_println(ERROR, _("invalid option '-{:c}'"), opt); - } else { + } + else + { log_println(ERROR, _("invalid option '--{}'"), opts[option_index].name); } } @@ -741,9 +769,10 @@ int parseargs(int argc, char* argv[]) { } } - while (optind < argc) { + while (optind < argc) + { /* add the target to our target array */ - alpm_list_t *taur_targets_get = taur_targets.get(); + alpm_list_t* taur_targets_get = taur_targets.get(); (void)taur_targets.release(); alpm_list_append_strdup(&taur_targets_get, argv[optind]); taur_targets = make_list_smart_deleter(taur_targets_get); @@ -754,15 +783,16 @@ int parseargs(int argc, char* argv[]) { } // main -int main(int argc, char *argv[]) { - string configDir = getConfigDir(); +int main(int argc, char* argv[]) +{ + const std::string& configDir = getConfigDir(); #if defined(ENABLE_NLS) localize(); #endif - string configfile = (configDir + "/config.toml"); - string themefile = (configDir + "/theme.toml"); + std::string configfile = (configDir + "/config.toml"); + std::string themefile = (configDir + "/theme.toml"); parse_config_path(argc, argv, configfile, themefile); config = std::make_unique(configfile, themefile, configDir); @@ -770,18 +800,22 @@ int main(int argc, char *argv[]) { if (parseargs(argc, argv)) return 1; - if (op.test_colors) { + if (op.test_colors) + { test_colors(); return 0; } - if (op.show_recipe) { - if (config->secretRecipe) { + if (op.show_recipe) + { + if (config->secretRecipe) + { log_println(INFO, _("Secret recipe unlocked!")); log_println(INFO, _("Loading secret recipe...")); - for (auto const& i : secret) { + for (auto const& i : secret) + { fmt::println("{}", i); - usleep(650000); // 0.65 seconds + usleep(650000); // 0.65 seconds } return 0; } @@ -792,15 +826,20 @@ int main(int argc, char *argv[]) { // the code you're likely interested in backend = std::make_unique(*config); - if (op.requires_root && geteuid() != 0) { + if (op.requires_root && geteuid() != 0) + { log_println(ERROR, _("You need to be root to do this.")); return 1; - } else if (!op.requires_root && geteuid() == 0) { + } + else if (!op.requires_root && geteuid() == 0) + { log_println(ERROR, _("You are trying to run TabAUR as root when you don't need it.")); return 1; } - switch (op.op) { + // clang-format off + switch (op.op) + { case OP_SYNC: return installPkg(taur_targets.get()) ? 0 : 1; case OP_REM: @@ -810,8 +849,6 @@ int main(int argc, char *argv[]) { return queryPkgs(taur_targets.get()) ? 0 : 1; case OP_UPGRADE: return upgradePkgs(taur_targets.get()) ? 0 : 1; - case OP_SYSUPGRADE: - return (updateAll()) ? 0 : 1; default: log_println(ERROR, _("no operation specified (use {} -h for help)"), argv[0]); } diff --git a/src/pacman.cpp b/src/pacman.cpp index 46336ba..ab9bfdf 100644 --- a/src/pacman.cpp +++ b/src/pacman.cpp @@ -17,66 +17,78 @@ * along with this program. If not, see . */ - // general functions taken from pacman source code // putted it on a separeted file for keeping the codebase a bit more clean #include "pacman.hpp" -#include "util.hpp" -#include + #include +#include + +#include "util.hpp" + static int cached_columns = -1; int getcols_fd(int fd) { int width = -1; - if (!isatty(fd)) { + if (!isatty(fd)) + { return 0; } #if defined(TIOCGSIZE) struct ttysize win; - if (ioctl(fd, TIOCGSIZE, &win) == 0) { + if (ioctl(fd, TIOCGSIZE, &win) == 0) + { width = win.ts_cols; } #elif defined(TIOCGWINSZ) struct winsize win; - if (ioctl(fd, TIOCGWINSZ, &win) == 0) { + if (ioctl(fd, TIOCGWINSZ, &win) == 0) + { width = win.ws_col; } #endif - if (width <= 0) { + if (width <= 0) + { return -EIO; } return width; } -unsigned short getcols(void) { - const char *e; +unsigned short getcols(void) +{ + const char* e; int c = -1; - if (cached_columns >= 0) { + if (cached_columns >= 0) + { return cached_columns; } e = getenv("COLUMNS"); - if (e && e[0] != '\0') { - char *p = NULL; + if (e && e[0] != '\0') + { + char* p = NULL; c = strtol(e, &p, 10); - if (*p != '\0') { + if (*p != '\0') + { c = -1; } } - if (c < 0) { + if (c < 0) + { c = getcols_fd(STDOUT_FILENO); } - if (c < 0) { + if (c < 0) + { c = 80; } @@ -84,37 +96,47 @@ unsigned short getcols(void) { return c; } -static size_t string_length(const char *s) { +static size_t string_length(const char* s) +{ int len; - wchar_t *wcstr; + wchar_t* wcstr; - if (!s || s[0] == '\0') { + if (!s || s[0] == '\0') + { return 0; } - if (strstr(s, "\033")) { - char *replaced = (char *)malloc(sizeof(char) * strlen(s)); + if (strstr(s, "\033")) + { + char* replaced = (char*)malloc(sizeof(char) * strlen(s)); int iter = 0; - for (; *s; s++) { - if (*s == '\033') { - while (*s != 'm') { + for (; *s; s++) + { + if (*s == '\033') + { + while (*s != 'm') + { s++; } - } else { + } + else + { replaced[iter] = *s; iter++; } } replaced[iter] = '\0'; len = iter; - wcstr = (wchar_t *)calloc(len, sizeof(wchar_t)); + wcstr = (wchar_t*)calloc(len, sizeof(wchar_t)); len = mbstowcs(wcstr, replaced, len); len = wcswidth(wcstr, len); free(wcstr); free(replaced); - } else { + } + else + { /* len goes from # bytes -> # chars -> # cols */ len = strlen(s) + 1; - wcstr = (wchar_t *)calloc(len, sizeof(wchar_t)); + wcstr = (wchar_t*)calloc(len, sizeof(wchar_t)); len = mbstowcs(wcstr, s, len); len = wcswidth(wcstr, len); free(wcstr); @@ -123,28 +145,35 @@ static size_t string_length(const char *s) { return len; } -void list_display_linebreak(const char *title, const alpm_list_t *list, unsigned short maxcols) { +void list_display_linebreak(const char* title, const alpm_list_t* list, unsigned short maxcols) +{ unsigned short len = 0; - if (title) { + if (title) + { len = (unsigned short)string_length(title) + 1; fmt::print(BOLD, "{}{} ", title, NOCOLOR); } - if (!list) { + if (!list) + { printf("%s\n", _("None")); - } else { - const alpm_list_t *i; + } + else + { + const alpm_list_t* i; /* Print the first element */ - indentprint((const char *)list->data, len, maxcols); + indentprint((const char*)list->data, len, maxcols); printf("\n"); /* Print the rest */ - for (i = alpm_list_next(list); i; i = alpm_list_next(i)) { + for (i = alpm_list_next(list); i; i = alpm_list_next(i)) + { size_t j; - for (j = 1; j <= len; j++) { + for (j = 1; j <= len; j++) + { printf(" "); } - indentprint((const char *)i->data, len, maxcols); + indentprint((const char*)i->data, len, maxcols); printf("\n"); } } @@ -152,54 +181,65 @@ void list_display_linebreak(const char *title, const alpm_list_t *list, unsigned /* output a string, but wrap words properly with a specified indentation */ -void indentprint(const char *str, unsigned short indent, unsigned short cols) { - wchar_t *wcstr; - const wchar_t *p; +void indentprint(const char* str, unsigned short indent, unsigned short cols) +{ + wchar_t* wcstr; + const wchar_t* p; size_t len, cidx; - if (!str) { + if (!str) + { return; } /* if we're not a tty, or our tty is not wide enough that wrapping even makes - * sense, print without indenting */ - if (cols == 0 || indent > cols) { + * sense, print without indenting */ + if (cols == 0 || indent > cols) + { fputs(str, stdout); return; } len = strlen(str) + 1; - wcstr = (wchar_t *)calloc(len, sizeof(wchar_t)); + wcstr = (wchar_t*)calloc(len, sizeof(wchar_t)); len = mbstowcs(wcstr, str, len); p = wcstr; cidx = indent; - if (!p || !len) { + if (!p || !len) + { free(wcstr); return; } - while (*p) { - if (*p == L' ') { + while (*p) + { + if (*p == L' ') + { const wchar_t *q, *next; p++; if (p == NULL || *p == L' ') continue; next = wcschr(p, L' '); - if (next == NULL) { + if (next == NULL) + { next = p + wcslen(p); } /* len captures # cols */ len = 0; q = p; - while (q < next) { + while (q < next) + { len += wcwidth(*q++); } - if ((len + 1) > (cols - cidx)) { + if ((len + 1) > (cols - cidx)) + { /* wrap to a newline and reindent */ printf("\n%-*s", (int)indent, ""); cidx = indent; - } else { + } + else + { printf(" "); cidx++; } @@ -212,13 +252,18 @@ void indentprint(const char *str, unsigned short indent, unsigned short cols) { free(wcstr); } -void string_display(string_view title, string_view string, unsigned short cols) { - if (title.length()) { +void string_display(std::string_view title, std::string_view string, unsigned short cols) +{ + if (title.length()) + { fmt::print(BOLD, "{}{} ", title, NOCOLOR); } - if (string.empty()) { + if (string.empty()) + { printf(_("None")); - } else { + } + else + { /* compute the length of title + a space */ size_t len = string_length(title.data()) + 1; indentprint(string.data(), (unsigned short)len, cols); @@ -226,34 +271,44 @@ void string_display(string_view title, string_view string, unsigned short cols) fmt::print("\n"); } -void list_display(const char *title, const alpm_list_t *list, unsigned short maxcols) { - const alpm_list_t *i; +void list_display(const char* title, const alpm_list_t* list, unsigned short maxcols) +{ + const alpm_list_t* i; size_t len = 0; - if (title) { + if (title) + { len = string_length(title) + 1; fmt::print(BOLD, "{}{} ", title, NOCOLOR); } - if (!list) { + if (!list) + { printf("%s\n", _("None")); - } else { + } + else + { size_t cols = len; - const char *str = (const char *)list->data; + const char* str = (const char*)list->data; fputs(str, stdout); cols += string_length(str); - for (i = alpm_list_next(list); i; i = alpm_list_next(i)) { - str = (const char *)i->data; + for (i = alpm_list_next(list); i; i = alpm_list_next(i)) + { + str = (const char*)i->data; size_t s = string_length(str); /* wrap only if we have enough usable column space */ - if (maxcols > len && cols + s + 2 >= maxcols) { + if (maxcols > len && cols + s + 2 >= maxcols) + { size_t j; cols = len; printf("\n"); - for (j = 1; j <= len; j++) { + for (j = 1; j <= len; j++) + { printf(" "); } - } else if (cols != len) { + } + else if (cols != len) + { /* 2 spaces are added if this is not the first element on a line. */ printf(" "); cols += 2; @@ -268,10 +323,12 @@ void list_display(const char *title, const alpm_list_t *list, unsigned short max /** Turn a depends list into a text list. * @param deps a list with items of type alpm_depend_t */ -void deplist_display(const char *title, alpm_list_t *deps, unsigned short cols) { +void deplist_display(const char* title, alpm_list_t* deps, unsigned short cols) +{ alpm_list_t *i, *text = NULL; - for (i = deps; i; i = alpm_list_next(i)) { - alpm_depend_t *dep = (alpm_depend_t *)i->data; + for (i = deps; i; i = alpm_list_next(i)) + { + alpm_depend_t* dep = (alpm_depend_t*)i->data; text = alpm_list_add(text, alpm_dep_compute_string(dep)); } list_display(title, text, cols); diff --git a/src/taur.cpp b/src/taur.cpp index 9ea3afd..7d69457 100644 --- a/src/taur.cpp +++ b/src/taur.cpp @@ -1,25 +1,33 @@ // Functions for TabAUR, These include printing stuff and others. // main.cpp simply pieces each function together to make the program work. #include "taur.hpp" -#include "config.hpp" -#include "util.hpp" + #include #include #include +#include "config.hpp" +#include "util.hpp" + TaurBackend::TaurBackend(Config& cfg) : config(cfg) {} -bool TaurBackend::download_git(string_view url, path out_path) { - if (std::filesystem::exists(path(out_path) / ".git")) { - return taur_exec({config.git.c_str(), "-C", out_path, "pull", "--autostash", "--rebase", "--ff-only", "--force"}); - } else { +bool TaurBackend::download_git(std::string_view url, path out_path) +{ + if (std::filesystem::exists(path(out_path) / ".git")) + { + return taur_exec( + { config.git.c_str(), "-C", out_path, "pull", "--autostash", "--rebase", "--ff-only", "--force" }); + } + else + { if (std::filesystem::exists(path(out_path))) std::filesystem::remove_all(out_path); - return taur_exec({config.git.c_str(), "clone", url.data(), out_path}); + return taur_exec({ config.git.c_str(), "clone", url.data(), out_path }); } } -bool TaurBackend::download_tar(string_view url, path out_path) { +bool TaurBackend::download_tar(std::string_view url, path out_path) +{ std::ofstream out(out_path.replace_extension("tar.gz")); if (!out.is_open()) return false; @@ -34,15 +42,16 @@ bool TaurBackend::download_tar(string_view url, path out_path) { if (out_path.has_parent_path()) std::filesystem::current_path(out_path.parent_path()); - return taur_exec({"tar", "-xf", out_path.c_str()}); + return taur_exec({ "tar", "-xf", out_path.c_str() }); } /** Downloads a package from the AUR repository. * @param url a link to the download page, it will detect the extension and call the cooresponding download_x function. * @param out_path the path to extract the folder, for git folders, this will be where the repo is cloned. * @returns bool, true = success, false = failure. -*/ -bool TaurBackend::download_pkg(string_view url, path out_path) { + */ +bool TaurBackend::download_pkg(std::string_view url, path out_path) +{ if (hasEnding(url, ".git")) return this->download_git(url, out_path); else if (hasEnding(url, ".tar.gz")) @@ -50,23 +59,22 @@ bool TaurBackend::download_pkg(string_view url, path out_path) { return false; } -string getUrl(rapidjson::Value& pkgJson, bool returnGit = false) { - std::stringstream ss; +std::string getUrl(const rapidjson::Value& pkgJson, bool returnGit = false) +{ if (returnGit) - ss << "https://aur.archlinux.org/" << pkgJson["Name"].GetString() << ".git"; - else - ss << "https://aur.archlinux.org" << pkgJson["URLPath"].GetString(); - - return ss.str(); + return fmt::format("https://aur.archlinux.org/{}.git", pkgJson["Name"].GetString()); + else // URLPath starts with a / + return fmt::format("https://aur.archlinux.org{}", pkgJson["URLPath"].GetString()); } -TaurPkg_t parsePkg(rapidjson::Value& pkgJson, bool returnGit = false) { - vector makedepends, depends, totaldepends, licenses; +TaurPkg_t parsePkg(rapidjson::Value& pkgJson, bool returnGit = false) +{ + std::vector makedepends, depends, totaldepends, licenses; // yes, it will get depends and makedepends even one doesn't have it if (pkgJson.HasMember("Depends") && pkgJson["Depends"].IsArray()) { - const rapidjson::Value& dependsArray = pkgJson["Depends"].GetArray(); + const rapidjson::Value& dependsArray = pkgJson["Depends"].GetArray(); depends.reserve(dependsArray.Size()); @@ -74,14 +82,17 @@ TaurPkg_t parsePkg(rapidjson::Value& pkgJson, bool returnGit = false) { depends.push_back(dependsArray[i].GetString()); totaldepends.insert(totaldepends.end(), depends.begin(), depends.end()); - //for (size_t i = 0; i < makeDependsArray.Size(); i++) { - // // make sure it isn't in the depends list - // if (std::find(totaldepends.begin(), totaldepends.end(), makeDependsArray[i].GetString()) != totaldepends.end()) - // continue; - // totaldepends.push_back(makeDependsArray[i].GetString()); - //} + // for (size_t i = 0; i < makeDependsArray.Size(); i++) { + // // make sure it isn't in the depends list + // if (std::find(totaldepends.begin(), totaldepends.end(), makeDependsArray[i].GetString()) != + // totaldepends.end()) + // continue; + // totaldepends.push_back(makeDependsArray[i].GetString()); + // } } - if (pkgJson.HasMember("MakeDepends") && pkgJson["MakeDepends"].IsArray()) { + + if (pkgJson.HasMember("MakeDepends") && pkgJson["MakeDepends"].IsArray()) + { const rapidjson::Value& makeDependsArray = pkgJson["MakeDepends"].GetArray(); makedepends.reserve(makeDependsArray.Size()); @@ -91,8 +102,10 @@ TaurPkg_t parsePkg(rapidjson::Value& pkgJson, bool returnGit = false) { totaldepends.insert(totaldepends.end(), makedepends.begin(), makedepends.end()); } - if (pkgJson.HasMember("License") && pkgJson["License"].IsArray()) { - const rapidjson::Value& licensesArray = pkgJson["License"].GetArray(); + + if (pkgJson.HasMember("License") && pkgJson["License"].IsArray()) + { + const rapidjson::Value& licensesArray = pkgJson["License"].GetArray(); licenses.reserve(licensesArray.Size()); @@ -105,7 +118,9 @@ TaurPkg_t parsePkg(rapidjson::Value& pkgJson, bool returnGit = false) { .version = pkgJson["Version"].GetString(), .aur_url = getUrl(pkgJson, returnGit), .desc = pkgJson["Description"].IsString() ? pkgJson["Description"].GetString() : "", - .maintainer = pkgJson["Maintainer"].IsString() ? pkgJson["Maintainer"].GetString() : "\1", // it's impossible that the maintainer name is a binary char + .maintainer = pkgJson["Maintainer"].IsString() + ? pkgJson["Maintainer"].GetString() + : "\1", // it's impossible that the maintainer name is a binary char .last_modified = pkgJson["LastModified"].GetInt64(), .outofdate = pkgJson["OutOfDate"].IsInt64() ? pkgJson["OutOfDate"].GetInt64() : 0, .popularity = pkgJson["Popularity"].GetFloat(), @@ -120,8 +135,9 @@ TaurPkg_t parsePkg(rapidjson::Value& pkgJson, bool returnGit = false) { return out; } -optional TaurBackend::fetch_pkg(string_view pkg, bool returnGit) { - string urlStr = "https://aur.archlinux.org/rpc/v5/info/" + cpr::util::urlEncode(pkg.data()); +std::optional TaurBackend::fetch_pkg(std::string_view pkg, bool returnGit) +{ + std::string urlStr = "https://aur.archlinux.org/rpc/v5/info/" + cpr::util::urlEncode(pkg.data()); cpr::Url url = cpr::Url(urlStr); cpr::Response resp = cpr::Get(url); @@ -138,11 +154,12 @@ optional TaurBackend::fetch_pkg(string_view pkg, bool returnGit) { return {}; } -vector TaurBackend::fetch_pkgs(vector const& pkgs, bool returnGit) { +std::vector TaurBackend::fetch_pkgs(std::vector const& pkgs, bool returnGit) +{ if (pkgs.empty()) - return vector(); + return std::vector(); - string urlStr = "https://aur.archlinux.org/rpc/v5/info?arg%5B%5D=" + pkgs[0]; + std::string urlStr = "https://aur.archlinux.org/rpc/v5/info?arg%5B%5D=" + pkgs[0]; for (size_t i = 1; i < pkgs.size(); i++) urlStr += ("&arg%5B%5D=" + pkgs[i]); @@ -153,12 +170,12 @@ vector TaurBackend::fetch_pkgs(vector const& pkgs, bool retur cpr::Response resp = cpr::Get(url); if (resp.status_code != 200) - return vector(); + return std::vector(); rapidjson::Document json; json.Parse(resp.text.c_str()); - vector out; + std::vector out; out.reserve(json["resultcount"].GetInt()); for (int i = 0; i < json["resultcount"].GetInt(); i++) out.push_back(parsePkg(json["results"][i], returnGit)); @@ -167,20 +184,24 @@ vector TaurBackend::fetch_pkgs(vector const& pkgs, bool retur } /** Removes a single packages using libalpm. - @param pkgs an alpm package to remove. - @param ownTransaction a bool that dictates whether this function owns the transaction, this is used for remove_pkgs, if not, it will not initialize it and not release it. - @return success. + * @param pkgs an alpm package to remove. + * @param ownTransaction a bool that dictates whether this function owns the transaction, this is used for remove_pkgs, + * if not, it will not initialize it and not release it. + * @return success. */ -bool TaurBackend::remove_pkg(alpm_pkg_t *pkg, bool ownTransaction) { +bool TaurBackend::remove_pkg(alpm_pkg_t* pkg, bool ownTransaction) +{ if (!pkg) return false; - if (ownTransaction && alpm_trans_init(this->config.handle, this->config.flags)) { + if (ownTransaction && alpm_trans_init(this->config.handle, this->config.flags)) + { log_println(ERROR, _("Failed to initialize transaction ({})"), alpm_strerror(alpm_errno(this->config.handle))); return false; } - if (alpm_remove_pkg(this->config.handle, pkg) != 0) { + if (alpm_remove_pkg(this->config.handle, pkg) != 0) + { log_println(ERROR, _("Failed to remove package ({})"), alpm_strerror(alpm_errno(this->config.handle))); if (ownTransaction) alpm_trans_release(this->config.handle); @@ -195,37 +216,46 @@ bool TaurBackend::remove_pkg(alpm_pkg_t *pkg, bool ownTransaction) { /** Removes a list of packages using libalpm. * Will automatically call remove_pkg instead, if the size of pkgs is equal to 1. - @param pkgs alpm list of alpm_pkg_t pointers to remove. - @return success. -*/ -bool TaurBackend::remove_pkgs(alpm_list_smart_pointer& pkgs) { + * @param pkgs alpm list of alpm_pkg_t pointers to remove. + * @return success. + */ +bool TaurBackend::remove_pkgs(alpm_list_smart_pointer& pkgs) +{ if (!pkgs) return false; - if (alpm_trans_init(this->config.handle, this->config.flags)) { + if (alpm_trans_init(this->config.handle, this->config.flags)) + { log_println(ERROR, _("Failed to initialize transaction ({})"), alpm_strerror(alpm_errno(this->config.handle))); return false; } size_t pkgs_length = alpm_list_count(pkgs.get()); - if (pkgs_length == 0) { + if (pkgs_length == 0) + { log_println(ERROR, _("Couldn't find any packages!")); alpm_trans_release(this->config.handle); return false; - } else if (pkgs_length == 1) { - return this->remove_pkg((alpm_pkg_t *)(pkgs->data), false) && commitTransactionAndRelease(); } - for (alpm_list_t *pkgsGet = pkgs.get(); pkgsGet; pkgsGet = pkgsGet->next) { - bool success = this->remove_pkg((alpm_pkg_t *)(pkgsGet->data), false); - if (!success) { + else if (pkgs_length == 1) + { + return this->remove_pkg((alpm_pkg_t*)(pkgs->data), false) && commitTransactionAndRelease(); + } + + for (alpm_list_t* pkgsGet = pkgs.get(); pkgsGet; pkgsGet = pkgsGet->next) + { + bool success = this->remove_pkg((alpm_pkg_t*)(pkgsGet->data), false); + if (!success) + { alpm_trans_release(this->config.handle); return false; } } - if (!commitTransactionAndRelease()) { + if (!commitTransactionAndRelease()) + { log_println(ERROR, _("Failed to prepare, commit, or release alpm transaction.")); return false; } @@ -233,46 +263,55 @@ bool TaurBackend::remove_pkgs(alpm_list_smart_pointer& pkgs) { return true; } -bool TaurBackend::build_pkg(string_view pkg_name, string extracted_path, bool alreadyprepared) { - // never forget to sanitize - sanitizeStr(extracted_path); +bool TaurBackend::build_pkg(std::string_view pkg_name, std::string_view extracted_path, bool alreadyprepared) +{ std::filesystem::current_path(extracted_path); - if (!alreadyprepared) { + if (!alreadyprepared) + { log_println(INFO, _("Verifying package sources..")); - makepkg_exec({"--verifysource", "--skippgpcheck", "-fs", "-Cc"}); + makepkg_exec({ "--verifysource", "--skippgpcheck", "-fs", "-Cc" }); log_println(INFO, _("Preparing for compilation..")); - makepkg_exec({"--nobuild", "--skippgpcheck", "-fs", "-C", "--ignorearch"}); + makepkg_exec({ "--nobuild", "--skippgpcheck", "-fs", "-C", "--ignorearch" }); } built_pkg = makepkg_list(pkg_name.data(), extracted_path); log_println(DEBUG, "built_pkg = {}", built_pkg); - if (!std::filesystem::exists(built_pkg)) { - /*log_println(INFO, _("Compiling {} in 3 seconds, you can cancel at this point if you can't compile."), pkg_name); - sleep(3);*/ + if (!std::filesystem::exists(built_pkg)) + { + /*log_println(INFO, _("Compiling {} in 3 seconds, you can cancel at this point if you can't compile."), + pkg_name); sleep(3);*/ - return makepkg_exec({"-fs", "--noconfirm", "--noextract", "--noprepare", "--nocheck", "--holdver", "--ignorearch", "-c"}, false); - } else + return makepkg_exec( + { "-fs", "--noconfirm", "--noextract", "--noprepare", "--nocheck", "--holdver", "--ignorearch", "-c" }, + false); + } + else log_println(INFO, _("{} exists already, skipping..."), built_pkg); return true; } // I don't know but I feel this is shitty, atleast it works great -bool TaurBackend::handle_aur_depends(TaurPkg_t pkg, path out_path, vector const& localPkgs, bool useGit) { +bool TaurBackend::handle_aur_depends(const TaurPkg_t& pkg, path out_path, std::vector const& localPkgs, bool useGit) +{ log_println(DEBUG, "pkg.name = {}", pkg.name); log_println(DEBUG, "pkg.totaldepends = {}", pkg.totaldepends); - vector aur_list = load_aur_list(); + std::vector aur_list = load_aur_list(); + + for (size_t i = 0; i < pkg.totaldepends.size(); i++) + { + std::string_view pkg_depend; + + if (std::binary_search(aur_list.begin(), aur_list.end(), pkg.totaldepends.at(i))) + pkg_depend = pkg.totaldepends.at(i); - for (size_t i = 0; i < pkg.totaldepends.size(); i++) { - string_view pkg_depend = binarySearch(aur_list, pkg.totaldepends[i]); - if (pkg_depend.empty()) continue; - - optional oDepend = this->fetch_pkg(pkg_depend, useGit); + + std::optional oDepend = this->fetch_pkg(pkg_depend, useGit); if (!oDepend) continue; @@ -282,21 +321,31 @@ bool TaurBackend::handle_aur_depends(TaurPkg_t pkg, path out_path, vector oSubDepend = this->fetch_pkg(sub_pkg_depend, useGit); + + std::optional oSubDepend = this->fetch_pkg(sub_pkg_depend, useGit); if (!oDepend) continue; @@ -304,42 +353,55 @@ bool TaurBackend::handle_aur_depends(TaurPkg_t pkg, path out_path, vectordownload_pkg(subDepend.aur_url, filename)) { - log_println(ERROR, _("Failed to download dependency {} of dependency {}."), subDepend.name, depend.name); + log_println(DEBUG, "Downloading dependency {} of dependency {}.", subDepend.name, depend.name); + if (!this->download_pkg(subDepend.aur_url, filename)) + { + log_println(ERROR, _("Failed to download dependency {} of dependency {}."), subDepend.name, + depend.name); continue; } - log_println(DEBUG, "Handling dependencies for dependency {} of dependency {}.", subDepend.name, depend.name); - if (!depend.totaldepends.empty() && !handle_aur_depends(subDepend, out_path, localPkgs, useGit)) { - log_println(ERROR, _("Failed to handle dependencies for dependency {} of dependency {}."), subDepend.name, depend.name); + log_println(DEBUG, "Handling dependencies for dependency {} of dependency {}.", subDepend.name, + depend.name); + if (!depend.totaldepends.empty() && !handle_aur_depends(subDepend, out_path, localPkgs, useGit)) + { + log_println(ERROR, _("Failed to handle dependencies for dependency {} of dependency {}."), + subDepend.name, depend.name); continue; } if (!useGit) filename = filename.substr(0, filename.rfind(".tar.gz")); - log_println(DEBUG, "Installing dependency {} of dependency {}.", subDepend.name, depend.name); - if (!this->build_pkg(subDepend.name, filename, false)) { + log_println(DEBUG, "Installing dependency {} of dependency {}.", subDepend.name, depend.name); + if (!this->build_pkg(subDepend.name, filename, false)) + { log_println(ERROR, _("Failed to compile dependency {} of dependency {}."), subDepend.name, depend.name); continue; } - log_println(DEBUG, "Installing dependency {} of dependency {}.", subDepend.name, depend.name); - if (!pacman_exec("-U", split(built_pkg, ' '), false)) { + log_println(DEBUG, "Installing dependency {} of dependency {}.", subDepend.name, depend.name); + if (!pacman_exec("-U", split(built_pkg, ' '), false)) + { log_println(ERROR, _("Failed to install dependency {} of dependency {}."), subDepend.name, depend.name); continue; } @@ -347,27 +409,30 @@ bool TaurBackend::handle_aur_depends(TaurPkg_t pkg, path out_path, vectordownload_pkg(depend.aur_url, filename); - if (!downloadStatus) { + if (!downloadStatus) + { log_println(ERROR, _("Failed to download dependency of {} (Source: {})"), pkg.name, depend.aur_url); return false; } - bool installStatus = this->build_pkg(depend.name, out_path / depend.name, false); + bool installStatus = this->build_pkg(depend.name, (out_path / depend.name).string(), false); - if (!installStatus) { + if (!installStatus) + { log_println(ERROR, _("Failed to install dependency of {} ({})"), pkg.name, depend.name); return false; } - log_println(DEBUG, "Installing dependency of {} ({}).", pkg.name, depend.name); - if (!pacman_exec("-U", split(built_pkg, ' '), false)) { + log_println(DEBUG, "Installing dependency of {} ({}).", pkg.name, depend.name); + if (!pacman_exec("-U", split(built_pkg, ' '), false)) + { log_println(ERROR, _("Failed to install dependency of {} ({})."), pkg.name, depend.name); return false; } @@ -376,58 +441,70 @@ bool TaurBackend::handle_aur_depends(TaurPkg_t pkg, path out_path, vector localPkgs = this->get_all_local_pkgs(true); +bool TaurBackend::update_all_aur_pkgs(path cacheDir, bool useGit) +{ + std::vector localPkgs = this->get_all_local_pkgs(true); - if (localPkgs.empty()) { + if (localPkgs.empty()) + { log_println(INFO, _("No AUR packages found in your system.")); return true; } - vector pkgNames; + std::string line; + std::vector pkgNames; pkgNames.reserve(localPkgs.size()); - for (const TaurPkg_t &pkg : localPkgs) + for (const TaurPkg_t& pkg : localPkgs) pkgNames.push_back(pkg.name); - vector onlinePkgs = this->fetch_pkgs(pkgNames, useGit); + std::vector onlinePkgs = this->fetch_pkgs(pkgNames, useGit); - int updatedPkgs = 0; - int attemptedDownloads = 0; + int updatedPkgs = 0; + int attemptedDownloads = 0; if (onlinePkgs.size() != localPkgs.size()) - log_println(WARN, _("Couldn't get all packages! (searched {} packages, got {}) Still trying to update the others."), localPkgs.size(), onlinePkgs.size()); + log_println(WARN, + _("Couldn't get all packages! (searched {} packages, got {}) Still trying to update the others."), + localPkgs.size(), onlinePkgs.size()); - log_println(INFO, "Here's a list of packages that may be updated:"); + log_println(INFO, "Here's a list of packages that may be upgraded:"); - vector> potentialUpgradeTargets; + std::vector> potentialUpgradeTargets; - for (size_t i = 0; i < onlinePkgs.size(); i++) { - TaurPkg_t &pkg = onlinePkgs[i]; - auto pkgIteratorInLocalPkgs = std::find_if(localPkgs.begin(), localPkgs.end(), [&pkg] (const TaurPkg_t &element) { return element.name == pkg.name; }); - size_t pkgIndexInLocalPkgs = std::distance(localPkgs.begin(), pkgIteratorInLocalPkgs); - TaurPkg_t &localPkg = localPkgs[pkgIndexInLocalPkgs]; + for (size_t i = 0; i < onlinePkgs.size(); i++) + { + TaurPkg_t& pkg = onlinePkgs[i]; + auto pkgIteratorInLocalPkgs = std::find_if(localPkgs.begin(), localPkgs.end(), [&pkg](const TaurPkg_t& element) { return element.name == pkg.name; }); + size_t pkgIndexInLocalPkgs = std::distance(localPkgs.begin(), pkgIteratorInLocalPkgs); + TaurPkg_t& localPkg = localPkgs[pkgIndexInLocalPkgs]; - if (hasEnding(pkg.name, "-git")) { + if (hasEnding(pkg.name, "-git")) + { potentialUpgradeTargets.push_back(std::make_tuple(pkg, localPkg)); - log_println(INFO, "- {} (from {} to {}, (dev package, may change despite AUR version))", localPkg.name, localPkg.version, pkg.version); + log_println(INFO, "- {} (from {} to {}, (dev package, may change despite AUR version))", localPkg.name, + localPkg.version, pkg.version); continue; } - - if ((localPkg.version != pkg.version) && alpm_pkg_vercmp(pkg.version.c_str(), localPkg.version.c_str()) == 1) { + + if ((localPkg.version != pkg.version) && alpm_pkg_vercmp(pkg.version.c_str(), localPkg.version.c_str()) == 1) + { potentialUpgradeTargets.push_back(std::make_tuple(pkg, localPkg)); log_println(INFO, "- {} (from {} to {})", localPkg.name, localPkg.version, pkg.version); continue; } } + log_println(INFO, _("{} packages to upgrade."), potentialUpgradeTargets.size()); + if (!askUserYorN(true, PROMPT_YN_PROCEED_UPGRADE)) return false; - for (const auto &potentialUpgrade : potentialUpgradeTargets) { + for (const auto& potentialUpgrade : potentialUpgradeTargets) + { // size_t pkgIndex; // bool found = false; - bool alrprepared = false; + bool alrprepared = false; // for (pkgIndex = 0; pkgIndex < localPkgs.size(); pkgIndex++) { // if (localPkgs[pkgIndex].name == onlinePkgs[i].name) { @@ -436,22 +513,22 @@ bool TaurBackend::update_all_aur_pkgs(path cacheDir, bool useGit) { // } // } - TaurPkg_t potentialUpgradeTargetTo = std::get<0>(potentialUpgrade); - TaurPkg_t potentialUpgradeTargetFrom = std::get<1>(potentialUpgrade); + const TaurPkg_t& potentialUpgradeTargetTo = std::get<0>(potentialUpgrade); + const TaurPkg_t& potentialUpgradeTargetFrom = std::get<1>(potentialUpgrade); log_println(DEBUG, "potentialUpgradeTarget.name = {}", potentialUpgradeTargetTo.name); log_println(DEBUG, "potentialUpgradeTarget.totaldepends = {}", potentialUpgradeTargetTo.totaldepends); // if (!found) { - // log_println(WARN, _("We couldn't find {} in the local pkg database, This shouldn't happen."), onlinePkgs[i].name); - // continue; + // log_println(WARN, _("We couldn't find {} in the local pkg database, This shouldn't happen."), + // onlinePkgs[i].name); continue; // } bool isGitPackage = hasEnding(potentialUpgradeTargetTo.name, "-git"); // if (!isGitPackage && localPkgs[pkgIndex].version == onlinePkgs[i].version) { - // log_println(DEBUG, "pkg {} has no update, local: {}, online: {}, skipping!", localPkgs[pkgIndex].name, localPkgs[pkgIndex].version, onlinePkgs[i].version); - // continue; + // log_println(DEBUG, "pkg {} has no update, local: {}, online: {}, skipping!", localPkgs[pkgIndex].name, + // localPkgs[pkgIndex].version, onlinePkgs[i].version); continue; // } path pkgDir = cacheDir / potentialUpgradeTargetTo.name; @@ -463,28 +540,44 @@ bool TaurBackend::update_all_aur_pkgs(path cacheDir, bool useGit) { bool downloadSuccess = this->download_pkg(potentialUpgradeTargetTo.aur_url, pkgDir); - if (!downloadSuccess) { + if (!downloadSuccess) + { log_println(WARN, _("Failed to download package {}!"), potentialUpgradeTargetTo.name); continue; } // workaround for -git package because they are "special" - if (isGitPackage) { + if (isGitPackage) + { alrprepared = true; std::filesystem::current_path(pkgDir); - makepkg_exec({"--verifysource", "-fA"}); - makepkg_exec({"--nobuild", "-dfA"}); + makepkg_exec({ "--verifysource", "-fA" }); + makepkg_exec({ "--nobuild", "-dfA" }); } - string versionInfo = shell_exec("grep 'pkgver=' " + pkgDir.string() + "/PKGBUILD | cut -d= -f2"); + u_short iter_index = 0; + std::ifstream pkgbuild(pkgDir.string() + "/PKGBUILD", std::ios::in); + std::string versionInfo, pkgrel, epoch; + while (std::getline(pkgbuild, line) && iter_index < 3) + { + if (hasStart(line, "pkgver=")) + getFileValue(iter_index, line, versionInfo, "pkgver="_len); - if (versionInfo.empty()) { - log_println(WARN, _("Failed to parse version information from {}'s PKGBUILD, You might be able to ignore this safely."), potentialUpgradeTargetTo.name); - continue; + else if (hasStart(line, "pkgrel=")) + getFileValue(iter_index, line, pkgrel, "pkgrel"_len); + + else if (hasStart(line, "epoch=")) + getFileValue(iter_index, line, epoch, "epoch="_len); } - string pkgrel = shell_exec("grep 'pkgrel=' " + pkgDir.string() + "/PKGBUILD | cut -d= -f2"); - string epoch = shell_exec("grep 'epoch=' " + pkgDir.string() + "/PKGBUILD | cut -d= -f2"); + if (versionInfo.empty()) + { + log_println( + WARN, + _("Failed to parse version information from {}'s PKGBUILD, You might be able to ignore this safely."), + potentialUpgradeTargetTo.name); + continue; + } if (!pkgrel.empty() && pkgrel[0] != '\0') versionInfo += '-' + pkgrel; @@ -492,51 +585,64 @@ bool TaurBackend::update_all_aur_pkgs(path cacheDir, bool useGit) { if (!epoch.empty() && epoch[0] != '\0') versionInfo = epoch + ':' + versionInfo; - log_println(DEBUG, "pkg {} versions: local {} vs online {}", potentialUpgradeTargetTo.name, potentialUpgradeTargetTo.version, potentialUpgradeTargetFrom.version); + log_println(DEBUG, "pkg {} versions: local {} vs online {}", potentialUpgradeTargetTo.name, + potentialUpgradeTargetTo.version, potentialUpgradeTargetFrom.version); - if ((alpm_pkg_vercmp(potentialUpgradeTargetFrom.version.data(), versionInfo.c_str())) == 0) { - - log_println(DEBUG, _("pkg {} has the same version on the AUR than in its PKGBUILD, local: {}, online: {}, PKGBUILD: {}, skipping!"), potentialUpgradeTargetFrom.name, - potentialUpgradeTargetFrom.version, potentialUpgradeTargetTo.version, versionInfo); + if ((alpm_pkg_vercmp(potentialUpgradeTargetFrom.version.data(), versionInfo.c_str())) == 0) + { + log_println(DEBUG, + _("pkg {} has the same version on the AUR than in its PKGBUILD, local: {}, online: {}, " + "PKGBUILD: {}, skipping!"), + potentialUpgradeTargetFrom.name, potentialUpgradeTargetFrom.version, + potentialUpgradeTargetTo.version, versionInfo); continue; } - log_println(INFO, _("Upgrading package {} from version {} to version {}!"), potentialUpgradeTargetFrom.name, potentialUpgradeTargetTo.version, potentialUpgradeTargetTo.version); + log_println(INFO, _("Upgrading package {} from version {} to version {}!"), potentialUpgradeTargetFrom.name, + potentialUpgradeTargetTo.version, potentialUpgradeTargetTo.version); attemptedDownloads++; this->handle_aur_depends(potentialUpgradeTargetTo, pkgDir, this->get_all_local_pkgs(true), useGit); - bool installSuccess = this->build_pkg(potentialUpgradeTargetTo.name, pkgDir, alrprepared); + bool installSuccess = this->build_pkg(potentialUpgradeTargetTo.name, pkgDir.string(), alrprepared); - if (installSuccess) { + if (installSuccess) + { pkgs_to_install += built_pkg + ' '; log_println(DEBUG, "pkgs_to_install = {}", pkgs_to_install); updatedPkgs++; // prevent duplicated entries - //localPkgs.erase(localPkgs.begin() + pkgIndex); - } else { + // localPkgs.erase(localPkgs.begin() + pkgIndex); + } + else + { pkgs_failed_to_build += potentialUpgradeTargetTo.name + ' '; log_println(DEBUG, "pkgs_failed_to_build = {}", pkgs_failed_to_build); } } - if (pkgs_to_install.size() <= 0) { + if (pkgs_to_install.size() <= 0) + { log_println(WARN, _("No packages to be upgraded.")); return false; } - if (!pacman_exec("-U", split(pkgs_to_install, ' '), false)) { + if (!pacman_exec("-U", split(pkgs_to_install, ' '), false)) + { log_println(ERROR, _("Failed to install/upgrade packages")); return false; } log_println(INFO, _("Upgraded {}/{} packages."), updatedPkgs, attemptedDownloads); - if (attemptedDownloads > updatedPkgs) { + if (attemptedDownloads > updatedPkgs) + { pkgs_failed_to_build.erase(pkgs_failed_to_build.end() - 1); log_println(WARN, fg(color.red), _("Failed to upgrade: {}"), pkgs_failed_to_build); - //log_println(WARN, _("Some packages failed to download/upgrade, Please redo this command and log the issue.\nIf it is an issue with TabAUR, feel free to open an issue in GitHub.")); - log_println(INFO, fg(color.cyan), _("Tip: try to run taur with \"-S {}\" (e.g \"taur -S {}\")"), pkgs_failed_to_build, pkgs_failed_to_build); + // log_println(WARN, _("Some packages failed to download/upgrade, Please redo this command and log the + // issue.\nIf it is an issue with TabAUR, feel free to open an issue in GitHub.")); + log_println(INFO, fg(color.cyan), _("Tip: try to run taur with \"-S {}\" (e.g \"taur -S {}\")"), + pkgs_failed_to_build, pkgs_failed_to_build); } return true; @@ -544,37 +650,41 @@ bool TaurBackend::update_all_aur_pkgs(path cacheDir, bool useGit) { // all AUR local packages // returns a barebones TaurPkg_t structure, with no description/depends list -vector TaurBackend::get_all_local_pkgs(bool aurOnly) { - vector pkgs; +std::vector TaurBackend::get_all_local_pkgs(bool aurOnly) +{ + std::vector pkgs; - alpm_list_t *pkg, *syncdbs; + alpm_list_t *pkg, *syncdbs; syncdbs = config.repos; for (pkg = alpm_db_get_pkgcache(alpm_get_localdb(config.handle)); pkg; pkg = pkg->next) - pkgs.push_back((alpm_pkg_t *)(pkg->data)); + pkgs.push_back((alpm_pkg_t*)(pkg->data)); if (aurOnly) pkgs = filterAURPkgs(pkgs, syncdbs, true); - vector out; + std::vector out; out.reserve(pkgs.size()); - for (alpm_pkg_t *pkg : pkgs) { - out.push_back({.name = alpm_pkg_get_name(pkg), - .version = alpm_pkg_get_version(pkg), - .aur_url = "https://aur.archlinux.org/" + cpr::util::urlEncode(alpm_pkg_get_name(pkg)) + ".git", - .installed = true}); + for (alpm_pkg_t* pkg : pkgs) + { + out.push_back({ .name = alpm_pkg_get_name(pkg), + .version = alpm_pkg_get_version(pkg), + .aur_url = "https://aur.archlinux.org/" + cpr::util::urlEncode(alpm_pkg_get_name(pkg)) + ".git", + .installed = true }); } return out; } -// They are different because we found that fetching each AUR pkg is very time consuming, so we store the name and look it up later. -vector TaurBackend::getPkgFromJson(rapidjson::Document& doc, bool useGit) { +// They are different because we found that fetching each AUR pkg is very time consuming, so we store the name and look +// it up later. +std::vector TaurBackend::getPkgFromJson(rapidjson::Document& doc, bool useGit) +{ int resultcount = doc["resultcount"].GetInt(); - vector out(resultcount); + std::vector out(resultcount); for (int i = 0; i < resultcount; i++) out[i] = parsePkg(doc["results"][i], useGit); @@ -582,37 +692,42 @@ vector TaurBackend::getPkgFromJson(rapidjson::Document& doc, bool use return out; } -vector TaurBackend::search_pac(string_view query) { +std::vector TaurBackend::search_pac(std::string_view query) +{ // we search for the package name and print only the name, not the description - alpm_list_t *syncdbs = config.repos; - alpm_db_t *localdb = alpm_get_localdb(config.handle); + alpm_list_t* syncdbs = config.repos; + alpm_db_t* localdb = alpm_get_localdb(config.handle); alpm_list_smart_pointer packages(nullptr, alpm_list_free); - alpm_list_smart_pointer query_regex(alpm_list_add(nullptr, (void *)query.data()), alpm_list_free); + alpm_list_smart_pointer query_regex(alpm_list_add(nullptr, (void*)query.data()), alpm_list_free); - for (; syncdbs; syncdbs = alpm_list_next(syncdbs)) { - alpm_list_t *ret = nullptr; - if (alpm_db_search((alpm_db_t *)(syncdbs->data), query_regex.get(), &ret) == 0) { - for (alpm_list_t *retClone = ret; retClone; retClone = retClone->next) { - alpm_list_t *packages_get = packages.get(); + for (; syncdbs; syncdbs = alpm_list_next(syncdbs)) + { + alpm_list_t* ret = nullptr; + if (alpm_db_search((alpm_db_t*)(syncdbs->data), query_regex.get(), &ret) == 0) + { + for (alpm_list_t* retClone = ret; retClone; retClone = retClone->next) + { + alpm_list_t* packages_get = packages.get(); (void)packages.release(); - packages = make_list_smart_pointer(alpm_list_add(packages_get, (alpm_pkg_t *)(retClone->data))); + packages = make_list_smart_pointer(alpm_list_add(packages_get, (alpm_pkg_t*)(retClone->data))); } } } - vector out; - - for (alpm_list_t *packages_get = packages.get(); packages_get; packages_get = packages_get->next) { - alpm_pkg_t *pkg = (alpm_pkg_t *)(packages_get->data); + std::vector out; - out.push_back({.name = alpm_pkg_get_name(pkg), - .version = alpm_pkg_get_version(pkg), - .desc = alpm_pkg_get_desc(pkg), - .last_modified = alpm_pkg_get_builddate(pkg), - .votes = -1, - .installed = alpm_db_get_pkg(localdb, alpm_pkg_get_name(pkg)) != nullptr, - .db_name = alpm_db_get_name(alpm_pkg_get_db(pkg))}); + for (alpm_list_t* packages_get = packages.get(); packages_get; packages_get = packages_get->next) + { + alpm_pkg_t* pkg = (alpm_pkg_t*)(packages_get->data); + + out.push_back({ .name = alpm_pkg_get_name(pkg), + .version = alpm_pkg_get_version(pkg), + .desc = alpm_pkg_get_desc(pkg), + .last_modified = alpm_pkg_get_builddate(pkg), + .votes = -1, + .installed = alpm_db_get_pkg(localdb, alpm_pkg_get_name(pkg)) != nullptr, + .db_name = alpm_db_get_name(alpm_pkg_get_db(pkg)) }); } return out; @@ -620,25 +735,27 @@ vector TaurBackend::search_pac(string_view query) { // Returns an optional that is empty if an error occurs // status will be set to -1 in the case of an error as well. -vector TaurBackend::search(string_view query, bool useGit, bool aurOnly, bool checkExactMatch) { +std::vector TaurBackend::search(std::string_view query, bool useGit, bool aurOnly, bool checkExactMatch) +{ if (query.empty()) - return vector(); + return std::vector(); // link to AUR API. Took search pattern from yay - cpr::Url url(("https://aur.archlinux.org/rpc?arg%5B%5D=" + cpr::util::urlEncode(query.data()) + "&by=" + config.getConfigValue("searchBy", "name-desc") + "&type=search&v=5")); + cpr::Url url(("https://aur.archlinux.org/rpc?arg%5B%5D=" + cpr::util::urlEncode(query.data()) + + "&by=" + config.getConfigValue("searchBy", "name-desc") + "&type=search&v=5")); log_println(DEBUG, _("url search = {}"), url.str()); - cpr::Response r = cpr::Get(url); + cpr::Response r = cpr::Get(url); - string_view raw_text_response = r.text; + std::string_view raw_text_response = r.text; rapidjson::Document json_response; json_response.Parse(raw_text_response.data()); - vector aurPkgs; - vector pacPkgs; + std::vector aurPkgs; + std::vector pacPkgs; if (json_response["resultcount"].GetInt() > 0) aurPkgs = this->getPkgFromJson(json_response, useGit); - if (string_view(json_response["type"].GetString(), json_response["type"].GetStringLength()) == "error") + if (std::string_view(json_response["type"].GetString(), json_response["type"].GetStringLength()) == "error") log_println(ERROR, "AUR Search error: {}", json_response["error"].GetString()); if (!aurOnly) @@ -646,7 +763,7 @@ vector TaurBackend::search(string_view query, bool useGit, bool aurOn size_t allPkgsSize = aurPkgs.size() + pacPkgs.size(); - vector combined; + std::vector combined; combined.reserve(allPkgsSize); @@ -655,12 +772,12 @@ vector TaurBackend::search(string_view query, bool useGit, bool aurOn if (!pacPkgs.empty()) combined.insert(combined.end(), pacPkgs.begin(), pacPkgs.end()); - if (!checkExactMatch) // caller doesn't want us to check for an exact match. + if (!checkExactMatch) // caller doesn't want us to check for an exact match. return combined; for (size_t i = 0; i < combined.size(); i++) if (combined[i].name == query) - return {combined[i]}; // return the exact match only. + return { combined[i] }; // return the exact match only. return combined; } diff --git a/src/util.cpp b/src/util.cpp index 520c1f8..05dbd56 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -18,27 +18,31 @@ */ #include +#include +#include #pragma GCC diagnostic ignored "-Wignored-attributes" -#include "util.hpp" #include "config.hpp" +#include "switch_fnv1a.hpp" #include "pacman.hpp" #include "taur.hpp" +#include "util.hpp" /** Build the `titles` array of localized titles and pad them with spaces so - * that they align with the longest title. Storage for strings is stack + * that they align with the longest title. Storage for std::strings is stack * allocated and naively truncated to TITLE_MAXLEN characters. */ // function from pacman code for aligning colonums -static void make_aligned_titles(void) { +static void make_aligned_titles(void) +{ unsigned int i; size_t maxlen = 0; int maxcol = 0; static const wchar_t title_suffix[] = L" :"; - wchar_t wbuf[ARRAYSIZE(titles)][TITLE_MAXLEN + ARRAYSIZE(title_suffix)] = {{0}}; + wchar_t wbuf[ARRAYSIZE(titles)][TITLE_MAXLEN + ARRAYSIZE(title_suffix)] = { { 0 } }; size_t wlen[ARRAYSIZE(wbuf)]; int wcol[ARRAYSIZE(wbuf)]; - char *buf[ARRAYSIZE(wbuf)]; + char* buf[ARRAYSIZE(wbuf)]; buf[T_ARCHITECTURE] = _("Architecture"); buf[T_BACKUP_FILES] = _("Backup Files"); buf[T_BUILD_DATE] = _("Build Date"); @@ -68,18 +72,22 @@ static void make_aligned_titles(void) { buf[T_VALIDATED_BY] = _("Validated By"); buf[T_VERSION] = _("Version"); - for (i = 0; i < ARRAYSIZE(wbuf); i++) { + for (i = 0; i < ARRAYSIZE(wbuf); i++) + { wlen[i] = mbstowcs(wbuf[i], buf[i], strlen(buf[i]) + 1); wcol[i] = wcswidth(wbuf[i], wlen[i]); - if (wcol[i] > maxcol) { + if (wcol[i] > maxcol) + { maxcol = wcol[i]; } - if (wlen[i] > maxlen) { + if (wlen[i] > maxlen) + { maxlen = wlen[i]; } } - for (i = 0; i < ARRAYSIZE(wbuf); i++) { + for (i = 0; i < ARRAYSIZE(wbuf); i++) + { size_t padlen = maxcol - wcol[i]; wmemset(wbuf[i] + wlen[i], L' ', padlen); wmemcpy(wbuf[i] + wlen[i] + padlen, title_suffix, ARRAYSIZE(title_suffix)); @@ -91,16 +99,20 @@ static void make_aligned_titles(void) { * @param optdeps a list with items of type alpm_depend_t */ // taken from pacman -static void optdeplist_display(alpm_pkg_t *pkg, unsigned short cols = getcols()) { +static void optdeplist_display(alpm_pkg_t* pkg, unsigned short cols = getcols()) +{ alpm_list_t *i, *text = NULL; - alpm_db_t *localdb = alpm_get_localdb(config->handle); - for (i = alpm_pkg_get_optdepends(pkg); i; i = alpm_list_next(i)) { - alpm_depend_t *optdep = (alpm_depend_t *)i->data; - char *depstring = alpm_dep_compute_string(optdep); - if (alpm_pkg_get_origin(pkg) == ALPM_PKG_FROM_LOCALDB) { - if (alpm_find_satisfier(alpm_db_get_pkgcache(localdb), depstring)) { - const char *installed = _(" [installed]"); - depstring = (char *)realloc(depstring, strlen(depstring) + strlen(installed) + 1); + alpm_db_t* localdb = alpm_get_localdb(config->handle); + for (i = alpm_pkg_get_optdepends(pkg); i; i = alpm_list_next(i)) + { + alpm_depend_t* optdep = (alpm_depend_t*)i->data; + char* depstring = alpm_dep_compute_string(optdep); + if (alpm_pkg_get_origin(pkg) == ALPM_PKG_FROM_LOCALDB) + { + if (alpm_find_satisfier(alpm_db_get_pkgcache(localdb), depstring)) + { + const char* installed = _(" [installed]"); + depstring = (char*)realloc(depstring, strlen(depstring) + strlen(installed) + 1); strcpy(depstring + strlen(depstring), installed); } } @@ -110,71 +122,77 @@ static void optdeplist_display(alpm_pkg_t *pkg, unsigned short cols = getcols()) FREELIST(text); } -// https://stackoverflow.com/questions/874134/find-out-if-string-ends-with-another-string-in-c#874160 -bool hasEnding(string_view fullString, string_view ending) { +// https://stackoverflow.com/questions/874134/find-out-if-std::string-ends-with-another-std::string-in-c#874160 +bool hasEnding(std::string_view fullString, std::string_view ending) +{ if (ending.length() > fullString.length()) return false; return (0 == fullString.compare(fullString.length() - ending.length(), ending.length(), ending)); } -bool hasStart(string_view fullString, string_view start) { +bool hasStart(std::string_view fullString, std::string_view start) +{ if (start.length() > fullString.length()) return false; return (0 == fullString.compare(0, start.length(), start)); } -bool isInvalid(char c) { - return !isprint(c); -} +// clang-format off +bool isInvalid(char c) +{ return !isprint(c); } -void sanitizeStr(string& str) { - str.erase(std::remove_if(str.begin(), str.end(), isInvalid), str.end()); -} +void sanitizeStr(std::string& str) +{ str.erase(std::remove_if(str.begin(), str.end(), isInvalid), str.end()); } -/** Print some text after hitting CTRL-C. +/** Print some text after hitting CTRL-C. * Used only in main() for signal() */ -void interruptHandler(int) { - die(_("Caught CTRL-C, Exiting!")); -} +void interruptHandler(int) +{ die(_("Caught CTRL-C, Exiting!")); } +// clang-format on /** Function to check if a package is from a synchronization database * Basically if it's in pacman repos like core, extra, multilib, etc. * @param name The package name to check * @param syncdbs * @return true if the pkg exists, else false */ -bool is_package_from_syncdb(const char *name, alpm_list_t *syncdbs) { +bool is_package_from_syncdb(const char* name, alpm_list_t* syncdbs) +{ for (; syncdbs; syncdbs = alpm_list_next(syncdbs)) - if (alpm_db_get_pkg((alpm_db_t *)(syncdbs->data), name)) + if (alpm_db_get_pkg((alpm_db_t*)(syncdbs->data), name)) return true; return false; } // soft means it won't return false (or even try) if the list is empty -bool commitTransactionAndRelease(bool soft) { - alpm_handle_t *handle = config->handle; +bool commitTransactionAndRelease(bool soft) +{ + alpm_handle_t* handle = config->handle; - alpm_list_t *addPkgs = alpm_trans_get_add(handle); - alpm_list_t *removePkgs = alpm_trans_get_remove(handle); - alpm_list_t *data = nullptr; + alpm_list_t* addPkgs = alpm_trans_get_add(handle); + alpm_list_t* removePkgs = alpm_trans_get_remove(handle); + alpm_list_t* data = nullptr; if (soft && !addPkgs && !removePkgs) return true; log_println(INFO, _("Changes to be made:")); - for (alpm_list_t *addPkgsClone = addPkgs; addPkgsClone; addPkgsClone = addPkgsClone->next) { - fmt::print(BOLD_TEXT(color.green), " ++ "); - fmt::println(fmt::emphasis::bold, "{}", alpm_pkg_get_name((alpm_pkg_t *)(addPkgsClone->data))); + for (alpm_list_t* addPkgsClone = addPkgs; addPkgsClone; addPkgsClone = addPkgsClone->next) + { + fmt::print(BOLD_COLOR(color.green), " ++ "); + fmt::println(fmt::emphasis::bold, "{}", alpm_pkg_get_name((alpm_pkg_t*)(addPkgsClone->data))); } - for (alpm_list_t *removePkgsClone = removePkgs; removePkgsClone; removePkgsClone = removePkgsClone->next) { - fmt::print(BOLD_TEXT(color.red), " -- "); - fmt::println(fmt::emphasis::bold, "{}", alpm_pkg_get_name((alpm_pkg_t *)(removePkgsClone->data))); + for (alpm_list_t* removePkgsClone = removePkgs; removePkgsClone; removePkgsClone = removePkgsClone->next) + { + fmt::print(BOLD_COLOR(color.red), " -- "); + fmt::println(fmt::emphasis::bold, "{}", alpm_pkg_get_name((alpm_pkg_t*)(removePkgsClone->data))); } - if (!askUserYorN(true, PROMPT_YN_PROCEED_TRANSACTION)) { + if (!askUserYorN(true, PROMPT_YN_PROCEED_TRANSACTION)) + { bool releaseStatus = alpm_trans_release(handle) == 0; if (!releaseStatus) log_println(ERROR, _("Failed to release transaction ({})."), alpm_strerror(alpm_errno(handle))); @@ -184,99 +202,102 @@ bool commitTransactionAndRelease(bool soft) { } bool prepareStatus = alpm_trans_prepare(handle, &data) == 0; - if (!prepareStatus) { + if (!prepareStatus) + { alpm_errno_t err = alpm_errno(handle); log_println(ERROR, _("Failed to prepare transaction ({})."), alpm_strerror(err)); /* TODO: we can use the errno and (data) to list off more precise information about the error. */ - alpm_pkg_t *pkg = nullptr; - switch (err) { - case ALPM_ERR_PKG_INVALID_ARCH: - for (alpm_list_t *i = data; i; i = i->next) { - string_view pkgName = (char *)(i->data); - log_println(ERROR, "This package ({}) is built on an invalid architecture.", pkgName); - free(i->data); - } - break; - case ALPM_ERR_UNSATISFIED_DEPS: - for (alpm_list_t *i = data; i; i = i->next) { - alpm_depmissing_t *missing_dep = (alpm_depmissing_t *)i->data; - - char *depstring = alpm_dep_compute_string(missing_dep->depend); - if (missing_dep->causingpkg == NULL) { - log_println(ERROR, "Package {} depends on '{}', which can't be satisfied.", - missing_dep->target, - depstring - ); - } else if ((pkg = alpm_pkg_find(addPkgs, missing_dep->causingpkg))) { - /* we're trying to upgrade a package that breaks another dependency. */ - log_println(ERROR, "Upgrading package {} (version {}) breaks dependency '{}' for {}", - alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg), - depstring, missing_dep->target); - } else { - /* we're trying to remove a package that breaks another dependency. */ - log_println(ERROR, "Removing package {} breaks dependency '{}' for {}.", - missing_dep->causingpkg, depstring, missing_dep->target - ); + alpm_pkg_t* pkg = nullptr; + switch (err) + { + case ALPM_ERR_PKG_INVALID_ARCH: + for (alpm_list_t* i = data; i; i = i->next) + { + std::string_view pkgName = (char*)(i->data); + log_println(ERROR, "This package ({}) is built on an invalid architecture.", pkgName); + free(i->data); } - free(depstring); + break; + case ALPM_ERR_UNSATISFIED_DEPS: + for (alpm_list_t* i = data; i; i = i->next) + { + alpm_depmissing_t* missing_dep = (alpm_depmissing_t*)i->data; + + char* depstring = alpm_dep_compute_string(missing_dep->depend); + if (missing_dep->causingpkg == NULL) + { + log_println(ERROR, "Package {} depends on '{}', which can't be satisfied.", missing_dep->target, + depstring); + } + else if ((pkg = alpm_pkg_find(addPkgs, missing_dep->causingpkg))) + { + /* we're trying to upgrade a package that breaks another dependency. */ + log_println(ERROR, "Upgrading package {} (version {}) breaks dependency '{}' for {}", + alpm_pkg_get_name(pkg), alpm_pkg_get_version(pkg), depstring, missing_dep->target); + } + else + { + /* we're trying to remove a package that breaks another dependency. */ + log_println(ERROR, "Removing package {} breaks dependency '{}' for {}.", + missing_dep->causingpkg, depstring, missing_dep->target); + } + free(depstring); - alpm_depmissing_free(missing_dep); - } - break; - case ALPM_ERR_CONFLICTING_DEPS: - for (alpm_list_t *i = data; i; i = i->next) { - alpm_conflict_t *conflict = (alpm_conflict_t *)i->data; - if (conflict->reason->mod == ALPM_DEP_MOD_ANY) { - log_println(ERROR, "Packages {} (version {}) and {} (version {}) conflict eachother.", - alpm_pkg_get_name(conflict->package1), - alpm_pkg_get_version(conflict->package1), - alpm_pkg_get_name(conflict->package2), - alpm_pkg_get_version(conflict->package2) - ); - } else { - char *reason = alpm_dep_compute_string(conflict->reason); - log_println(ERROR, "Packages {} (version {}) and {} (version {}) conflict eachother. ({})", - alpm_pkg_get_name(conflict->package1), - alpm_pkg_get_version(conflict->package1), - alpm_pkg_get_name(conflict->package2), - alpm_pkg_get_version(conflict->package2), - reason - ); - free(reason); + alpm_depmissing_free(missing_dep); } - alpm_conflict_free(conflict); - } - break; - default: - break; + break; + case ALPM_ERR_CONFLICTING_DEPS: + for (alpm_list_t* i = data; i; i = i->next) + { + alpm_conflict_t* conflict = (alpm_conflict_t*)i->data; + if (conflict->reason->mod == ALPM_DEP_MOD_ANY) + { + log_println(ERROR, "Packages {} (version {}) and {} (version {}) conflict eachother.", + alpm_pkg_get_name(conflict->package1), alpm_pkg_get_version(conflict->package1), + alpm_pkg_get_name(conflict->package2), alpm_pkg_get_version(conflict->package2)); + } + else + { + char* reason = alpm_dep_compute_string(conflict->reason); + log_println(ERROR, "Packages {} (version {}) and {} (version {}) conflict eachother. ({})", + alpm_pkg_get_name(conflict->package1), alpm_pkg_get_version(conflict->package1), + alpm_pkg_get_name(conflict->package2), alpm_pkg_get_version(conflict->package2), + reason); + free(reason); + } + alpm_conflict_free(conflict); + } + break; + default: break; } } bool commitStatus = alpm_trans_commit(handle, &data) == 0; - if (!commitStatus) { + if (!commitStatus) + { alpm_errno_t err = alpm_errno(handle); log_println(ERROR, _("Failed to commit transaction ({})."), alpm_strerror(err)); /* similarly. */ - switch (err) { + switch (err) + { case ALPM_ERR_FILE_CONFLICTS: - for (alpm_list_t *i = 0; i; i = i->next) { - alpm_fileconflict_t *fileConflict = (alpm_fileconflict_t *)i->data; - switch (fileConflict->type) { + for (alpm_list_t* i = 0; i; i = i->next) + { + alpm_fileconflict_t* fileConflict = (alpm_fileconflict_t*)i->data; + switch (fileConflict->type) + { /* Two packages are trying to install the same file. */ case ALPM_FILECONFLICT_TARGET: log_println(ERROR, "Packages {} and {} are trying to install the same file!", - fileConflict->target, fileConflict->ctarget - ); + fileConflict->target, fileConflict->ctarget); /* A package is trying to overwrite a file (maybe installed by another package) */ case ALPM_FILECONFLICT_FILESYSTEM: if (fileConflict->ctarget) log_println(ERROR, "Package {} is trying to install a file ({}) that is owned by {}!", - fileConflict->target, fileConflict->file, fileConflict->ctarget - ); + fileConflict->target, fileConflict->file, fileConflict->ctarget); else log_println(ERROR, "Package {} is trying to install a file ({}) that already exists!", - fileConflict->target, fileConflict->file - ); + fileConflict->target, fileConflict->file); } alpm_fileconflict_free(fileConflict); } @@ -284,14 +305,14 @@ bool commitTransactionAndRelease(bool soft) { // We don't have to catch this as ALPM_ERR_PKG_INVALID_SIG also applies. case ALPM_ERR_PKG_INVALID_CHECKSUM: case ALPM_ERR_PKG_INVALID_SIG: - for (alpm_list_t *i = data; i; i = i->next) { - string_view name = (char *)i->data; + for (alpm_list_t* i = data; i; i = i->next) + { + std::string_view name = (char*)i->data; log_println(ERROR, "Package {} is corrupt or invalid!", name); free(i->data); } break; - default: - break; + default: break; } } @@ -299,7 +320,8 @@ bool commitTransactionAndRelease(bool soft) { if (!releaseStatus) log_println(ERROR, _("Failed to release transaction ({})."), alpm_strerror(alpm_errno(handle))); - if (prepareStatus && commitStatus && releaseStatus) { + if (prepareStatus && commitStatus && releaseStatus) + { log_println(INFO, _("Successfully finished transaction.")); return true; } @@ -308,32 +330,37 @@ bool commitTransactionAndRelease(bool soft) { } /** Replace special symbols such as ~ and $ in std::strings - * @param str The string - * @return The modified string + * @param str The std::string + * @return The modified std::string */ -string expandVar(string& str) { - const char *env; - if (str[0] == '~') { +std::string expandVar(std::string& str) +{ + const char* env; + if (str[0] == '~') + { env = getenv("HOME"); if (env == nullptr) die(_("FATAL: $HOME enviroment variable is not set (how?)")); - - str.replace(0, 1, env); // replace ~ with the $HOME value - } else if (str[0] == '$') { - str.erase(0, 1); // erase from str[0] to str[1] + + str.replace(0, 1, env); // replace ~ with the $HOME value + } + else if (str[0] == '$') + { + str.erase(0, 1); // erase from str[0] to str[1] env = getenv(str.c_str()); if (env == nullptr) die(_("No such enviroment variable: {}"), str); - + str = env; } return str; } -fmt::rgb hexStringToColor(string_view hexstr) { +fmt::rgb hexStringToColor(std::string_view hexstr) +{ hexstr = hexstr.substr(1); - // convert the hexadecimal string to individual components + // convert the hexadecimal std::string to individual components std::stringstream ss; ss << std::hex << hexstr; @@ -348,9 +375,10 @@ fmt::rgb hexStringToColor(string_view hexstr) { } // http://stackoverflow.com/questions/478898/ddg#478960 -string shell_exec(string_view cmd) { - std::array buffer; - string result; +std::string shell_exec(std::string_view cmd) +{ + std::array buffer; + std::string result; std::unique_ptr pipe(popen(cmd.data(), "r"), pclose); if (!pipe) @@ -365,15 +393,19 @@ string shell_exec(string_view cmd) { return result; } -// https://stackoverflow.com/questions/4654636/how-to-determine-if-a-string-is-a-number-with-c#4654718 -bool is_numerical(string_view s, bool allowSpace) { +// https://stackoverflow.com/questions/4654636/how-to-determine-if-a-std::string-is-a-number-with-c#4654718 +bool is_numerical(std::string_view s, bool allowSpace) +{ if (allowSpace) - return !s.empty() && std::find_if(s.begin(), s.end(), [](unsigned char c) { return (!std::isdigit(c) && (c != ' ')); }) == s.end(); + return !s.empty() && std::find_if(s.begin(), s.end(), + [](unsigned char c) { return (!std::isdigit(c) && (c != ' ')); }) == s.end(); else - return !s.empty() && std::find_if(s.begin(), s.end(), [](unsigned char c) { return (!std::isdigit(c)); }) == s.end(); + return !s.empty() && + std::find_if(s.begin(), s.end(), [](unsigned char c) { return (!std::isdigit(c)); }) == s.end(); } -bool taur_read_exec(vector cmd, string& output, bool exitOnFailure) { +bool taur_read_exec(std::vector cmd, std::string& output, bool exitOnFailure) +{ int pipeout[2]; if (pipe(pipeout) < 0) @@ -381,38 +413,47 @@ bool taur_read_exec(vector cmd, string& output, bool exitOnFailure int pid = fork(); - if (pid > 0) { // we wait for the command to finish then start executing the rest + if (pid > 0) + { // we wait for the command to finish then start executing the rest close(pipeout[1]); int status; - waitpid(pid, &status, 0); // Wait for the child to finish + waitpid(pid, &status, 0); // Wait for the child to finish - if (WIFEXITED(status) && WEXITSTATUS(status) == 0) { + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) + { // read stdout char c; - while (read(pipeout[0], &c, 1) == 1) { + while (read(pipeout[0], &c, 1) == 1) + { output += c; } close(pipeout[0]); return true; - } else { + } + else + { log_println(ERROR, _("Failed to execute the command: {}"), fmt::join(cmd, " ")); if (exitOnFailure) exit(-1); } - } else if (pid == 0) { + } + else if (pid == 0) + { if (dup2(pipeout[1], STDOUT_FILENO) == -1) exit(1); close(pipeout[0]); close(pipeout[1]); cmd.push_back(nullptr); - execvp(cmd[0], const_cast(cmd.data())); + execvp(cmd[0], const_cast(cmd.data())); die(_("An error has occurred: {}"), strerror(errno)); - } else { + } + else + { close(pipeout[0]); close(pipeout[1]); die(_("fork() failed: {}"), strerror(errno)); @@ -425,36 +466,41 @@ bool taur_read_exec(vector cmd, string& output, bool exitOnFailure } /** Executes commands with execvp() and keep the program running without existing - * @param cmd_str The command to execute + * @param cmd_str The command to execute * @param exitOnFailure Whether to call exit(1) on command failure. - * @return true if the command successed, else false + * @return true if the command successed, else false */ -bool taur_exec(vector cmd_str, bool exitOnFailure) { - - vector cmd; - for (string& str : cmd_str) +bool taur_exec(std::vector cmd_str, bool exitOnFailure) +{ + std::vector cmd; + for (std::string& str : cmd_str) cmd.push_back(str.c_str()); int pid = fork(); - if (pid < 0) { + if (pid < 0) + { die(_("fork() failed: {}"), strerror(errno)); } - if (pid == 0) { + if (pid == 0) + { log_println(DEBUG, "running {}", cmd); cmd.push_back(nullptr); - execvp(cmd[0], const_cast(cmd.data())); + execvp(cmd[0], const_cast(cmd.data())); // execvp() returns instead of exiting when failed die(_("An error has occurred: {}: {}"), cmd[0], strerror(errno)); - } else if (pid > 0) { // we wait for the command to finish then start executing the rest + } + else if (pid > 0) + { // we wait for the command to finish then start executing the rest int status; - waitpid(pid, &status, 0); // Wait for the child to finish + waitpid(pid, &status, 0); // Wait for the child to finish if (WIFEXITED(status) && WEXITSTATUS(status) == 0) return true; - else { + else + { log_println(ERROR, _("Failed to execute the command: {}"), fmt::join(cmd, " ")); if (exitOnFailure) exit(1); @@ -465,12 +511,13 @@ bool taur_exec(vector cmd_str, bool exitOnFailure) { } /** Convinient way to executes makepkg commands with taur_exec() and keep the program running without existing - * @param cmd The command to execute + * @param cmd The command to execute * @param exitOnFailure Whether to call exit(1) on command failure. - * @return true if the command successed, else false + * @return true if the command successed, else false */ -bool makepkg_exec(vector const& args, bool exitOnFailure) { - vector cmd = {config->makepkgBin}; +bool makepkg_exec(std::vector const& args, bool exitOnFailure) +{ + std::vector cmd = { config->makepkgBin }; if (config->noconfirm) cmd.push_back("--noconfirm"); @@ -493,15 +540,16 @@ bool makepkg_exec(vector const& args, bool exitOnFailure) { * @param args The packages to be installed * @param exitOnFailure Whether to call exit(1) on command failure. (Default true) * @param root If pacman should be executed as root (Default true) - * @return true if the command successed, else false + * @return true if the command successed, else false */ -bool pacman_exec(string_view op, vector const& args, bool exitOnFailure, bool root) { - vector cmd; +bool pacman_exec(std::string_view op, std::vector const& args, bool exitOnFailure, bool root) +{ + std::vector cmd; if (root) - cmd = {config->sudo, "pacman", op.data()}; + cmd = { config->sudo, "pacman", op.data() }; else - cmd = {"pacman", op.data()}; + cmd = { "pacman", op.data() }; if (config->noconfirm) cmd.push_back("--noconfirm"); @@ -522,11 +570,12 @@ bool pacman_exec(string_view op, vector const& args, bool exitOnFailure, } /** Free a list and every single item in it. - * This will attempt to free everything, should be used for strings that contain C strings only. + * This will attempt to free everything, should be used for std::strings that contain C std::strings only. * @param list the list to free -*/ -void free_list_and_internals(alpm_list_t *list) { - for (alpm_list_t *listClone = list; listClone; listClone = listClone->next) + */ +void free_list_and_internals(alpm_list_t* list) +{ + for (alpm_list_t* listClone = list; listClone; listClone = listClone->next) free(listClone->data); alpm_list_free(list); @@ -536,53 +585,59 @@ void free_list_and_internals(alpm_list_t *list) { * @param db_name The database name * @return database's color in bold */ -fmt::text_style getColorFromDBName(string_view db_name) { - if (db_name == "aur") - return BOLD_TEXT(color.aur); - else if (db_name == "extra") - return BOLD_TEXT(color.extra); - else if (db_name == "core") - return BOLD_TEXT(color.core); - else if (db_name == "multilib") - return BOLD_TEXT(color.multilib); - else - return BOLD_TEXT(color.others); +fmt::text_style getColorFromDBName(std::string_view db_name) +{ + switch (fnv1a16::hash(db_name)) + { + case "aur"_fnv1a16: return BOLD_COLOR(color.aur); + case "extra"_fnv1a16: return BOLD_COLOR(color.extra); + case "core"_fnv1a16: return BOLD_COLOR(color.core); + case "multilib"_fnv1a16: return BOLD_COLOR(color.multilib); + } + + return BOLD_COLOR(color.others); } // Takes a pkg to show on search. -void printPkgInfo(TaurPkg_t& pkg, string_view db_name) { +void printPkgInfo(TaurPkg_t& pkg, std::string_view db_name) +{ fmt::print(getColorFromDBName(db_name), "{}/", db_name); fmt::print(BOLD, "{} ", pkg.name); - fmt::print(BOLD_TEXT(color.version), "{} ", pkg.version); + fmt::print(BOLD_COLOR(color.version), "{} ", pkg.version); // Don't print popularity and votes on system packages - if (pkg.votes > -1) { + if (pkg.votes > -1) + { fmt::print(fg(color.popularity), " Popularity: {:.2f} ", pkg.popularity); fmt::print(fg(color.votes), "Votes: {} ({}) ", pkg.votes, getTitleFromVotes(pkg.votes)); } if (pkg.maintainer == "\1") - fmt::print(BOLD_TEXT(color.orphan), "(un-maintained) "); - - if (pkg.outofdate) { - char *timestr = std::ctime(&pkg.outofdate); - string_view timestr_view = timestr; - if (!timestr_view.empty()) { - timestr[timestr_view.length() - 1] = '\0'; // delete the last newline. - fmt::print(BOLD_TEXT(color.outofdate), "(Outdated: {}) ", timestr); + fmt::print(BOLD_COLOR(color.orphan), "(un-maintained) "); + + if (pkg.outofdate) + { + char* timestr = std::ctime(&pkg.outofdate); + std::string_view timestr_view = timestr; + if (!timestr_view.empty()) + { + timestr[timestr_view.length() - 1] = '\0'; // delete the last newline. + fmt::print(BOLD_COLOR(color.outofdate), "(Outdated: {}) ", timestr); } } if (pkg.installed) - fmt::println(BOLD_TEXT(color.installed), "[Installed]"); + fmt::println(BOLD_COLOR(color.installed), "[Installed]"); else fmt::print("\n"); fmt::println(" {}", pkg.desc); } -void printLocalFullPkgInfo(alpm_pkg_t *pkg) { +void printLocalFullPkgInfo(alpm_pkg_t* pkg) +{ /* make aligned titles once only */ static int need_alignment = 1; - if (need_alignment) { + if (need_alignment) + { need_alignment = 0; make_aligned_titles(); } @@ -601,13 +656,56 @@ void printLocalFullPkgInfo(alpm_pkg_t *pkg) { fmt::print("\n"); } -// faster than makepkg --packagelist -string makepkg_list(string_view pkg_name, string path) { - string ret; +/* Get file value from a file and trim quotes and double-quotes + * @param iterIndex The iteration index used for getting the necessary value only tot times + * @param line The string used in std::getline + * @param str The string to assign the trimmed value, inline + * @param amount The amount to be used in the line.substr() (should be used with something like "foobar"_len) + */ +void getFileValue(u_short& iterIndex, const std::string& line, std::string& str, const size_t& amount) +{ + str = line.substr(amount); + str.erase(std::remove(str.begin(), str.end(), '\"'), str.end()); + str.erase(std::remove(str.begin(), str.end(), '\''), str.end()); + iterIndex++; +} - string versionInfo = shell_exec("grep 'pkgver=' " + path + "/PKGBUILD | cut -d= -f2 | cut -d' ' -f1"); - string pkgrel = shell_exec("grep 'pkgrel=' " + path + "/PKGBUILD | cut -d= -f2 | cut -d' ' -f1"); - string epoch = shell_exec("grep 'epoch=' " + path + "/PKGBUILD | cut -d= -f2 | cut -d' ' -f1"); +// faster than `makepkg --packagelist` +std::string makepkg_list(std::string_view pkg_name, std::string_view path) +{ + std::ifstream pkgbuild_f(fmt::format("{}/PKGBUILD", path), std::ios::in); + + /*std::string versionInfo = shell_exec("grep 'pkgver=' " + path + + "/PKGBUILD | cut -d= -f2 | cut -d' ' -f1 | sed -e \"s/'//g\" -e 's/\"//g'"); + std::string pkgrel = shell_exec("grep 'pkgrel=' " + path + + "/PKGBUILD | cut -d= -f2 | cut -d' ' -f1 | sed -e \"s/'//g\" -e 's/\"//g'"); + std::string epoch = shell_exec("grep 'epoch=' " + path + + "/PKGBUILD | cut -d= -f2 | cut -d' ' -f1 | sed -e \"s/'//g\" -e 's/\"//g'"); + std::string arch_field + = shell_exec("awk -F '[()]' '/^arch=/ {gsub(/\"/,\"\",$2); print $2}' " + path + + "/PKGBUILD | sed -e \"s/'//g\" -e 's/\"//g'");*/ + + std::string versionInfo, pkgrel, epoch, arch_field; + u_short iterIndex = 0; + + std::string line; + while (std::getline(pkgbuild_f, line) && iterIndex < 4) + { + if (hasStart(line, "pkgver=")) + getFileValue(iterIndex, line, versionInfo, "pkgver="_len); + + else if (hasStart(line, "pkgrel=")) + getFileValue(iterIndex, line, pkgrel, "pkgrel="_len); + + else if (hasStart(line, "epoch=")) + getFileValue(iterIndex, line, epoch, "epoch="_len); + + else if (hasStart(line, "arch=(")) + { + getFileValue(iterIndex, line, arch_field, "arch=("_len); + arch_field.pop_back(); + } + } if (!pkgrel.empty() && pkgrel[0] != '\0') versionInfo += '-' + pkgrel; @@ -615,49 +713,67 @@ string makepkg_list(string_view pkg_name, string path) { if (!epoch.empty() && epoch[0] != '\0') versionInfo = epoch + ':' + versionInfo; - string arch = shell_exec("grep 'CARCH=' " + config->makepkgConf + " | cut -d= -f2 | sed -e \"s/'//g\" -e 's/\"//g'"); - string arch_field = shell_exec("awk -F '[()]' '/^arch=/ {gsub(/\"/,\"\",$2); print $2}' " + path + "/PKGBUILD | sed -e \"s/'//g\" -e 's/\"//g'"); + /*std::string arch = + shell_exec("grep 'CARCH=' " + config->makepkgConf + " | cut -d= -f2 | sed -e \"s/'//g\" -e 's/\"//g'"); + + std::string pkgext = + shell_exec("grep 'PKGEXT=' " + config->makepkgConf + " | cut -d= -f2 | sed -e \"s/'//g\" -e 's/\"//g'");*/ + + std::ifstream makepkgConf(config->makepkgConf, std::ios::in); + std::string arch, pkgext; + iterIndex = 0; + + while (std::getline(makepkgConf, line) && iterIndex < 2) + { + if (hasStart(line, "CARCH=")) + getFileValue(iterIndex, line, arch, "CARCH="_len); + + else if (hasStart(line, "PKGEXT=")) + getFileValue(iterIndex, line, pkgext, "PKGEXT="_len); + } if (arch_field == "any") arch = "any"; - string pkgext = shell_exec("grep 'PKGEXT=' " + config->makepkgConf + " | cut -d= -f2 | sed -e \"s/'//g\" -e 's/\"//g'"); - - ret = fmt::format("{}/{}-{}-{}{}", path, pkg_name, versionInfo, arch, pkgext); - return ret; + return fmt::format("{}/{}-{}-{}{}", path, pkg_name, versionInfo, arch, pkgext); } /** Search a DB using libalpm - * Why? well, alpm_db_search searches for results that match ALL regex patterns, this one searches for results that match ANY regex pattern. -*/ -bool util_db_search(alpm_db_t *db, alpm_list_t *needles, alpm_list_t **ret) { + * Why? well, alpm_db_search searches for results that match ALL regex patterns, this one searches for results that + * match ANY regex pattern. + */ +bool util_db_search(alpm_db_t* db, alpm_list_t* needles, alpm_list_t** ret) +{ if (!db || !needles || *ret != nullptr) return false; - - for (; needles; needles = needles->next) { - alpm_list_t *ret_needle = nullptr; - alpm_list_t *needles_next = needles->next; // save the next value, we will overwrite it. + + for (; needles; needles = needles->next) + { + alpm_list_t* ret_needle = nullptr; + alpm_list_t* needles_next = needles->next; // save the next value, we will overwrite it. needles->next = nullptr; - if (alpm_db_search(db, needles, &ret_needle) != 0) + if (alpm_db_search(db, needles, &ret_needle) != 0) return false; *ret = alpm_list_join(*ret, ret_needle); - needles->next = needles_next; // put it back + needles->next = needles_next; // put it back } return true; } -// Function to perform binary search on a vector of strings -string_view binarySearch(const vector& arr, string_view target) { +// Function to perform binary search on a vector of std::strings +/*std::string_view binarySearch(const std::vector& arr, std::string_view target) +{ int left = 0, right = arr.size() - 1; - - while (left <= right) { + + while (left <= right) + { int mid = left + (right - left) / 2; if (arr[mid] == target) - return arr[mid]; // returns the word if target is found + return arr[mid]; // returns the word if target is found else if (arr[mid] < target) left = mid + 1; else @@ -665,35 +781,41 @@ string_view binarySearch(const vector& arr, string_view target) { } return ""; -} +}*/ -vector load_aur_list() { - path file_path = config->cacheDir / "packages.aur"; +std::vector load_aur_list() +{ + path file_path = config->cacheDir / "packages.aur"; std::ifstream infile(file_path); if (!infile.good()) die(_("Failed to open {}"), file_path.c_str()); - vector aur_list; - string pkg; - - while (infile >> pkg) + std::vector aur_list; + std::string pkg; + + while (std::getline(infile, pkg)) aur_list.push_back(pkg); return aur_list; } -bool download_aur_cache(path file_path) { - cpr::Response r = cpr::Get(cpr::Url{AUR_URL"/packages.gz"}); - - if (r.status_code == 200) { +bool download_aur_cache(path file_path) +{ + cpr::Response r = cpr::Get(cpr::Url{ AUR_URL "/packages.gz" }); + + if (r.status_code == 200) + { std::ofstream outfile(file_path, std::ios::trunc); - if (!outfile.is_open()) { + if (!outfile.is_open()) + { log_println(ERROR, _("Failed to open/write {}"), file_path.c_str()); return false; } outfile << r.text; - } else { - log_println(ERROR, _("Failed to download {} with status code: {}"), r.url.str(), r.status_code); + } + else + { + log_println(ERROR, _("Failed to download {} with status code: {}"), r.url.str(), r.status_code); return false; } @@ -704,12 +826,15 @@ bool download_aur_cache(path file_path) { // This function will automatically try again after downloading the file, if not already present. // Do not call this with true unless you do not want this behavior. // It is, by default, false. -bool update_aur_cache(bool recursiveCall) { +bool update_aur_cache(bool recursiveCall) +{ path file_path = config->cacheDir / "packages.aur"; - + struct stat file_stat; - if (stat(file_path.c_str(), &file_stat) != 0) { - if (errno == ENOENT && !recursiveCall) { // file not found, download THEN try again once more. + if (stat(file_path.c_str(), &file_stat) != 0) + { + if (errno == ENOENT && !recursiveCall) + { // file not found, download THEN try again once more. log_println(INFO, _("File {} not found, attempting download."), file_path.string()); return download_aur_cache(file_path) && update_aur_cache(true); } @@ -718,18 +843,19 @@ bool update_aur_cache(bool recursiveCall) { return false; } - - auto _current_time = std::chrono::system_clock::now(); + + auto _current_time = std::chrono::system_clock::now(); auto _timeoutDuration = std::chrono::hours(24 * 5); - - auto timeout = std::chrono::duration_cast(_timeoutDuration).count(); + + auto timeout = std::chrono::duration_cast(_timeoutDuration).count(); std::time_t now_time_t = std::chrono::system_clock::to_time_t(_current_time); - if (file_stat.st_mtim.tv_sec < now_time_t - timeout) { + if (file_stat.st_mtim.tv_sec < now_time_t - timeout) + { log_println(INFO, _("Refreshing {}"), file_path.string()); download_aur_cache(file_path); } - + return true; } @@ -738,21 +864,29 @@ bool update_aur_cache(bool recursiveCall) { * @param backend A reference to the TaurBackend responsible for fetching any AUR packages. * @param useGit Whether the fetched pkg should use a .git url * @return Optional TaurPkg_t, will not return if interrupted. -*/ -optional> askUserForPkg(vector pkgs, TaurBackend& backend, bool useGit) { - if (pkgs.size() == 1) { - return pkgs[0].aur_url.empty() ? pkgs : vector({backend.fetch_pkg(pkgs[0].name, useGit).value_or(pkgs[0])}); - } else if (pkgs.size() > 1) { + */ +std::optional> askUserForPkg(std::vector pkgs, TaurBackend& backend, bool useGit) +{ + if (pkgs.size() == 1) + { + return pkgs[0].aur_url.empty() + ? pkgs + : std::vector({ backend.fetch_pkg(pkgs[0].name, useGit).value_or(pkgs[0]) }); + } + else if (pkgs.size() > 1) + { log_println(INFO, _("TabAUR has found multiple packages relating to your search query, Please pick one.")); - string input; - do { + std::string input; + do + { // CTRL-D - ctrl_d_handler(); + ctrl_d_handler(std::cin); if (!input.empty()) log_println(WARN, _("Invalid input!")); - for (size_t i = 0; i < pkgs.size(); i++) { + for (size_t i = 0; i < pkgs.size(); i++) + { fmt::print(fg(color.index), "[{}] ", i); printPkgInfo(pkgs[i], pkgs[i].db_name); } @@ -761,18 +895,21 @@ optional> askUserForPkg(vector pkgs, TaurBackend& b std::getline(std::cin, input); } while (!is_numerical(input, true)); - vector indices = split(input, ' '); + std::vector indices = split(input, ' '); - vector output; + std::vector output; output.reserve(indices.size()); - for (size_t i = 0; i < indices.size(); i++) { + for (size_t i = 0; i < indices.size(); i++) + { size_t selected = std::stoi(indices[i]); if (selected >= pkgs.size()) continue; - output.push_back(pkgs[selected].aur_url.empty() ? pkgs[selected] : backend.fetch_pkg(pkgs[selected].name, useGit).value_or(pkgs[selected])); + output.push_back(pkgs[selected].aur_url.empty() + ? pkgs[selected] + : backend.fetch_pkg(pkgs[selected].name, useGit).value_or(pkgs[selected])); } return output; @@ -781,10 +918,10 @@ optional> askUserForPkg(vector pkgs, TaurBackend& b return {}; } -void ctrl_d_handler() { - if (std::cin.eof()) { +void ctrl_d_handler(const std::istream& cin) +{ + if (cin.eof()) die(_("Exiting due to CTRL-D or EOF")); - } } /** Filters out/only AUR packages. @@ -792,14 +929,17 @@ void ctrl_d_handler() { * @param pkgs a unique_ptr to a list of packages to filter. * @param inverse a bool that, if true, will return only AUR packages instead of the other way around. * @return an optional unique_ptr to a result. -*/ -vector filterAURPkgs(vector pkgs, alpm_list_t *syncdbs, bool inverse) { - vector out; + */ +std::vector filterAURPkgs(std::vector pkgs, alpm_list_t* syncdbs, bool inverse) +{ + std::vector out; out.reserve(pkgs.size()); - for (; syncdbs; syncdbs = syncdbs->next) { - for (size_t i = 0; i < pkgs.size(); i++) { - bool existsInSync = alpm_db_get_pkg((alpm_db_t *)(syncdbs->data), alpm_pkg_get_name(pkgs[i])) != nullptr; + for (; syncdbs; syncdbs = syncdbs->next) + { + for (size_t i = 0; i < pkgs.size(); i++) + { + bool existsInSync = alpm_db_get_pkg((alpm_db_t*)(syncdbs->data), alpm_pkg_get_name(pkgs[i])) != nullptr; if ((existsInSync && inverse) || (!existsInSync && !inverse)) pkgs[i] = nullptr; @@ -818,14 +958,17 @@ vector filterAURPkgs(vector pkgs, alpm_list_t *syncd * @param pkgs a unique_ptr to a list of packages to filter. * @param inverse a bool that, if true, will return only AUR packages instead of the other way around. * @return an optional unique_ptr to a result. -*/ -vector filterAURPkgsNames(vector pkgs, alpm_list_t *syncdbs, bool inverse) { - vector out; + */ +std::vector filterAURPkgsNames(std::vector pkgs, alpm_list_t* syncdbs, bool inverse) +{ + std::vector out; out.reserve(pkgs.size()); - for (; syncdbs; syncdbs = syncdbs->next) { - for (size_t i = 0; i < pkgs.size(); i++) { - bool existsInSync = alpm_db_get_pkg((alpm_db_t *)(syncdbs->data), pkgs[i].data()) != nullptr; + for (; syncdbs; syncdbs = syncdbs->next) + { + for (size_t i = 0; i < pkgs.size(); i++) + { + bool existsInSync = alpm_db_get_pkg((alpm_db_t*)(syncdbs->data), pkgs[i].data()) != nullptr; if ((existsInSync && inverse) || (!existsInSync && !inverse)) pkgs[i] = ""; @@ -839,7 +982,8 @@ vector filterAURPkgsNames(vector pkgs, alpm_list_t *sy return out; } -string getTitleFromVotes(float votes) { +std::string getTitleFromVotes(float votes) +{ if (votes < 2) return _("Untrustable"); if (votes < 5) @@ -854,68 +998,76 @@ string getTitleFromVotes(float votes) { } /* -* Get the user cache directory -* either from $XDG_CACHE_HOME or from $HOME/.cache/ -* @return user's cache directory -*/ -string getHomeCacheDir() { - char *dir = getenv("XDG_CACHE_HOME"); - if (dir != NULL && dir[0] != '\0' && std::filesystem::exists(dir)) { - string str_dir(dir); + * Get the user cache directory + * either from $XDG_CACHE_HOME or from $HOME/.cache/ + * @return user's cache directory + */ +std::string getHomeCacheDir() +{ + char* dir = getenv("XDG_CACHE_HOME"); + if (dir != NULL && dir[0] != '\0' && std::filesystem::exists(dir)) + { + std::string str_dir(dir); return hasEnding(str_dir, "/") ? str_dir.substr(0, str_dir.rfind('/')) : str_dir; - } else { - char *home = getenv("HOME"); + } + else + { + char* home = getenv("HOME"); if (home == nullptr) die(_("Failed to find $HOME, set it to your home directory!")); - - return string(home) + "/.cache"; + + return std::string(home) + "/.cache"; } } /* -* Get the user config directory -* either from $XDG_CONFIG_HOME or from $HOME/.config/ -* @return user's config directory -*/ -string getHomeConfigDir() { - char *dir = getenv("XDG_CONFIG_HOME"); - if (dir != NULL && dir[0] != '\0' && std::filesystem::exists(dir)) { - string str_dir(dir); + * Get the user config directory + * either from $XDG_CONFIG_HOME or from $HOME/.config/ + * @return user's config directory + */ +std::string getHomeConfigDir() +{ + char* dir = getenv("XDG_CONFIG_HOME"); + if (dir != NULL && dir[0] != '\0' && std::filesystem::exists(dir)) + { + std::string str_dir(dir); return hasEnding(str_dir, "/") ? str_dir.substr(0, str_dir.rfind('/')) : str_dir; - } else { - char *home = getenv("HOME"); + } + else + { + char* home = getenv("HOME"); if (home == nullptr) die(_("Failed to find $HOME, set it to your home directory!")); - return string(home) + "/.config"; + return std::string(home) + "/.config"; } } /* - * Get the TabAUR config directory + * Get the TabAUR config directory * where we'll have both "config.toml" and "theme.toml" * from Config::getHomeConfigDir() * @return TabAUR's config directory */ -string getConfigDir() { - return getHomeConfigDir() + "/TabAUR"; -} +std::string getConfigDir() +{ return getHomeConfigDir() + "/TabAUR"; } /* - * Get the TabAUR cache directory + * Get the TabAUR cache directory * where we'll have all AUR packages downloaded here * from Config::getHomeCacheDir() * @return TabAUR's cache directory */ -string getCacheDir() { - return getHomeCacheDir() + "/TabAUR"; -} - -vector split(string_view text, char delim) { - string line; - vector vec; - std::stringstream ss(text.data()); - while (std::getline(ss, line, delim)) { +std::string getCacheDir() +{ return getHomeCacheDir() + "/TabAUR"; } + +std::vector split(std::string_view text, char delim) +{ + std::string line; + std::vector vec; + std::stringstream ss(text.data()); + while (std::getline(ss, line, delim)) + { vec.push_back(line); } return vec;