diff --git a/inc/Directory.hpp b/inc/Directory.hpp new file mode 100644 index 0000000..047c374 --- /dev/null +++ b/inc/Directory.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include "Node.hpp" + +class Directory : public Node { + typedef std::map DirectoryTree; + +public: + Directory(); + Directory(const Directory& node); + + ~Directory() override; + + [[nodiscard]] Directory* clone() const override; + + bool attachNode(const std::vector& directoryPath, const Key& newKey, Node* newNode) override; + bool detachNode(const std::vector& directoryPath, const Key& key) override; + + bool detachNode(const Key& key) override; + bool attachNode(const Key &newKey, Node *newNode) override; + + Node* findNode(const std::vector& path, ui32 currentDepth = 0) override; + Node* findNode(const Key& path) override; + + [[nodiscard]] ui64 size() const override; + + bool isDirectory() const override { return true; } + +private: + void getMaxDepthUtil(ui32 depth, ui32& maxDepth) const; + void dumpUtil(std::stringstream& ss, const Key& key, ui32 currentDepth, std::vector& indents) override; + +public: + DirectoryTree mMembers; +}; \ No newline at end of file diff --git a/inc/DirectoryTree.hpp b/inc/DirectoryTree.hpp deleted file mode 100644 index d245945..0000000 --- a/inc/DirectoryTree.hpp +++ /dev/null @@ -1,93 +0,0 @@ -#pragma once - -typedef unsigned long long ui64; -typedef unsigned long ui32; -typedef long long i64; -typedef long i32; - -#include -#include -#include -#include - -class Link; - -extern std::string gError; -typedef std::string Key; - -class Node { -public: - Node() = default; - Node(const Node& node); - virtual ~Node(); - - [[nodiscard]] virtual Node* clone() const; - -public: - enum Type : ui32 { NONE, DIRECTORY, FILE, LINK } ; - Type mType = NONE; // TODO : remove - - class Directory* mParent = nullptr; - - std::vector mIncomingHardLinks; - std::vector mIncomingDynamicLinks; -}; - -typedef std::map DirectoryTree; - -class File : public Node { -public: - File(); - File(const File& node); - - [[nodiscard]] File* clone() const override; -}; - -class Link : public Node { -public: - Link(Node* target, bool isHard); - Link(const Link& node); - ~Link() override; - - [[nodiscard]] Link* clone() const override; - - [[nodiscard]] Node* getLink() const; - [[nodiscard]] bool isHard() const; - -private: - Node* mLink = nullptr; - bool mIsHard = false; -}; - -class Directory : public Node { -public: - Directory(); - Directory(const Directory& node); - - ~Directory() override; - - [[nodiscard]] Directory* clone() const override; - - void dump(std::stringstream& ss); - - bool attachNode(const std::vector& directoryPath, const Key& newKey, Node* newNode); - bool detachNode(const std::vector& directoryPath, const Key& key); - - bool detachNode(const Key& key); - bool attachNode(const Key &newKey, Node *newNode); - - Node* findNode(const std::vector& path, ui32 currentDepth = 0); - Node* findNode(const Key& path); - - [[nodiscard]] ui32 getMaxDepth() const; - - void getNodeStraightPath(Node* node, std::vector& path) const; - [[nodiscard]] ui64 size() const; - -private: - void getMaxDepthUtil(ui32 depth, ui32& maxDepth) const; - void dumpUtil(std::stringstream& ss, ui32 currentDepth, std::vector& indents); - -public: - DirectoryTree mMembers; -}; \ No newline at end of file diff --git a/inc/FileSystem.hpp b/inc/FileSystem.hpp index 9f5b5c4..c7c2e85 100644 --- a/inc/FileSystem.hpp +++ b/inc/FileSystem.hpp @@ -1,21 +1,20 @@ #pragma once +#include "Node.hpp" #include "Path.hpp" -#include "DirectoryTree.hpp" #include -// FIX MEMORY VIOLATIONS WITH LINKS +// restore path unwindingk +// RESTORE LINKS +// COPY operator update link targets +// Improve error logs +// use smart pointers - -// DONT STORE CACHE -// DO O(N) deletion and moving // in-tree node links will report false to those operations and hard nodes deletion // use in node 'is_delete' flag and travers all nodes with link checks // remove mParent mTreeNode links -// Key (is copied on each tree access) !!! - // Functionality: // deleting directory - mark all nodes as deleted // traverse and check for links @@ -51,6 +50,6 @@ private: bool isPathContainsCurrent(Node* node); private: - Directory* root = nullptr; - Directory* currentDirectory = nullptr; + Node* root = nullptr; + Node* currentDirectory = nullptr; }; \ No newline at end of file diff --git a/inc/Link.hpp b/inc/Link.hpp new file mode 100644 index 0000000..f444f57 --- /dev/null +++ b/inc/Link.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "Node.hpp" + +class Link : public Node { +public: + Link(Node* target, bool isHard); + Link(const Link& node); + ~Link() override; + + [[nodiscard]] Link* clone() const override; + + [[nodiscard]] Node* getLink() const; + [[nodiscard]] bool isHard() const; + + Node* getTarget() override; + + // link on link is not allowed, so no inf looping here + Node* findNode(const std::vector& path, ui32 currentDepth) override; + + void dumpUtil(std::stringstream& ss, const Key& key, ui32 currentDepth, std::vector& indents) override; + + bool isLink() const override { return true; } + +private: + Node* mLink = nullptr; + bool mIsHard = false; +}; \ No newline at end of file diff --git a/inc/Node.hpp b/inc/Node.hpp new file mode 100644 index 0000000..336b683 --- /dev/null +++ b/inc/Node.hpp @@ -0,0 +1,64 @@ +#pragma once + +typedef unsigned long long ui64; +typedef unsigned long ui32; +typedef long long i64; +typedef long i32; + +#include +#include +#include +#include +#include + +class Link; + +extern std::string gError; +typedef std::string Key; + +class Node { +public: + Node() = default; + Node(const Node& node); + virtual ~Node(); + + [[nodiscard]] virtual Node* clone() const; + + virtual bool attachNode(const std::vector& directoryPath, const Key& newKey, Node* newNode) { return false; } + virtual bool detachNode(const std::vector& directoryPath, const Key& key) { return false; } + + virtual bool detachNode(const Key& key) { return false; } + virtual bool attachNode(const Key &newKey, Node *newNode) { return false; } + + virtual void getMaxDepthUtil(ui32 depth, ui32& maxDepth) const {} + + virtual void dumpUtil(std::stringstream& ss, const Key& key, ui32 currentDepth, std::vector& indents); + + ui32 getMaxDepth() const; + + void dump(std::stringstream& ss); + + void getNodeStraightPath(Node* node, std::vector& path) const; + + virtual ui64 size() const { return 0; } + + virtual Node* getTarget() { return this; } + + virtual Node* findNode(const std::vector& path, ui32 currentDepth = 0); + + virtual Node* findNode(const Key& path) { return nullptr; } + + bool empty() const { return !size(); } + + static void indent(std::stringstream & ss, ui32 depth, std::vector& indents); + + virtual bool isDirectory() const { return false; } + virtual bool isLink() const { return false; } + virtual bool isHard() const { return false; } + +public: + Node* mParent = nullptr; + + std::vector mIncomingHardLinks; + std::vector mIncomingDynamicLinks; +}; \ No newline at end of file diff --git a/src/Directory.cpp b/src/Directory.cpp new file mode 100644 index 0000000..18342e1 --- /dev/null +++ b/src/Directory.cpp @@ -0,0 +1,130 @@ + +#include "Directory.hpp" + +#include +#include +#include + +Directory::Directory() { +} + +bool Directory::attachNode(const std::vector& directoryPath, const Key& newKey, Node* newNode) { + Node* node = findNode(directoryPath); + + if (!node) { + gError = "Invalid path"; + return false; + } + + while (node->getTarget() != node) node = node->getTarget(); + return node->attachNode(newKey, newNode); +} + +bool Directory::attachNode(const Key &newKey, Node *newNode) { + auto iterNode = mMembers.find(newKey); + + if (iterNode != mMembers.end()) { + Node* existingNode = iterNode->second; + if (!(typeid(*existingNode) == typeid(*newNode) && newNode->empty() && existingNode->empty())) { + gError = "Such node already exists"; + return false; + } + return false; // exit silently + } + + mMembers.insert({ newKey, newNode }); + newNode->mParent = this; + return true; +} + +bool Directory::detachNode(const std::vector& directoryPath, const Key& key) { + Node* node = findNode(directoryPath); + return node->detachNode(key); +} + +bool Directory::detachNode(const Key& key) { + auto removeNode = mMembers.find(key); + if (removeNode == mMembers.end()) { + gError = "Invalid path"; + return false; + } + + //if (removeNode->key.incomingLinksHard || removeNode->key.incomingLinksDynamic) { + // gError = "Cannot modify node with incoming hard links"; + // return false; + //} + + removeNode->second->mParent = nullptr; + mMembers.erase(key); + return true; +} + +Node* Directory::findNode(const Key& key) { + auto iterNode = mMembers.find(key); + return iterNode != mMembers.end() ? iterNode->second : nullptr; +} + +Node* Directory::findNode(const std::vector& path, ui32 currentDepth) { + if (path.size() == currentDepth) { + return this; + } + + const Key& key = path[currentDepth]; + auto nextNode = mMembers.find(key); + + if (nextNode == mMembers.end()) { + return nullptr; + } + + return nextNode->second->findNode(path, currentDepth + 1); +} + +void Directory::getMaxDepthUtil(ui32 depth, ui32& maxDepth) const { + if (mMembers.empty()) return; + maxDepth = std::max(depth, maxDepth); + for (auto& node : mMembers) { + node.second->getMaxDepthUtil(++depth, maxDepth); + } +} + +void Directory::dumpUtil(std::stringstream& ss, const Key& key, ui32 currentDepth, std::vector& indents) { + + indents[currentDepth] = true; + + indent(ss, currentDepth, indents); + ss << key; + ss << " [" << mIncomingHardLinks.size() << ":" << mIncomingDynamicLinks.size() << "]"; + ss << "\n"; + + currentDepth++; + + if (mMembers.empty()) return; + + const auto& lastNode = mMembers.rbegin()->first; + for (auto & member : mMembers) { + if (lastNode == member.first) indents[currentDepth - 1] = false; + member.second->dumpUtil(ss, member.first, currentDepth, indents); + } +} + +ui64 Directory::size() const { + return mMembers.size(); +} + +Directory::Directory(const Directory &node) : Node(node) { + for (auto & member : node.mMembers) { + auto newNode = member.second->clone(); + mMembers.insert({ member.first, newNode }); + newNode->mParent = this; + } +} + +Directory::~Directory() { + for (const auto& node : mMembers) { + delete node.second; + } +} + +Directory *Directory::clone() const { + return new Directory(*this); +} diff --git a/src/DirectoryTree.cpp b/src/DirectoryTree.cpp deleted file mode 100644 index e084556..0000000 --- a/src/DirectoryTree.cpp +++ /dev/null @@ -1,286 +0,0 @@ - -#include "DirectoryTree.hpp" - -#include -#include -#include - -std::string gError; -bool gDebug = true; - -Node::~Node() { - // assert(mIncomingHardLinks.empty()); - // for (auto dynamicLink : mIncomingDynamicLinks) { - // dynamicLink->mParent->detachNode(dynamicLink->mTreeNode->key.val); - // delete dynamicLink; - // -} - -Node::Node(const Node &node) { - mType = node.mType; -} - -Node *Node::clone() const { - return new Node(*this); -} - -File::File() { - mType = Type::FILE; -} - -File::File(const File& node) : Node(node) { - // nothing to do -} - -File *File::clone() const { - return new File(*this); -} - -Link::Link(Node* target, bool isHard) { - mType = LINK; - mIsHard = isHard; - mLink = target; - - if (mIsHard) { - target->mIncomingHardLinks.push_back(this); - } else { - target->mIncomingDynamicLinks.push_back(this); - } -} - -Node *Link::getLink() const { - return mLink; -} - -bool Link::isHard() const { - return mIsHard; -} - -Link::Link(const Link &node) : Node(node) { - mLink = node.mLink; - mIsHard = node.mIsHard; - - assert(mLink); - if (mIsHard) { - mLink->mIncomingHardLinks.push_back(this); - } else { - mLink->mIncomingDynamicLinks.push_back(this); - } -} - -Link *Link::clone() const { - return new Link(*this); -} - -Link::~Link() { - // assert(mLink); - // auto& links = mIsHard ? mLink->mIncomingHardLinks : mLink->mIncomingDynamicLinks; - // links.erase(std::remove(links.begin(), links.end(), this), links.end()); - // Directory::updateTreeLinkCount(mLink); - // mLink = nullptr; -} - -Directory::Directory() { - mType = DIRECTORY; -} - -bool Directory::attachNode(const std::vector& directoryPath, const Key& newKey, Node* newNode) { - Node* node = findNode(directoryPath, 0); - - while (node && node->mType == LINK) node = ((Link*)node)->getLink(); - - if (!node || node->mType != DIRECTORY) { - gError = "Invalid path"; - return false; - } - - return ((Directory*) node)->attachNode(newKey, newNode); -} - -bool Directory::attachNode(const Key &newKey, Node *newNode) { - auto iterNode = mMembers.find(newKey); - if (iterNode != mMembers.end()) { - if (iterNode->second->mType == newNode->mType) return false; // exit silently - gError = "Can not add node"; - return false; - } - - mMembers.insert({ newKey, newNode }); - - newNode->mParent = this; - return true; -} - -bool Directory::detachNode(const std::vector& directoryPath, const Key& key) { - Node* node = findNode(directoryPath, 0); - if (!node || node->mType != DIRECTORY) { - gError = "Invalid path"; - return false; - } - - return ((Directory*) node)->detachNode(key); -} - -bool Directory::detachNode(const Key& key) { - auto removeNode = mMembers.find(key); - if (removeNode == mMembers.end()) { - gError = "Invalid path"; - return false; - } - - //if (removeNode->key.incomingLinksHard || removeNode->key.incomingLinksDynamic) { - // gError = "Cannot modify node with incoming hard links"; - // return false; - //} - - removeNode->second->mParent = nullptr; - mMembers.erase(key); - return true; -} - -Node* Directory::findNode(const Key& key) { - auto iterNode = mMembers.find(key); - return iterNode != mMembers.end() ? iterNode->second : nullptr; -} - -Node* Directory::findNode(const std::vector& path, ui32 currentDepth) { - if (path.size() == currentDepth) { - return this; - } - - const Key& key = path[currentDepth]; - auto iterNode = mMembers.find(key); - - if (iterNode == mMembers.end()) { - return nullptr; - } - - Node* node = iterNode->second; - - // link on link is not allowed - while (true) { - switch (node->mType) { - case Node::FILE: - if (currentDepth == path.size() - 1) return node; - return nullptr; - - case Node::DIRECTORY: - return ((Directory*)node)->findNode(path, ++currentDepth); - - case Node::LINK: - if (currentDepth == path.size() - 1) return node; - node = ((Link*)node)->getLink(); - break; - - default: - return nullptr; - } - } -} - -void Directory::getMaxDepthUtil(ui32 depth, ui32& maxDepth) const { - if (mMembers.empty()) return; - maxDepth = std::max(depth, maxDepth); - for (auto& node : mMembers) { - if (node.second->mType == DIRECTORY) { - ((Directory*)node.second)->getMaxDepthUtil(++depth, maxDepth); - } - } -} - -ui32 Directory::getMaxDepth() const { - ui32 maxDepth = 1; - getMaxDepthUtil(1, maxDepth); - return maxDepth; -} - -void Directory::dump(std::stringstream& ss) { - std::vector indents; - indents.resize(getMaxDepth()); - dumpUtil(ss, 0, indents); - ss << "\n"; -} - -static void indent(std::stringstream & ss, ui32 depth, std::vector& indents) { - if (!depth) return; - for (auto i = 0; i < depth - 1; i++) { - ss << (indents[i] ? " |" : " "); - } - ss << " |_ "; -} - -void Directory::dumpUtil(std::stringstream& ss, ui32 currentDepth, std::vector& indents) { - if (mMembers.empty()) - return; - - indents[currentDepth] = true; - currentDepth++; - - const auto& lastNode = mMembers.rbegin()->first; - - for (auto & member : mMembers) { - if (lastNode == member.first) - indents[currentDepth - 1] = false; - - indent(ss, currentDepth, indents); - - switch (member.second->mType) { - case Node::DIRECTORY: - ss << member.first; - if (gDebug) ss << " [" << member.second->mIncomingHardLinks.size() << ":" << member.second->mIncomingDynamicLinks.size() << "]"; - ss << "\n"; - ((Directory*) member.second)->dumpUtil(ss, currentDepth, indents); - break; - - case Node::LINK: { - auto linkNode = ((Link*)member.second); - ss << member.first << (linkNode->isHard() ? " hlink[/" : " dlink[/"); - std::vector path; - getNodeStraightPath(linkNode->getLink(), path); - std::reverse(path.begin(), path.end()); - for (auto key : path) - ss << "X" << "/"; - ss << "]\n"; - break; - } - - case Node::FILE: - ss << member.first; - if (gDebug) ss << " [file]"; - ss << "\n"; - break; - - default: - ss << " ERROR \n"; - break; - } - } -} - -void Directory::getNodeStraightPath(Node* node, std::vector& path) const { - if (!node) return; - path.push_back(node); - getNodeStraightPath(node->mParent, path); -} - -ui64 Directory::size() const { - return mMembers.size(); -} - -Directory::Directory(const Directory &node) : Node(node) { - for (auto & member : node.mMembers) { - auto newNode = member.second->clone(); - mMembers.insert({ member.first, newNode }); - newNode->mParent = this; - } -} - -Directory::~Directory() { - for (auto node : mMembers) { - delete node.second; - } -} - -Directory *Directory::clone() const { - return new Directory(*this); -} diff --git a/src/FileSystem.cpp b/src/FileSystem.cpp index 089ce37..56b8a55 100644 --- a/src/FileSystem.cpp +++ b/src/FileSystem.cpp @@ -1,6 +1,9 @@ #include "FileSystem.hpp" +#include "Directory.hpp" +#include "Link.hpp" + #include #include #include @@ -21,19 +24,12 @@ bool FileSystem::makeDirectory(const Path& path) { return false; } - Directory* parentDirectory = path.isAbsolute() ? root : currentDirectory; - - auto existingNode = parentDirectory->findNode(path.getChain()); - if (existingNode) { - if (existingNode->mType == Node::DIRECTORY) return true; - gError = "Can not create directory, such node exists"; - return false; - } + Node* parentNode = path.isAbsolute() ? root : currentDirectory; auto newDirectory = new Directory(); - if (!parentDirectory->attachNode(path.getParentChain(), path.getFilename(), newDirectory)) { + if (!parentNode->attachNode(path.getParentChain(), path.getFilename(), newDirectory)) { delete newDirectory; - gError = "Invalid path"; + gError = "Invalid path or node exists"; return false; } @@ -46,17 +42,10 @@ bool FileSystem::makeFile(const Path& path) { return false; } - Directory* parentDirectory = path.isAbsolute() ? root : currentDirectory; + Node* parentNode = path.isAbsolute() ? root : currentDirectory; - auto existingNode = parentDirectory->findNode(path.getChain()); - if (existingNode) { - if (existingNode->mType == Node::FILE) return true; - gError = "Can not create file, such node exists"; - return false; - } - - auto newFile = new File(); - if (!parentDirectory->attachNode(path.getParentChain(), path.getFilename(), newFile)) { + auto newFile = new Node(); + if (!parentNode->attachNode(path.getParentChain(), path.getFilename(), newFile)) { delete newFile; gError = "Invalid path"; return false; @@ -71,20 +60,20 @@ bool FileSystem::removeDirectory(const Path& path, bool recursively) { return false; } - Directory* parentDirectory = path.isAbsolute() ? root : currentDirectory; + Node* parentNode = path.isAbsolute() ? root : currentDirectory; - auto existingNode = parentDirectory->findNode(path.getChain()); + auto existingNode = parentNode->findNode(path.getChain()); if (!existingNode) { gError = "Cant remove, such node doesnt exists"; return false; } - if (existingNode->mType != Node::DIRECTORY) { + if (!existingNode->isDirectory()) { gError = "Path is not a directory"; return false; } - if (!recursively && existingNode->mType == Node::DIRECTORY && ((Directory*)existingNode)->size()) { + if (!recursively && !existingNode->empty()) { gError = "Directory is not empty"; return false; } @@ -94,7 +83,10 @@ bool FileSystem::removeDirectory(const Path& path, bool recursively) { return false; } - assert(parentDirectory->detachNode(path.getParentChain(), path.getFilename())); + if (!parentNode->detachNode(path.getParentChain(), path.getFilename())) { + gError = "Can not detach node"; + return false; + } delete existingNode; return true; @@ -106,9 +98,8 @@ bool FileSystem::copyNode(const Path& source, const Path& target) { return false; } - // TODO : remove code duplication - Directory* workingDirectorySource = source.isAbsolute() ? root : currentDirectory; - Directory* workingDirectoryTarget = target.isAbsolute() ? root : currentDirectory; + Node* workingDirectorySource = source.isAbsolute() ? root : currentDirectory; + Node* workingDirectoryTarget = target.isAbsolute() ? root : currentDirectory; auto sourceNode = workingDirectorySource->findNode(source.getChain()); if (!sourceNode) { @@ -117,23 +108,25 @@ bool FileSystem::copyNode(const Path& source, const Path& target) { } auto targetNode = workingDirectoryTarget->findNode(target.getChain()); - if (!targetNode || targetNode->mType != Node::DIRECTORY) { + if (!targetNode) { gError = "Invalid target directory"; return false; } - auto targetDirectory = (Directory*)targetNode; - Key key = source.getFilename(); - if (targetDirectory->findNode(key)) { + if (targetNode->findNode(key)) { // gError = "Node with such name already exists in the target directory"; // return false; - key += "-copy"; + key += "_copy"; } Node* clonedNode = sourceNode->clone(); - assert(targetDirectory->attachNode(key, clonedNode)); + + if (!targetNode->attachNode(key, clonedNode)) { + delete clonedNode; + return false; + } return true; } @@ -144,39 +137,39 @@ bool FileSystem::moveNode(const Path &source, const Path &target) { return false; } - // TODO : remove code duplication - Directory* workingDirectorySource = source.isAbsolute() ? root : currentDirectory; - Directory* workingDirectoryTarget = target.isAbsolute() ? root : currentDirectory; + Node* workingDirectorySource = source.isAbsolute() ? root : currentDirectory; + Node* workingDirectoryTarget = target.isAbsolute() ? root : currentDirectory; auto sourceParentNode = workingDirectorySource->findNode(source.getParentChain()); - assert(sourceParentNode->mType == Node::DIRECTORY); - auto sourceNode = workingDirectorySource->findNode(source.getChain()); + if (!sourceParentNode) { + gError = "Invalid source path"; + return false; + } + + auto sourceNode = sourceParentNode->findNode(source.getFilename()); if (!sourceNode) { gError = "Invalid source path"; return false; } auto targetNode = workingDirectoryTarget->findNode(target.getChain()); - if (!targetNode || targetNode->mType != Node::DIRECTORY) { + if (!targetNode) { gError = "Invalid target directory"; return false; } - auto sourceParentDirectory = (Directory*)sourceParentNode; - auto targetDirectory = (Directory*)targetNode; + const Key& key = source.getFilename(); - Key key = source.getFilename(); - - if (targetDirectory->findNode(key)) { - gError = "Node with such name already exists in the target directory"; + if (sourceParentNode->isHard()) { + gError = "Node contains incoming hard links"; return false; } - if (!sourceParentDirectory->detachNode(key)) { + if (!targetNode->attachNode(key, sourceNode)) { return false; } - assert(targetDirectory->attachNode(key, sourceNode)); + assert(sourceParentNode->detachNode(key)); return true; } @@ -187,9 +180,8 @@ bool FileSystem::makeLink(const Path& source, const Path& target, bool isDynamic return false; } - // TODO : remove code duplication - Directory* workingDirectorySource = source.isAbsolute() ? root : currentDirectory; - Directory* workingDirectoryTarget = target.isAbsolute() ? root : currentDirectory; + Node* workingDirectorySource = source.isAbsolute() ? root : currentDirectory; + Node* workingDirectoryTarget = target.isAbsolute() ? root : currentDirectory; auto sourceNode = workingDirectorySource->findNode(source.getChain()); if (!sourceNode) { @@ -197,13 +189,13 @@ bool FileSystem::makeLink(const Path& source, const Path& target, bool isDynamic return false; } - if (sourceNode->mType == Node::LINK) { + if (sourceNode->isLink()) { gError = "Link on link is not allowed"; return false; } auto targetNode = workingDirectoryTarget->findNode(target.getChain()); - if (!targetNode || targetNode->mType != Node::DIRECTORY) { + if (!targetNode || !targetNode->isDirectory()) { gError = "Invalid target directory"; return false; } @@ -211,15 +203,12 @@ bool FileSystem::makeLink(const Path& source, const Path& target, bool isDynamic const Key& key = source.getFilename(); auto targetDirectory = (Directory*)targetNode; - if (targetDirectory->findNode(key)) { - gError = "Node with such name already exists in the target directory"; + auto newLink = new Link(sourceNode, !isDynamic); + if (!targetDirectory->attachNode(key, newLink)) { + delete newLink; return false; } - auto newLink = new Link(sourceNode, !isDynamic); - - assert(targetDirectory->attachNode(key, newLink)); - return true; } @@ -229,7 +218,7 @@ bool FileSystem::removeFileOrLink(const Path &path) { return false; } - Directory* parentDirectory = path.isAbsolute() ? root : currentDirectory; + Node* parentDirectory = path.isAbsolute() ? root : currentDirectory; auto existingNode = parentDirectory->findNode(path.getChain()); if (!existingNode) { @@ -237,12 +226,15 @@ bool FileSystem::removeFileOrLink(const Path &path) { return false; } - if (existingNode->mType == Node::DIRECTORY) { + if (existingNode->isDirectory()) { gError = "Path is a directory"; return false; } - assert(parentDirectory->detachNode(path.getParentChain(), path.getFilename())); + if (!parentDirectory->detachNode(path.getParentChain(), path.getFilename())) { + gError = "Can not remove node"; + return false; + } delete existingNode; return true; @@ -255,12 +247,12 @@ bool FileSystem::changeCurrent(const Path& path) { } auto node = path.isAbsolute() ? root->findNode(path.getChain(), 0) : currentDirectory->findNode(path.getChain(), 0); - if (!node || node->mType != Node::DIRECTORY) { + if (!node || !node->isDirectory()) { gError = "No such directory"; return false; } - currentDirectory = (Directory*) node; + currentDirectory = node; return true; } diff --git a/src/Interpreter.cpp b/src/Interpreter.cpp index fd5938e..d21b999 100644 --- a/src/Interpreter.cpp +++ b/src/Interpreter.cpp @@ -92,7 +92,7 @@ Interpreter::Interpreter() { } void Interpreter::reportError(const std::string& description) { - std::cout << "ERROR : " << description << std::endl; + std::cout << "ERROR : " << description << "\n\n"; } void Interpreter::printHelp() { @@ -102,13 +102,14 @@ void Interpreter::printHelp() { std::cout << command.second.description; std::cout << "\n"; } + std::cout << "\n\n"; } void Interpreter::interpret(const std::string& command) { std::vector words; getWords(command, words); - std::cout << command << "\n"; + std::cout << ">> " << command << "\n"; if (words.empty()) { reportError("Empty command"); diff --git a/src/Link.cpp b/src/Link.cpp new file mode 100644 index 0000000..d006dc7 --- /dev/null +++ b/src/Link.cpp @@ -0,0 +1,74 @@ + +#include "Link.hpp" + +#include +#include +#include + +Link::Link(Node* target, bool isHard) { + mIsHard = isHard; + mLink = target; + + if (mIsHard) { + target->mIncomingHardLinks.push_back(this); + } else { + target->mIncomingDynamicLinks.push_back(this); + } +} + +Link::Link(const Link &node) : Node(node) { + mLink = node.mLink; + mIsHard = node.mIsHard; + + assert(mLink); + if (mIsHard) { + mLink->mIncomingHardLinks.push_back(this); + } else { + mLink->mIncomingDynamicLinks.push_back(this); + } +} + +Link *Link::clone() const { + return new Link(*this); +} + +Link::~Link() { + // assert(mLink); + // auto& links = mIsHard ? mLink->mIncomingHardLinks : mLink->mIncomingDynamicLinks; + // links.erase(std::remove(links.begin(), links.end(), this), links.end()); + // Directory::updateTreeLinkCount(mLink); + // mLink = nullptr; +} + +Node *Link::getLink() const { + return mLink; +} + +bool Link::isHard() const { + return mIsHard; +} + +Node* Link::getTarget() { +return getLink(); +} + +Node* Link::findNode(const std::vector& path, ui32 currentDepth) { + if (path.size() == currentDepth) { + return this; + } + assert(mLink); + return mLink->findNode(path, currentDepth + 1); +} + + +void Link::dumpUtil(std::stringstream& ss, const Key& key, ui32 currentDepth, std::vector& indents) { + indent(ss, currentDepth, indents); + ss << key << (isHard() ? " hlink[/" : " dlink[/"); + std::vector path; + getNodeStraightPath(getLink(), path); + std::reverse(path.begin(), path.end()); + for (auto tmp : path) + ss << "X" << "/"; + ss << "]\n"; +} + diff --git a/src/Node.cpp b/src/Node.cpp new file mode 100644 index 0000000..4a0d4cd --- /dev/null +++ b/src/Node.cpp @@ -0,0 +1,64 @@ + +#include "Node.hpp" + +#include +#include +#include + +std::string gError; +bool gDebug = true; + +Node::Node(const Node &node) {} + +Node::~Node() { + // assert(mIncomingHardLinks.empty()); + // for (auto dynamicLink : mIncomingDynamicLinks) { + // dynamicLink->mParent->detachNode(dynamicLink->mTreeNode->key.val); + // delete dynamicLink; + // +} + +Node *Node::clone() const { + return new Node(*this); +} + +ui32 Node::getMaxDepth() const { + ui32 maxDepth = 1; + getMaxDepthUtil(1, maxDepth); + return maxDepth; +} + +Node* Node::findNode(const std::vector& path, ui32 currentDepth) { + if (path.size() - 1 == currentDepth) { + return this; + } + return nullptr; +} + +void Node::getNodeStraightPath(Node* node, std::vector& path) const { + if (!node) return; + path.push_back(node); + getNodeStraightPath(node->mParent, path); +} + +void Node::dump(std::stringstream& ss) { + std::vector indents; + indents.resize(getMaxDepth()); + dumpUtil(ss, "/", 0, indents); + ss << "\n"; +} + +void Node::dumpUtil(std::stringstream& ss, const Key& key, ui32 currentDepth, std::vector& indents) { + indent(ss, currentDepth, indents); + ss << key; + if (gDebug) ss << " [file]"; + ss << "\n"; +} + +void Node::indent(std::stringstream & ss, ui32 depth, std::vector& indents) { + if (!depth) return; + for (auto i = 0; i < depth - 1; i++) { + ss << (indents[i] ? " |" : " "); + } + ss << " |_ "; +} \ No newline at end of file diff --git a/src/Path.cpp b/src/Path.cpp index 7cfa212..3e65314 100644 --- a/src/Path.cpp +++ b/src/Path.cpp @@ -11,7 +11,7 @@ void initializeTransitions() { for (char i = 'A'; i <= 'Z'; i++) transitions[i] = i /* + ('a' - 'A')*/; transitions['/'] = '/'; transitions['.'] = '.'; - transitions['-'] = '-'; + transitions['_'] = '_'; } Path::Path(const std::string& path) { diff --git a/test/Tests.cpp b/test/Tests.cpp index 4837422..96fe4b6 100644 --- a/test/Tests.cpp +++ b/test/Tests.cpp @@ -24,7 +24,7 @@ int main() { interpreter.interpret("rd d"); interpreter.interpret("deltree A123"); - interpreter.interpret("deltree A123-copy"); + interpreter.interpret("deltree A123_copy"); interpreter.interpret("md a"); interpreter.interpret("md a/b"); @@ -33,11 +33,11 @@ int main() { interpreter.interpret("md a/b/c/a/k"); interpreter.interpret("copy a /"); - interpreter.interpret("mdl a/b/c /a-copy/k"); + interpreter.interpret("mdl a/b/c /a_copy/k"); - interpreter.interpret("move a/b/c /a-copy"); + interpreter.interpret("move a/b/c /a_copy"); - interpreter.interpret("del /a-copy/k/c"); + interpreter.interpret("del /a_copy/k/c"); return UnitTest::RunAllTests(); } \ No newline at end of file