bug fixes
This commit is contained in:
parent
7f1fa02ba3
commit
04ff6584ed
12 changed files with 153 additions and 74 deletions
|
|
@ -19,8 +19,9 @@ public:
|
|||
[[nodiscard]] ui64 size() const override;
|
||||
|
||||
void clearFlags(std::shared_ptr<Node>& directory) override;
|
||||
bool isHard() const override;
|
||||
bool isHardNode() const override;
|
||||
void removeIncomingDynamicLinks() override;
|
||||
void removeOutgoingLinks() override;
|
||||
|
||||
template <typename tFunctor>
|
||||
static void traverse(std::shared_ptr<Node>& node, tFunctor functor) {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@
|
|||
// copy operator exit if exists, do not rename
|
||||
// clean-ups
|
||||
// in subtree links should be in-tree after copy operation
|
||||
// update link if exists
|
||||
|
||||
class FileSystem {
|
||||
public:
|
||||
|
|
@ -31,7 +32,7 @@ public:
|
|||
static const std::string& getLastError();
|
||||
|
||||
private:
|
||||
bool isPathContainsCurrent(const std::shared_ptr<Node>& node);
|
||||
bool isPathContains(const std::shared_ptr<Node>& node, const std::shared_ptr<Node>& contains);
|
||||
std::shared_ptr<Node> getNode(const Path& path, bool parent);
|
||||
|
||||
private:
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ public:
|
|||
NodeType getType() const override { return LINK; }
|
||||
[[nodiscard]] std::shared_ptr<Node> clone() const override;
|
||||
[[nodiscard]] std::shared_ptr<Node> getLink() const;
|
||||
[[nodiscard]] bool isHard() const;
|
||||
[[nodiscard]] bool isHardLink() const;
|
||||
|
||||
std::shared_ptr<Node> getTarget() override;
|
||||
|
||||
|
|
@ -20,7 +20,8 @@ public:
|
|||
void dumpUtil(std::stringstream& ss, const Key& key, ui32 currentDepth, std::vector<bool>& indents) override;
|
||||
|
||||
static bool linkNodes(const std::shared_ptr<Link>& link, const std::shared_ptr<Node>& target, bool hard);
|
||||
static void unlinkNodes(const std::shared_ptr<Link>& link);
|
||||
|
||||
void removeOutgoingLinks() override;
|
||||
|
||||
private:
|
||||
std::weak_ptr<Node> mLink;
|
||||
|
|
|
|||
|
|
@ -38,8 +38,9 @@ public:
|
|||
virtual std::shared_ptr<Node> findNode(const Key& path) { return nullptr; }
|
||||
|
||||
virtual void clearFlags(std::shared_ptr<Node>& directory) {}
|
||||
virtual bool isHard() const;
|
||||
virtual bool isHardNode() const;
|
||||
virtual void removeIncomingDynamicLinks();
|
||||
virtual void removeOutgoingLinks() {}
|
||||
|
||||
ui32 getMaxDepth() const;
|
||||
void dump(std::stringstream& ss);
|
||||
|
|
|
|||
|
|
@ -4,16 +4,11 @@
|
|||
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
Directory::Directory() = default;
|
||||
|
||||
Directory::~Directory() {
|
||||
for (auto& member : mMembers) {
|
||||
std::shared_ptr<Node>& node = member.second;
|
||||
if (auto linkNode = std::dynamic_pointer_cast<Link>(node)) {
|
||||
Link::unlinkNodes(linkNode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Directory::attachNode(const Key &newKey, std::shared_ptr<Node> newNode) {
|
||||
|
|
@ -64,6 +59,7 @@ void Directory::dumpUtil(std::stringstream& ss, const Key& key, ui32 currentDept
|
|||
indent(ss, currentDepth, indents);
|
||||
ss << key;
|
||||
// ss << " [" << mIncomingHardLinks.size() << ":" << mIncomingDynamicLinks.size() << "] " << size();
|
||||
ss << " [" << mWorkingNodeFlag.lock().get() << "] ";
|
||||
ss << "\n";
|
||||
|
||||
currentDepth++;
|
||||
|
|
@ -98,7 +94,7 @@ std::shared_ptr<Node> Directory::clone() const {
|
|||
std::shared_ptr<Node>& node = member.second;
|
||||
if (auto linkNode = std::dynamic_pointer_cast<Link>(node)) {
|
||||
auto targetNode = linkNode->getTarget();
|
||||
assert(Link::linkNodes(linkNode, targetNode, linkNode->isHard()));
|
||||
assert(Link::linkNodes(linkNode, targetNode, linkNode->isHardLink()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -111,8 +107,11 @@ void Directory::clearFlags(std::shared_ptr<Node> &directory) {
|
|||
});
|
||||
}
|
||||
|
||||
bool Directory::isHard() const {
|
||||
return std::any_of(mMembers.begin(), mMembers.end(), [](const auto& member) { return member.second->isHard(); });
|
||||
bool Directory::isHardNode() const {
|
||||
if (Node::isHardNode()) return true;
|
||||
return std::any_of(mMembers.begin(), mMembers.end(), [](const auto& member) {
|
||||
return member.second->isHardNode();
|
||||
});
|
||||
}
|
||||
|
||||
void Directory::removeIncomingDynamicLinks() {
|
||||
|
|
@ -120,4 +119,10 @@ void Directory::removeIncomingDynamicLinks() {
|
|||
for (auto& member : mMembers) {
|
||||
member.second->removeIncomingDynamicLinks();
|
||||
}
|
||||
}
|
||||
|
||||
void Directory::removeOutgoingLinks() {
|
||||
for (auto& member : mMembers) {
|
||||
member.second->removeOutgoingLinks();
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@ static std::string gError;
|
|||
|
||||
FileSystem::FileSystem() {
|
||||
root = std::make_shared<Directory>();
|
||||
root->mKey = "C:";
|
||||
currentDirectory = root;
|
||||
initializeTransitions();
|
||||
}
|
||||
|
|
@ -34,7 +35,7 @@ std::shared_ptr<Node> FileSystem::getNode(const Path& path, bool parent) {
|
|||
|
||||
bool FileSystem::changeCurrent(const Path& path) {
|
||||
if (path.isInvalid()) {
|
||||
gError = "Path not found";
|
||||
gError = "Invalid path";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -45,6 +46,10 @@ bool FileSystem::changeCurrent(const Path& path) {
|
|||
return false;
|
||||
}
|
||||
|
||||
while (parentNode->getTarget()) {
|
||||
parentNode = parentNode->getTarget();
|
||||
}
|
||||
|
||||
if (parentNode->getType() != Node::DIRECTORY) {
|
||||
gError = "Path is not a directory";
|
||||
return false;
|
||||
|
|
@ -63,14 +68,14 @@ bool FileSystem::makeDirectory(const Path& path) {
|
|||
auto parentNode = getNode(path, true);
|
||||
|
||||
if (!parentNode) {
|
||||
gError = "Invalid path";
|
||||
gError = "Path not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto existingNode = parentNode->findNode(path.getFilename());
|
||||
if (existingNode) {
|
||||
if (!(existingNode->getType() == Node::DIRECTORY && existingNode->empty())) {
|
||||
gError = "File or link with such name already exists";
|
||||
gError = "File, link or not empty directory with such name already exists";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
|
@ -92,7 +97,7 @@ bool FileSystem::makeFile(const Path& path) {
|
|||
auto parentNode = getNode(path, true);
|
||||
|
||||
if (!parentNode) {
|
||||
gError = "Invalid path";
|
||||
gError = "Path not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -120,19 +125,19 @@ bool FileSystem::makeLink(const Path& source, const Path& target, bool isDynamic
|
|||
|
||||
auto parentNodeSource = getNode(source, true);
|
||||
if (!parentNodeSource) {
|
||||
gError = "Invalid source path";
|
||||
gError = "Source path not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto parentNodeTarget = getNode(target, false);
|
||||
if (!parentNodeTarget) {
|
||||
gError = "Invalid target path";
|
||||
gError = "Destination path not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto sourceNode = parentNodeSource->findNode(source.getFilename());
|
||||
if (!sourceNode) {
|
||||
gError = "Invalid source path";
|
||||
gError = "Source path not found";
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -189,19 +194,20 @@ bool FileSystem::removeDirectory(const Path& path, bool recursively) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (isPathContainsCurrent(existingNode)) {
|
||||
if (isPathContains(existingNode, currentDirectory)) {
|
||||
gError = "Can not remove directory you are currently working in";
|
||||
return false;
|
||||
}
|
||||
|
||||
existingNode->clearFlags(parentNode);
|
||||
if (existingNode->isHard()) {
|
||||
gError = "Directory is referenced by a hard links";
|
||||
existingNode->clearFlags(existingNode);
|
||||
if (existingNode->isHardNode()) {
|
||||
gError = "Directory is referenced by a hard link";
|
||||
return false;
|
||||
}
|
||||
|
||||
// deletes dynamic incoming links to this file system sub-ree
|
||||
existingNode->removeIncomingDynamicLinks();
|
||||
existingNode->removeOutgoingLinks();
|
||||
|
||||
if (!parentNode->detachNode(path.getFilename())) {
|
||||
gError = "Can not remove directory because it has hard references";
|
||||
|
|
@ -235,15 +241,13 @@ bool FileSystem::removeFileOrLink(const Path &path) {
|
|||
return false;
|
||||
}
|
||||
|
||||
existingNode->clearFlags(parentNode);
|
||||
if (existingNode->isHard()) {
|
||||
existingNode->clearFlags(existingNode);
|
||||
if (existingNode->isHardNode()) {
|
||||
gError = "File is referenced by a hard link";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto link = dynamic_pointer_cast<Link>(existingNode)) {
|
||||
Link::unlinkNodes(link);
|
||||
}
|
||||
existingNode->removeOutgoingLinks();
|
||||
|
||||
if (!parentNode->detachNode(path.getFilename())) {
|
||||
gError = "Can not remove file or link because it is referenced by a hard link";
|
||||
|
|
@ -320,6 +324,16 @@ bool FileSystem::moveNode(const Path &source, const Path &target) {
|
|||
return false;
|
||||
}
|
||||
|
||||
if (isPathContains(sourceNode, currentDirectory)) {
|
||||
gError = "Can not move directory you are currently working in";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isPathContains(sourceNode, parentNodeTarget)) {
|
||||
gError = "Can not move node into itself";
|
||||
return false;
|
||||
}
|
||||
|
||||
const Key& key = source.getFilename();
|
||||
|
||||
if (parentNodeTarget->findNode(key)) {
|
||||
|
|
@ -327,8 +341,8 @@ bool FileSystem::moveNode(const Path &source, const Path &target) {
|
|||
return false;
|
||||
}
|
||||
|
||||
sourceNode->clearFlags(parentNodeSource);
|
||||
if (sourceNode->isHard()) {
|
||||
sourceNode->clearFlags(sourceNode);
|
||||
if (sourceNode->isHardNode()) {
|
||||
gError = "Node is referenced by a hard link";
|
||||
return false;
|
||||
}
|
||||
|
|
@ -347,11 +361,12 @@ void FileSystem::log() const {
|
|||
root->getNodeStraightPath(currentDirectory, currentPath);
|
||||
std::reverse(currentPath.begin(), currentPath.end());
|
||||
|
||||
ss << "cd [";
|
||||
for (const auto& node : currentPath) {
|
||||
ss << node->mKey << "/";
|
||||
ss << "pwd [ ";
|
||||
for (auto it = currentPath.begin(); it != currentPath.end(); ++it) {
|
||||
ss << (*it)->mKey;
|
||||
if (std::distance(it, currentPath.end()) > 1) ss << "/";
|
||||
}
|
||||
ss << "]\n";
|
||||
ss << " ]\n";
|
||||
|
||||
root->dump(ss);
|
||||
std::cout << ss.str();
|
||||
|
|
@ -361,11 +376,11 @@ const std::string& FileSystem::getLastError() {
|
|||
return gError;
|
||||
}
|
||||
|
||||
bool FileSystem::isPathContainsCurrent(const std::shared_ptr<Node>& node) {
|
||||
bool FileSystem::isPathContains(const std::shared_ptr<Node>& node, const std::shared_ptr<Node>& current) {
|
||||
std::vector<std::shared_ptr<Node>> currentPath;
|
||||
std::vector<std::shared_ptr<Node>> path;
|
||||
|
||||
root->getNodeStraightPath(currentDirectory, currentPath);
|
||||
root->getNodeStraightPath(current, currentPath);
|
||||
root->getNodeStraightPath(node, path);
|
||||
|
||||
std::reverse(currentPath.begin(), currentPath.end());
|
||||
|
|
|
|||
|
|
@ -109,6 +109,8 @@ bool Interpreter::interpret(const std::string& command) {
|
|||
std::vector<std::string> words;
|
||||
getWords(command, words);
|
||||
|
||||
std::cout << command << "\n";
|
||||
|
||||
if (words.empty()) {
|
||||
reportError("Empty command");
|
||||
return false;
|
||||
|
|
|
|||
26
src/Link.cpp
26
src/Link.cpp
|
|
@ -35,18 +35,14 @@ bool Link::linkNodes(const std::shared_ptr<Link>& link, const std::shared_ptr<No
|
|||
return true;
|
||||
}
|
||||
|
||||
void Link::unlinkNodes(const std::shared_ptr<Link>& link) {
|
||||
auto target = link->mLink.lock();
|
||||
|
||||
// this is happening when
|
||||
if (!target) return;
|
||||
|
||||
auto& links = link->isHard() ? target->mIncomingHardLinks : target->mIncomingDynamicLinks;
|
||||
void Link::removeOutgoingLinks() {
|
||||
auto target = mLink.lock();
|
||||
assert(target);
|
||||
|
||||
auto& links = isHardLink() ? target->mIncomingHardLinks : target->mIncomingDynamicLinks;
|
||||
links.erase(std::remove_if(links.begin(), links.end(), [&](std::weak_ptr<Link>& node){
|
||||
auto targetLink = node.lock();
|
||||
assert(targetLink);
|
||||
return targetLink == link;
|
||||
return !targetLink || targetLink.get() == this;
|
||||
}), links.end());
|
||||
}
|
||||
|
||||
|
|
@ -56,7 +52,7 @@ std::shared_ptr<Node> Link::getLink() const {
|
|||
return target;
|
||||
}
|
||||
|
||||
bool Link::isHard() const {
|
||||
bool Link::isHardLink() const {
|
||||
return mIsHard;
|
||||
}
|
||||
|
||||
|
|
@ -71,12 +67,16 @@ std::shared_ptr<Node> Link::findNode(const std::vector<Key>& path, ui32 currentD
|
|||
|
||||
void Link::dumpUtil(std::stringstream& ss, const Key& key, ui32 currentDepth, std::vector<bool>& indents) {
|
||||
indent(ss, currentDepth, indents);
|
||||
ss << key << (isHard() ? " hlink[" : " dlink[");
|
||||
ss << key << (isHardLink() ? " hlink[" : " dlink[");
|
||||
std::vector<std::shared_ptr<Node>> path;
|
||||
getNodeStraightPath(getLink(), path);
|
||||
std::reverse(path.begin(), path.end());
|
||||
for (auto& node : path)
|
||||
ss << node->mKey << "/";
|
||||
|
||||
for (auto it = path.begin(); it != path.end(); ++it) {
|
||||
ss << (*it)->mKey;
|
||||
if (std::distance(it, path.end()) > 1) ss << "/";
|
||||
}
|
||||
|
||||
ss << "]\n";
|
||||
}
|
||||
|
||||
|
|
|
|||
24
src/Node.cpp
24
src/Node.cpp
|
|
@ -6,8 +6,11 @@
|
|||
#include <sstream>
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
Node::Node(const Node &node) {}
|
||||
Node::Node(const Node &node) {
|
||||
mKey = node.mKey;
|
||||
}
|
||||
|
||||
Node::~Node() {
|
||||
// assert(mIncomingHardLinks.empty());
|
||||
|
|
@ -32,28 +35,27 @@ std::shared_ptr<Node> Node::findNode(const std::vector<Key>& path, ui32 currentD
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool Node::isHard() const {
|
||||
bool Node::isHardNode() const {
|
||||
return std::any_of(mIncomingHardLinks.begin(), mIncomingHardLinks.end(), [this](const std::weak_ptr<Link>& hardLink) {
|
||||
auto linkNode = hardLink.lock();
|
||||
assert(linkNode);
|
||||
auto linkTarget = linkNode->getLink()->mWorkingNodeFlag.lock();
|
||||
assert(linkTarget);
|
||||
auto ownLink = mWorkingNodeFlag.lock();
|
||||
assert(linkNode && ownLink);
|
||||
|
||||
auto linkTarget = linkNode->mWorkingNodeFlag.lock();
|
||||
|
||||
// if hard link in the same working tree it does not count
|
||||
return ownLink != linkTarget;
|
||||
return !linkTarget || ownLink != linkTarget;
|
||||
});
|
||||
}
|
||||
|
||||
void Node::removeIncomingDynamicLinks() {
|
||||
for (auto& dynamicLink : mIncomingDynamicLinks) {
|
||||
auto linkNode = dynamicLink.lock();
|
||||
assert(linkNode);
|
||||
auto linkTarget = linkNode->getLink()->mWorkingNodeFlag.lock();
|
||||
assert(linkTarget);
|
||||
auto ownLink = mWorkingNodeFlag.lock();
|
||||
assert(linkNode && ownLink);
|
||||
|
||||
if (ownLink == linkTarget) {
|
||||
auto linkTarget = linkNode->mWorkingNodeFlag.lock();
|
||||
if (linkTarget && ownLink == linkTarget) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -76,7 +78,7 @@ void Node::getNodeStraightPath(const std::shared_ptr<Node>& node, std::vector<st
|
|||
void Node::dump(std::stringstream& ss) {
|
||||
std::vector<bool> indents;
|
||||
indents.resize(getMaxDepth());
|
||||
dumpUtil(ss, "/", 0, indents);
|
||||
dumpUtil(ss, mKey, 0, indents);
|
||||
ss << "\n";
|
||||
}
|
||||
|
||||
|
|
|
|||
27
src/Path.cpp
27
src/Path.cpp
|
|
@ -1,7 +1,9 @@
|
|||
#include "Path.hpp"
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
|
||||
std::vector<char> transitions;
|
||||
constexpr auto drivePrefix = "C:";
|
||||
|
||||
void initializeTransitions() {
|
||||
transitions.resize(256);
|
||||
|
|
@ -50,14 +52,35 @@ const std::string& Path::getFilename() const {
|
|||
}
|
||||
|
||||
void Path::set(const std::string& path) {
|
||||
mIsValid = true;
|
||||
|
||||
if (path.empty()) {
|
||||
mIsValid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string lowercasePath = path;
|
||||
mIsValid = true;
|
||||
mAbsolute = path.front() == '/';
|
||||
|
||||
const auto drivePrefixSize = std::strlen(drivePrefix);
|
||||
if (drivePrefixSize) {
|
||||
if ((lowercasePath.size() >= drivePrefixSize) && (std::memcmp(lowercasePath.c_str(), drivePrefix, drivePrefixSize) == 0)) {
|
||||
lowercasePath.erase(0, drivePrefixSize);
|
||||
mAbsolute = true;
|
||||
if (lowercasePath.empty()) lowercasePath = "/";
|
||||
else if (lowercasePath[0] != '/') {
|
||||
mIsValid = false;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (lowercasePath[0] == '/') {
|
||||
mIsValid = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
mAbsolute = path.front() == '/';
|
||||
}
|
||||
|
||||
mDirectory = path.back() == '/';
|
||||
mChain.clear();
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,34 @@
|
|||
|
||||
int main() {
|
||||
Interpreter interpreter;
|
||||
interpreter.interpret("");
|
||||
interpreter.interpret("md a");
|
||||
interpreter.interpret("md a/b");
|
||||
interpreter.interpret("mdl a a/b");
|
||||
interpreter.interpret("copy a C:/");
|
||||
interpreter.interpret("deltree a");
|
||||
interpreter.interpret("deltree a_copy");
|
||||
|
||||
|
||||
interpreter.interpret("md a");
|
||||
interpreter.interpret("md a/b");
|
||||
interpreter.interpret("mhl a a/b");
|
||||
interpreter.interpret("copy a C:/");
|
||||
interpreter.interpret("deltree a");
|
||||
interpreter.interpret("deltree a_copy");
|
||||
interpreter.interpret("del a/b/a");
|
||||
interpreter.interpret("deltree a");
|
||||
|
||||
interpreter.interpret("md a");
|
||||
interpreter.interpret("md a/b");
|
||||
interpreter.interpret("mdl a a/b");
|
||||
interpreter.interpret("copy a C:/");
|
||||
interpreter.interpret("md c");
|
||||
interpreter.interpret("move a c");
|
||||
interpreter.interpret("move a_copy c");
|
||||
|
||||
interpreter.interpret("copy c C:/");
|
||||
|
||||
interpreter.interpret("deltree c");
|
||||
|
||||
return UnitTest::RunAllTests();
|
||||
}
|
||||
|
|
@ -2,24 +2,24 @@
|
|||
m
|
||||
mD a
|
||||
mD /
|
||||
MD /A123
|
||||
cd /A123
|
||||
MD C:/A123
|
||||
cd C:/A123
|
||||
md asd
|
||||
md asd/b
|
||||
md asd/b/c
|
||||
md asd/b/c/d
|
||||
cd asd/b/c/d
|
||||
cd /
|
||||
copy A123 /
|
||||
cd C:/
|
||||
copy A123 C:/
|
||||
rd d
|
||||
deltree A123
|
||||
deltree A123_copy
|
||||
md a
|
||||
md a/b
|
||||
md a/b/c
|
||||
mhl a /a/b/c/
|
||||
mhl a C:/a/b/c/
|
||||
md a/b/c/a/k
|
||||
copy a /
|
||||
mdl a/b/c /a_copy/k
|
||||
move a/b/c /a_copy
|
||||
del /a_copy/k/c
|
||||
copy a C:/
|
||||
mdl a/b/c C:/a_copy/k
|
||||
move a/b/c C:/a_copy
|
||||
del C:/a_copy/k/c
|
||||
Loading…
Add table
Add a link
Reference in a new issue