DYNXZQZVGIOW26Y6VPP4TIBOJEMK6T6Y7HU5ZJYH4GIWFFRIJSIQC
FYYWB54CXR3FTAQ4HLDTFS6XWBVLWRGKHVMMNKUXDEJ7UWLHXPBQC
H5RKFV7YLV3QFY5HCQHGU7T232FPYMGWSBR3ABNGKMEAN5DS523AC
XGCRPWKLJYWGVCMHDIR2K3PVT2R22JALVV5K2IXTHU3JBAEYANGAC
3KEFKH5FYGFUIWMIW6NY3K4FYSLE7LEXR4ZSF6WN52PCC3TVDZBAC
U5AKEHEQWHBOVXMTTQFBJA4M4VJ7OWSJJVYYB6FLTJB7NCFPYVRQC
SGQQ6LNZCGWM4WP5VOOCGDUZ2FVQEC4FANXJ5SEZPDQHSLF7YL2QC
XXCRSVXOJVHO4A6W5R4A32WXXSNJDJLAT2CAQ6DE3JAMUFJF3HPAC
7YS2X7JJVAFT7QFDASQR4ZVUAAFYF4OLK5XLY3JJ2URSXI34ZOTAC
std::string clone(const std::string_view &repoUrl,
const nix::PathView &repoDir,
const std::optional<std::string_view> &channel = {},
const std::optional<std::string_view> &state = {});
void clone(const std::string_view &repoUrl,
const Path &repoDir,
const std::optional<std::string_view> &channel = {},
const std::optional<std::string_view> &state = {});
auto program = "pijul"sv;
auto res = runProgram(RunOptions{
.program = std::string(program),
.searchPath = true,
.args = std::move(args),
.chdir = Path(chdir),
.input = std::move(input),
.isInteractive = isInteractive,
});
if (!statusOk(res.first)) {
throw ExecError(res.first, "program '%1%' %2%", program, statusToString(res.first));
}
return res.second;
runProcess("pijul", args, chdir ? chdir->c_str() : nullptr, mode);
std::string clone(const std::string_view &repoUrl,
const std::string_view &repoDir,
const std::optional<std::string_view> &channel,
const std::optional<std::string_view> &state)
void clone(const std::string_view &repoUrl,
const Path &repoDir,
const std::optional<std::string_view> &channel,
const std::optional<std::string_view> &state)
auto output = runPijul({"list"}, repoDir);
std::string output;
runPijul({"list"}, repoDir, OutputMode::WithOutput{output});
size_t pos = 0;
size_t next;
std::set<Path> out;
while ((next = output.find_first_of("\n\r", pos)) != std::string::npos) {
out.emplace(output.substr(pos, next - pos));
pos = next + 1;
}
#endif
#ifndef NIX_PLUGIN_PIJUL_COMPAT_H
#define NIX_PLUGIN_PIJUL_COMPAT_H
#ifdef NIX_VERSION
#include <error.hh>
#include <types.hh>
#else
#include <filesystem>
#include <string>
#include <vector>
#endif
#include <variant>
namespace nixpluginpijul
{
#ifdef NIX_VERSION
using StringList = nix::Strings;
using Path = nix::Path;
#else
using StringList = std::vector<std::string>;
using Path = std::filesystem::path;
#endif
struct OutputMode final {
enum Units {
Standard,
Interactive,
};
struct WithOutput {
std::string &output;
};
OutputMode(Units data)
: m_data(data)
{
}
OutputMode(WithOutput data)
: m_data(data)
{
}
bool interactive()
{
if (auto *t = get_if<Units>(&m_data)) {
return *t == Interactive;
} else {
return false;
}
}
std::string *output()
{
if (auto *t = get_if<WithOutput>(&m_data)) {
return &t->output;
} else {
return nullptr;
}
}
private:
std::variant<Units, WithOutput> m_data;
};
void runProcess(const char *path, const StringList &args, const char *chdir = nullptr, OutputMode mode = OutputMode::Standard);
[[noreturn]] void fatal(const char *text);
}
#endif // NIX_PLUGIN_PIJUL_COMPAT_H
#include "compat.h"
#ifdef NIX_VERSION
#if NIX_VERSION >= 0x021900
#include <processes.hh>
#else
#include <util.hh>
#endif
#else
#include <cstdio>
#include <fcntl.h>
#include <format>
#include <spawn.h>
#include <sys/wait.h>
#include <sysexits.h>
#endif
using namespace std::string_literals;
using namespace std::string_view_literals;
namespace nixpluginpijul
{
void runProcess(const char *p, const StringList &args, const char *chdir, OutputMode mode)
{
#ifdef NIX_VERSION
auto res = nix::runProgram(nix::RunOptions{
.program = std::string(p),
.searchPath = true,
.args = args,
.chdir = Path(chdir),
.isInteractive = mode.interactive(),
});
if (!nix::statusOk(res.first)) {
throw nix::ExecError(res.first, "program '%1%' %2%", p, nix::statusToString(res.first));
}
if (mode.output()) {
*mode.output() = std::move(res.second);
}
#else
int fds[2];
posix_spawn_file_actions_t fa;
posix_spawn_file_actions_init(&fa);
if (mode.output()) {
if (pipe(fds) != 0) {
perror("failed to create pipe");
exit(EX_OSERR);
}
posix_spawn_file_actions_adddup2(&fa, fds[1], 0);
posix_spawn_file_actions_adddup2(&fa, fds[1], 1);
}
if (!mode.interactive()) {
posix_spawn_file_actions_addclose(&fa, 2);
}
if (chdir) {
posix_spawn_file_actions_addchdir_np(&fa, chdir);
}
std::vector<const char *> argv;
argv.push_back(p);
for (const auto &el : args) {
argv.push_back(el.c_str());
}
pid_t pid;
if (posix_spawnp(&pid, p, &fa, nullptr, const_cast<char *const *const>(argv.data()), nullptr) != 0) {
perror("failed to spawn process");
exit(EX_OSERR);
}
close(fds[1]);
posix_spawn_file_actions_destroy(&fa);
fd_set set;
FD_ZERO(&set);
int nfds = 0;
if (mode.output()) {
FD_SET(fds[0], &set);
fcntl(fds[0], F_SETFL, fcntl(fds[0], F_GETFL) | O_NONBLOCK);
nfds = 1;
}
sigset_t sigs;
sigfillset(&sigs);
sigdelset(&sigs, SIGCHLD);
timespec timeout{.tv_sec = 1};
bool running = true;
int status;
while (running) {
// FIXME: this never catches the SIGCHLD
int ret = pselect(nfds, &set, nullptr, nullptr, &timeout, &sigs);
printf("%d\n", ret);
if (ret == -1) {
if (errno != EINTR) {
perror("pselect");
exit(EX_OSERR);
}
}
ret = waitpid(pid, &status, WNOHANG);
if (ret == -1) {
perror("waitpid");
exit(EX_OSERR);
} else if (ret == pid) {
running = false;
ret = 1; // get the last data out of the pipe
}
if (ret > 0 && mode.output()) {
char buf[4096];
ssize_t len;
do {
len = read(fds[0], buf, sizeof(buf));
if (len < 0 && errno != EWOULDBLOCK) {
perror("read");
exit(EX_OSERR);
}
if (len > 1) {
mode.output()->append(buf, len);
}
} while (len > 0);
}
}
if (mode.output()) {
close(fds[0]);
}
if (status != 0) {
fatal(std::format("process `{}' exited with status {}", p, WEXITSTATUS(status)).c_str());
}
#endif
}
void fatal(const char *text)
{
#ifdef NIX_VERSION
throw nix::Error("%1%", text);
#else
fprintf(stderr, "fatal: %s\n", text);
exit(EX_SOFTWARE);
#endif
}
}
#include "repo.h"
using namespace nixpluginpijul;
int main()
{
clone("https://nest.pijul.com/dblsaiko/nix-plugin-pijul", "nix-plugin-pijul");
const std::set<Path> &set = getTrackedFiles("nix-plugin-pijul");
for (const auto &item : set) {
printf("- %s\n", item.c_str());
}
}
#include "repo.h"
#include <format>
using namespace nixpluginpijul;
template<typename T>
void assert_eq(const T &left, const T &right)
{
if (left != right) {
const std::string &string = std::format("assertion failed: {} == {}", left, right);
fprintf(stderr, "%s\n", string.c_str());
exit(1);
}
}
template<typename F>
void assert_throws(F op)
{
try {
op();
} catch (...) {
return;
}
fprintf(stderr, "%s\n", "assertion failed: `op' didn't throw");
exit(1);
}
int main()
{
auto point = parseRFC3339("1970-01-01T00:00:00.000Z");
assert_eq(0L, point.time_since_epoch().count());
point = parseRFC3339("1970-01-01T00:00:00Z");
assert_eq(0L, point.time_since_epoch().count());
point = parseRFC3339("1970-01-01T00:30:00+00:30");
assert_eq(0L, point.time_since_epoch().count());
point = parseRFC3339("1970-01-01T00:00:00.000-01:00");
assert_eq(3600000L, point.time_since_epoch().count());
assert_throws([] {
parseRFC3339("garbage");
});
assert_throws([] {
parseRFC3339("1");
});
assert_throws([] {
parseRFC3339("1970-01-01T00:00:00");
});
// FIXME: this should be an invalid timestamp (at least, I think so) because
// it has both +00:30 and 'Z' but it parses
assert_throws([] {
parseRFC3339("1970-01-01T00:30:00+00:30Z");
});
}
add_compile_options(-O1 -ggdb)
add_executable(DateTest datetest.cpp)
target_link_libraries(DateTest PUBLIC PijulTesting)
add_test(NAME DateTest COMMAND DateTest)
add_executable(RepoTest repotest.cpp)
target_link_libraries(RepoTest PUBLIC PijulTesting)
add_test(NAME RepoTest COMMAND RepoTest)