aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt2
-rw-r--r--nihil.cli/CMakeLists.txt2
-rw-r--r--nihil.cli/command.ccm17
-rw-r--r--nihil.cli/command_tree.ccm2
-rw-r--r--nihil.cli/dispatch_command.cc15
-rw-r--r--nihil.cli/registry.cc43
-rw-r--r--nihil.cli/registry.ccm27
-rw-r--r--nihil.flagset/flagset.ccm107
-rw-r--r--nihil.posix/CMakeLists.txt1
-rw-r--r--nihil.posix/execv.ccm6
-rw-r--r--nihil.posix/open.ccm16
-rw-r--r--nihil.posix/posix.ccm1
-rw-r--r--nihil.posix/progname.ccm68
-rw-r--r--nihil.std/nihil.std.ccm32
14 files changed, 206 insertions, 133 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index c51c2aa..a2c9187 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,7 +18,7 @@ find_package(PkgConfig REQUIRED)
# clang-tidy support
find_program(CLANG_TIDY clang-tidy)
-if(NOT (CLANG_TIDY STREQUAL ""))
+if (NOT (CLANG_TIDY STREQUAL "CLANG_TIDY-NOTFOUND"))
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
file(GLOB_RECURSE NIHIL_SOURCES "*.cc" "*.ccm")
diff --git a/nihil.cli/CMakeLists.txt b/nihil.cli/CMakeLists.txt
index 091c34f..859fea5 100644
--- a/nihil.cli/CMakeLists.txt
+++ b/nihil.cli/CMakeLists.txt
@@ -4,6 +4,7 @@ add_library(nihil.cli STATIC)
target_link_libraries(nihil.cli PRIVATE
nihil.std
nihil.generator
+ nihil.posix;
nihil.util
)
target_sources(nihil.cli
@@ -18,7 +19,6 @@ target_sources(nihil.cli
PRIVATE
dispatch_command.cc
- registry.cc
)
if(NIHIL_TESTS)
diff --git a/nihil.cli/command.ccm b/nihil.cli/command.ccm
index dc3f29a..ea52bbf 100644
--- a/nihil.cli/command.ccm
+++ b/nihil.cli/command.ccm
@@ -31,15 +31,24 @@ export struct command final
{
using command_handler_t = std::function<int(int, char **)>;
- command(std::string_view const path, std::string_view const usage, command_handler_t handler) noexcept
- : m_path(path)
+ // Construct a new command with a handler and register it. Since this is usually invoked
+ // at global scope by static object construction, we handle exception internally.
+ command(std::string_view const path, std::string_view const usage, auto &&handler) noexcept
+ try : m_path(path)
, m_usage(usage)
- , m_handler(std::move(handler))
+ , m_handler(std::forward<decltype(handler)>(handler))
{
register_command(this);
+ } catch (std::exception const &exc) {
+ std::println(std::cerr, "register_command(): {}", exc.what());
+ std::quick_exit(1);
+ } catch (...) {
+ std::println(std::cerr, "register_command(): unknown error");
+ std::quick_exit(1);
}
- explicit command(std::string_view const path) noexcept
+ // Create a stub command which doesn't have a handler.
+ explicit command(std::string_view const path)
: m_path(path)
{
}
diff --git a/nihil.cli/command_tree.ccm b/nihil.cli/command_tree.ccm
index 7399357..8ed16c7 100644
--- a/nihil.cli/command_tree.ccm
+++ b/nihil.cli/command_tree.ccm
@@ -166,7 +166,7 @@ private:
{
auto tree = command_tree();
- for (auto &&command : get_registered_commands()) {
+ for (auto &&command : get_registry()) {
// Throws std::logic_error on duplicates.
tree.insert(command);
}
diff --git a/nihil.cli/dispatch_command.cc b/nihil.cli/dispatch_command.cc
index 6e3a757..e9bf779 100644
--- a/nihil.cli/dispatch_command.cc
+++ b/nihil.cli/dispatch_command.cc
@@ -9,6 +9,7 @@ module nihil.cli;
import nihil.std;
import nihil.core;
+import nihil.posix;
namespace nihil {
@@ -17,11 +18,11 @@ namespace {
auto print_commands(command_tree_node const &node) -> void
{
for (auto &&child : node.children())
- std::print(std::cerr, " {}\n", child.command().path());
+ std::println(std::cerr, " {}", child.command().path());
}
} // namespace
-auto dispatch_command(int argc, char **argv) -> int
+auto dispatch_command(int const argc, char **argv) -> int
{
// Reset getopt(3) for the command, in case main() used it already.
optreset = 1;
@@ -35,12 +36,8 @@ auto dispatch_command(int argc, char **argv) -> int
// Set the program name to the existing progname plus the full path to the command being
// invoked; this makes error messages nicer. Save the old progname so we can restore it
// after invoking the command.
- auto const *old_progname = ::getprogname();
-
- {
- auto cprogname = std::format("{} {}", ::getprogname(), command.path());
- ::setprogname(cprogname.c_str());
- }
+ auto progname_guard =
+ setprogname(std::format("{} {}", getprogname().value_or(""), command.path()));
// Invoke the command see what it returns. If it's an exit code, just return it.
// Otherwise, handle the error.
@@ -51,7 +48,7 @@ auto dispatch_command(int argc, char **argv) -> int
auto ret = command.invoke(nrest, argv + (argc - nrest));
// Restore the old progname.
- ::setprogname(old_progname);
+ progname_guard.release();
if (ret)
return *ret;
diff --git a/nihil.cli/registry.cc b/nihil.cli/registry.cc
deleted file mode 100644
index a972a65..0000000
--- a/nihil.cli/registry.cc
+++ /dev/null
@@ -1,43 +0,0 @@
-// This source code is released into the public domain.
-module nihil.cli;
-
-import nihil.std;
-
-namespace nihil {
-
-// Get the registry storage. Because this is called from global ctors,
-// it handles exceptions itself.
-auto get_registry() noexcept -> std::vector<std::shared_ptr<command>> &
-try {
- static auto commands = std::vector<std::shared_ptr<command>>();
- return commands;
-} catch (std::exception const &exc) {
- std::println(std::cerr, "{}", exc.what());
- std::exit(1); // NOLINT
-} catch (...) {
- std::println(std::cerr, "get_registered_commands(): unknown error\n");
- std::exit(1); // NOLINT
-}
-
-// Register a new command.
-auto register_command(command *cmd) noexcept -> void
-try {
- auto null_deleter = [] (command const *) -> void {};
-
- auto &commands = get_registry();
- commands.emplace_back(cmd, null_deleter);
-} catch (std::exception const &exc) {
- std::println(std::cerr, "{}", exc.what());
- std::exit(1); // NOLINT
-} catch (...) {
- std::println(std::cerr, "get_registered_commands(): unknown error\n");
- std::exit(1); // NOLINT
-}
-
-// Get the list of registered commands.
-auto get_registered_commands() -> std::span<std::shared_ptr<command>>
-{
- return {get_registry()};
-}
-
-} // namespace nihil
diff --git a/nihil.cli/registry.ccm b/nihil.cli/registry.ccm
index e5a7e29..5e31195 100644
--- a/nihil.cli/registry.ccm
+++ b/nihil.cli/registry.ccm
@@ -7,11 +7,28 @@ namespace nihil {
export struct command;
-// Register a command. This is guaranteed not to throw; errors will print
-// a diagnostic and exit.
-auto register_command(command *cmd) noexcept -> void;
+///////////////////////////////////////////////////////////////////////
+// Command registry storage. This is where command::command() registers
+// itself when the global command objects are constructed at startup.
+//
+// Because we sometimes create stub commands dynamically, the registry
+// storage is a list of shared_ptr<command>. The "real" commands (which
+// refer to global objects) will be created with a null deleter so they
+// are never deleted. This allows real and stub commands to mix in the
+// same command_tree.
-// Get previously registered commands.
-auto get_registered_commands() -> std::span<std::shared_ptr<command>>;
+// Get the current registry.
+auto get_registry() -> std::vector<std::shared_ptr<command>> &
+{
+ static auto commands = std::vector<std::shared_ptr<command>>();
+ return commands;
+}
+
+// Register a new command.
+auto register_command(command *cmd) noexcept -> void
+{
+ auto constexpr null_deleter = [] (command const *) -> void {};
+ get_registry().emplace_back(cmd, null_deleter);
+}
} // namespace nihil
diff --git a/nihil.flagset/flagset.ccm b/nihil.flagset/flagset.ccm
index 5bd7720..796a2c2 100644
--- a/nihil.flagset/flagset.ccm
+++ b/nihil.flagset/flagset.ccm
@@ -9,8 +9,9 @@ import nihil.std;
namespace nihil {
-export template<std::integral base_type, typename Tag>
-struct flagset final {
+export template <std::integral base_type, typename Tag>
+struct flagset final
+{
using underlying_type = base_type;
/*
@@ -23,12 +24,13 @@ struct flagset final {
*/
flagset(flagset const &other) noexcept
: m_value(other.m_value)
- {}
+ {
+ }
/*
* Create flags from an integer mask.
*/
- template<base_type flag>
+ template <base_type flag>
[[nodiscard]] static constexpr auto mask() noexcept -> flagset
{
return flagset(flag);
@@ -37,7 +39,7 @@ struct flagset final {
/*
* Create flags for a specific bit.
*/
- template<unsigned bitnr>
+ template <unsigned bitnr>
[[nodiscard]] static constexpr auto bit() noexcept -> flagset
{
static_assert(bitnr < std::numeric_limits<base_type>::digits);
@@ -47,8 +49,7 @@ struct flagset final {
/*
* Create flags from a runtime value.
*/
- [[nodiscard]] static auto from_int(base_type value) noexcept
- -> flagset
+ [[nodiscard]] static auto from_int(base_type value) noexcept -> flagset
{
return flagset(value);
}
@@ -56,8 +57,7 @@ struct flagset final {
/*
* Assign this flagset.
*/
- auto operator=(this flagset &lhs, flagset rhs) noexcept
- -> flagset &
+ auto operator=(this flagset &lhs, flagset rhs) noexcept -> flagset &
{
if (&lhs != &rhs)
lhs.m_value = rhs.m_value;
@@ -67,8 +67,7 @@ struct flagset final {
/*
* The integer value of this flagset.
*/
- [[nodiscard]] constexpr auto value(this flagset self) noexcept
- -> base_type
+ [[nodiscard]] constexpr auto value(this flagset self) noexcept -> base_type
{
return self.m_value;
}
@@ -76,8 +75,7 @@ struct flagset final {
/*
* True if this flagset has any bits set.
*/
- [[nodiscard]] explicit constexpr operator bool(this flagset self)
- noexcept
+ [[nodiscard]] explicit constexpr operator bool(this flagset self) noexcept
{
return self.m_value != 0;
}
@@ -85,8 +83,7 @@ struct flagset final {
/*
* Set bits.
*/
- constexpr auto operator|= (this flagset &lhs, flagset rhs) noexcept
- -> flagset &
+ constexpr auto operator|=(this flagset &lhs, flagset rhs) noexcept -> flagset &
{
lhs.m_value |= rhs.value();
return lhs;
@@ -95,8 +92,7 @@ struct flagset final {
/*
* Mask bits.
*/
- constexpr auto operator&= (this flagset &lhs, flagset rhs) noexcept
- -> flagset &
+ constexpr auto operator&=(this flagset &lhs, flagset rhs) noexcept -> flagset &
{
lhs.m_value &= rhs.value();
return lhs;
@@ -105,8 +101,7 @@ struct flagset final {
/*
* Invert bits.
*/
- [[nodiscard]] constexpr auto operator~ (this flagset self) noexcept
- -> flagset
+ [[nodiscard]] constexpr auto operator~(this flagset self) noexcept -> flagset
{
return flagset(~self.m_value);
}
@@ -114,8 +109,7 @@ struct flagset final {
/*
* xor bits.
*/
- constexpr auto operator^= (this flagset &lhs, flagset rhs)
- noexcept -> flagset
+ constexpr auto operator^=(this flagset &lhs, flagset rhs) noexcept -> flagset
{
lhs.m_value ^= rhs.value();
return lhs;
@@ -126,68 +120,55 @@ private:
explicit constexpr flagset(base_type mask) noexcept
: m_value(mask)
- {}
-};
+ {
+ }
-export template<std::integral base_type, typename Tag>
-[[nodiscard]] auto operator| (flagset<base_type, Tag> lhs,
- flagset<base_type, Tag> rhs) noexcept
- -> flagset<base_type, Tag>
-{
- return (lhs |= rhs);
-}
+ [[nodiscard]] friend auto operator|(flagset lhs, flagset rhs) noexcept -> flagset
+ {
+ return (lhs |= rhs);
+ }
-export template<std::integral base_type, typename Tag>
-[[nodiscard]] auto operator& (flagset<base_type, Tag> lhs,
- flagset<base_type, Tag> rhs) noexcept
- -> flagset<base_type, Tag>
-{
- return (lhs &= rhs);
-}
+ [[nodiscard]] friend auto operator&(flagset lhs, flagset rhs) noexcept -> flagset
+ {
+ return (lhs &= rhs);
+ }
-export template<std::integral base_type, typename Tag>
-[[nodiscard]] auto operator^ (flagset<base_type, Tag> lhs,
- flagset<base_type, Tag> rhs) noexcept
- -> flagset<base_type, Tag>
-{
- return (lhs ^= rhs);
-}
+ [[nodiscard]] friend auto operator^(flagset lhs, flagset rhs) noexcept -> flagset
+ {
+ return (lhs ^= rhs);
+ }
-export template<std::integral base_type, typename Tag>
-[[nodiscard]] auto operator== (flagset<base_type, Tag> lhs,
- flagset<base_type, Tag> rhs) noexcept
- -> bool
-{
- return lhs.value() == rhs.value();
-}
+ [[nodiscard]] friend auto
+ operator==(flagset lhs, flagset rhs) noexcept -> bool
+ {
+ return lhs.value() == rhs.value();
+ }
-export template<std::integral base_type, typename Tag>
-auto operator<<(std::ostream &strm, flagset<base_type, Tag> flags)
- -> std::ostream &
-{
- std::print(strm, "{}", flags);
- return strm;
-}
+ friend auto operator<<(std::ostream &strm, flagset<base_type, Tag> flags) -> std::ostream &
+ {
+ std::print(strm, "{}", flags);
+ return strm;
+ }
+};
} // namespace nihil
/*
* Formatting for flagset.
*/
-export template<std::integral base_type, typename Tag, typename Char>
+export template <std::integral base_type, typename Tag, typename Char>
struct std::formatter<nihil::flagset<base_type, Tag>, Char>
{
using flags_t = nihil::flagset<base_type, Tag>;
- template<typename ParseContext>
+ template <typename ParseContext>
constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator
{
return ctx.begin();
}
- template<typename FmtContext>
- auto format(flags_t flags, FmtContext& ctx) const
- -> FmtContext::iterator
+ template <typename FmtContext>
+ auto format(flags_t flags, FmtContext &ctx) const -> FmtContext::iterator
{
auto constexpr digits = std::numeric_limits<base_type>::digits;
auto value = flags.value();
diff --git a/nihil.posix/CMakeLists.txt b/nihil.posix/CMakeLists.txt
index d76ae4d..25f13f1 100644
--- a/nihil.posix/CMakeLists.txt
+++ b/nihil.posix/CMakeLists.txt
@@ -30,6 +30,7 @@ target_sources(nihil.posix
open_in_path.ccm
paths.ccm
process.ccm
+ progname.ccm
read_file.ccm
rename.ccm
spawn.ccm
diff --git a/nihil.posix/execv.ccm b/nihil.posix/execv.ccm
index d598d94..cd501f7 100644
--- a/nihil.posix/execv.ccm
+++ b/nihil.posix/execv.ccm
@@ -3,6 +3,10 @@ module;
#include <unistd.h> // execv()
+#include "nihil.hh"
+
+extern char **environ; // NOLINT
+
export module nihil.posix:execv;
import nihil.std;
@@ -56,7 +60,7 @@ export struct execv final
[&] (fd const &file) {
#if NIHIL_HAVE_FEXECVE == 1
- ::fexecv(file.get(), self.m_args.data());
+ ::fexecve(file.get(), self.m_args.data(), environ);
return std::unexpected(error("execve failed", error(sys_error())));
#else
std::ignore = file;
diff --git a/nihil.posix/open.ccm b/nihil.posix/open.ccm
index f2f5ecd..a5a9e5d 100644
--- a/nihil.posix/open.ccm
+++ b/nihil.posix/open.ccm
@@ -39,9 +39,6 @@ export inline constexpr auto open_shared_lock = open_flags::mask<O_SHLOCK>();
export inline constexpr auto open_exclusive_lock = open_flags::mask<O_EXLOCK>();
export inline constexpr auto open_directory = open_flags::mask<O_DIRECTORY>();
export inline constexpr auto open_nofollow = open_flags::mask<O_NOFOLLOW>();
-export inline constexpr auto open_nofollow_any = open_flags::mask<O_NOFOLLOW_ANY>();
-export inline constexpr auto open_symlink = open_flags::mask<O_SYMLINK>();
-export inline constexpr auto open_eventonly = open_flags::mask<O_EVTONLY>();
export inline constexpr auto open_close_on_exec = open_flags::mask<O_CLOEXEC>();
export inline constexpr auto open_resolve_beneath = open_flags::mask<O_RESOLVE_BENEATH>();
@@ -62,6 +59,19 @@ export inline constexpr auto open_path = open_flags::mask<O_PATH>();
export inline constexpr auto open_empty_path = open_flags::mask<O_EMPTY_PATH>();
#endif
+// macOS
+#ifdef O_NOFOLLOW_ANY
+export inline constexpr auto open_nofollow_any = open_flags::mask<O_NOFOLLOW_ANY>();
+#endif
+
+#ifdef O_SYMLINK
+export inline constexpr auto open_symlink = open_flags::mask<O_SYMLINK>();
+#endif
+
+#ifdef O_EVTONLY
+export inline constexpr auto open_eventonly = open_flags::mask<O_EVTONLY>();
+#endif
+
// Open the given file and return an fd for it.
export [[nodiscard]] auto open(std::filesystem::path const &filename, open_flags flags,
int mode = 0777) -> std::expected<fd, error>
diff --git a/nihil.posix/posix.ccm b/nihil.posix/posix.ccm
index aa21649..c80724d 100644
--- a/nihil.posix/posix.ccm
+++ b/nihil.posix/posix.ccm
@@ -19,6 +19,7 @@ export import :open;
export import :open_in_path;
export import :paths;
export import :process;
+export import :progname;
export import :read_file;
export import :rename;
export import :spawn;
diff --git a/nihil.posix/progname.ccm b/nihil.posix/progname.ccm
new file mode 100644
index 0000000..127b972
--- /dev/null
+++ b/nihil.posix/progname.ccm
@@ -0,0 +1,68 @@
+// This source code is released into the public domain.
+module;
+
+// progname: wrappers for getprogname and setprogname.
+//
+// setprogname() doesn't copy the provided name, which makes it awkward to provide
+// dynamic values (e.g., std::string). We solve this by making setprogname a guard
+// object which resets the previous progname when it's destroyed. This also happens
+// to be useful in cases like nihil.cli where we want to temporarily set the progname.
+
+#include <stdlib.h> // NOLINT
+
+export module nihil.posix:progname;
+
+import nihil.std;
+
+namespace nihil {
+
+// Get the current program name. We could return std::string_view here, but since
+// the current program name can change unexpectedly, we don't.
+export [[nodiscard]] auto getprogname() -> std::optional<std::string>
+{
+ if (auto const *progname = ::getprogname(); progname != nullptr)
+ return {progname};
+ return {};
+}
+
+// Set the progname name to a new value for the lifetime of this object.
+// Reset it to the previous value when the object is destroyed.
+export struct setprogname final
+{
+ // Set the program name.
+ explicit setprogname(std::string_view const progname)
+ : m_progname(progname)
+ , m_old_progname(::getprogname())
+ {
+ ::setprogname(m_progname.data());
+ }
+
+ // Restore the old name on destruction.
+ ~setprogname()
+ {
+ if (m_old_progname != nullptr)
+ ::setprogname(m_old_progname);
+ }
+
+ // Restore the old program name immediately.
+ auto release() -> void
+ {
+ if (m_old_progname != nullptr) {
+ ::setprogname(m_old_progname);
+ m_old_progname = nullptr;
+ }
+ }
+
+ // Not copyable.
+ setprogname(setprogname const &) = delete;
+ auto operator=(setprogname const &) -> setprogname & = delete;
+
+ // Not movable.
+ setprogname(setprogname &&) = delete;
+ auto operator=(setprogname &&) -> setprogname & = delete;
+
+private:
+ std::string m_progname;
+ char const *m_old_progname;
+};
+} // namespace nihil
diff --git a/nihil.std/nihil.std.ccm b/nihil.std/nihil.std.ccm
index 80ca3c9..d0a20b1 100644
--- a/nihil.std/nihil.std.ccm
+++ b/nihil.std/nihil.std.ccm
@@ -1,4 +1,5 @@
// This source code is released into the public domain.
+
module;
// Export the parts of std that nihil uses. This is technically undefined behaviour since we're
@@ -165,6 +166,7 @@ using std::uint64_t;
// <cstdlib>
using std::exit;
using std::quick_exit;
+using std::abort;
// <cstdio>
using std::FILE;
@@ -196,11 +198,35 @@ using std::filesystem::exists;
}
// <format>
+using std::basic_format_context;
+using std::format_context;
+using std::wformat_context;
+using std::basic_format_args;
+using std::format_args;
+using std::wformat_args;
+using std::basic_format_string;
+using std::format_string;
+using std::wformat_string;
+using std::runtime_format;
using std::format;
-using std::formatter;
using std::format_to;
+using std::vformat;
+using std::vformat_to;
using std::format_to_n;
-using std::runtime_format;
+using std::format_to_n_result;
+using std::formatted_size;
+using std::formatter;
+using std::formattable;
+using std::basic_format_parse_context;
+using std::format_parse_context;
+using std::wformat_parse_context;
+using std::format_kind;
+using std::range_format;
+using std::range_formatter;
+using std::basic_format_arg;
+using std::visit_format_arg;
+using std::make_format_args;
+using std::make_wformat_args;
using std::format_error;
// <functional>
@@ -289,6 +315,8 @@ using std::optional;
// <print>
using std::print;
using std::println;
+using std::vprint_nonunicode;
+using std::vprint_unicode;
// <random>
using std::default_random_engine;