forked from lix-project/lix
Jade Lovelace
1fa6a3e335
* some things that can throw are marked noexcept
yet the linter seems to think not. Maybe they can't throw in practice.
I would rather not have the UB possibility in pretty obvious cold
paths.
* various default-case-missing complaints
* a fair pile of casts from integer to character, which are in fact
deliberate.
* an instance of <https://clang.llvm.org/extra/clang-tidy/checks/bugprone/move-forwarding-reference.html>
* bugprone-not-null-terminated-result on handing a string to curl in
chunks of bytes. our usage is fine.
* reassigning a unique_ptr by CRIMES instead of using release(), then
using release() and ignoring the result. wild. let's use release() for
its intended purpose.
Change-Id: Ic3e7affef12383576213a8a7c8145c27e662513d
98 lines
2.6 KiB
C++
98 lines
2.6 KiB
C++
#include "source-path.hh"
|
|
|
|
namespace nix {
|
|
|
|
std::ostream & operator << (std::ostream & str, const SourcePath & path)
|
|
{
|
|
str << path.to_string();
|
|
return str;
|
|
}
|
|
|
|
std::string_view SourcePath::baseName() const
|
|
{
|
|
return path.baseName().value_or("source");
|
|
}
|
|
|
|
SourcePath SourcePath::parent() const
|
|
{
|
|
auto p = path.parent();
|
|
assert(p);
|
|
return std::move(*p);
|
|
}
|
|
|
|
InputAccessor::Stat SourcePath::lstat() const
|
|
{
|
|
auto st = nix::lstat(path.abs());
|
|
return InputAccessor::Stat {
|
|
.type =
|
|
S_ISREG(st.st_mode) ? InputAccessor::tRegular :
|
|
S_ISDIR(st.st_mode) ? InputAccessor::tDirectory :
|
|
S_ISLNK(st.st_mode) ? InputAccessor::tSymlink :
|
|
InputAccessor::tMisc,
|
|
.isExecutable = S_ISREG(st.st_mode) && st.st_mode & S_IXUSR
|
|
};
|
|
}
|
|
|
|
std::optional<InputAccessor::Stat> SourcePath::maybeLstat() const
|
|
{
|
|
// FIXME: merge these into one operation.
|
|
if (!pathExists())
|
|
return {};
|
|
return lstat();
|
|
}
|
|
|
|
InputAccessor::DirEntries SourcePath::readDirectory() const
|
|
{
|
|
InputAccessor::DirEntries res;
|
|
for (auto & entry : nix::readDirectory(path.abs())) {
|
|
std::optional<InputAccessor::Type> type;
|
|
switch (entry.type) {
|
|
case DT_REG: type = InputAccessor::Type::tRegular; break;
|
|
case DT_LNK: type = InputAccessor::Type::tSymlink; break;
|
|
case DT_DIR: type = InputAccessor::Type::tDirectory; break;
|
|
default: break; // unknown type
|
|
}
|
|
res.emplace(entry.name, type);
|
|
}
|
|
return res;
|
|
}
|
|
|
|
SourcePath SourcePath::resolveSymlinks(SymlinkResolution mode) const
|
|
{
|
|
SourcePath res(CanonPath::root);
|
|
|
|
int linksAllowed = 1024;
|
|
|
|
std::list<std::string> todo;
|
|
for (auto & c : path)
|
|
todo.push_back(std::string(c));
|
|
|
|
bool resolve_last = mode == SymlinkResolution::Full;
|
|
|
|
while (!todo.empty()) {
|
|
auto c = *todo.begin();
|
|
todo.pop_front();
|
|
if (c == "" || c == ".")
|
|
;
|
|
else if (c == "..")
|
|
res.path.pop();
|
|
else {
|
|
res.path.push(c);
|
|
if (resolve_last || !todo.empty()) {
|
|
if (auto st = res.maybeLstat(); st && st->type == InputAccessor::tSymlink) {
|
|
if (!linksAllowed--)
|
|
throw Error("infinite symlink recursion in path '%s'", path);
|
|
auto target = res.readLink();
|
|
res.path.pop();
|
|
if (target.starts_with("/"))
|
|
res.path = CanonPath::root;
|
|
todo.splice(todo.begin(), tokenizeString<std::list<std::string>>(target, "/"));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
}
|