Basic tree building and size measuring.

This commit is contained in:
Your Name 2022-01-18 23:38:16 +01:00
commit ad5eebba94
5 changed files with 427 additions and 0 deletions

4
.gitignore vendored Normal file
View file

@ -0,0 +1,4 @@
DiskUsageInteractive
workspace/*
*.d
build/*

75
Makefile Normal file
View file

@ -0,0 +1,75 @@
CXX ?= g++
# path #
SRC_PATH = src
BUILD_PATH = build
BIN_PATH = $(BUILD_PATH)/bin
# executable #
BIN_NAME = DiskUsageInteractive
# extensions #
SRC_EXT = cpp
# code lists #
# Find all source files in the source directory, sorted by
# most recently modified
SOURCES = $(shell find $(SRC_PATH) -name '*.$(SRC_EXT)' | sort -k 1nr | cut -f2-)
# Set the object file names, with the source directory stripped
# from the path, and the build path prepended in its place
OBJECTS = $(SOURCES:$(SRC_PATH)/%.$(SRC_EXT)=$(BUILD_PATH)/%.o)
# Set the dependency files that will be used to add header dependencies
DEPS = $(OBJECTS:.o=.d)
# flags #
COMPILE_FLAGS = -Wall -Wextra -g -std=gnu++0x
INCLUDES = -I include/ -I /usr/local/include
# Space-separated pkg-config libraries used by this project
LIBS =
.PHONY: default_target
default_target: release
.PHONY: release
release: export CXXFLAGS := $(CXXFLAGS) $(COMPILE_FLAGS)
release: dirs
@$(MAKE) all
.PHONY: dirs
dirs:
@echo "Creating directories"
@mkdir -p $(dir $(OBJECTS))
@mkdir -p $(BIN_PATH)
.PHONY: clean
clean:
@echo "Deleting $(BIN_NAME) symlink"
@$(RM) $(BIN_NAME)
@echo "Deleting directories"
@$(RM) -r $(BUILD_PATH)
@$(RM) -r $(BIN_PATH)
@echo "Deleting output files (PPM and CSV)"
@$(RM) *.csv
@$(RM) *.ppm
# checks the executable and symlinks to the output
.PHONY: all
all: $(BIN_PATH)/$(BIN_NAME)
@echo "Making symlink: $(BIN_NAME) -> $<"
@$(RM) $(BIN_NAME)
@ln -s $(BIN_PATH)/$(BIN_NAME) $(BIN_NAME)
# Creation of the executable
$(BIN_PATH)/$(BIN_NAME): $(OBJECTS)
@echo "Linking: $@"
$(CXX) $(OBJECTS) -o $@ ${LIBS}
# Add dependency files, if they exist
-include $(DEPS)
# Source file rules
# After the first compilation they will be joined with the rules from the
# dependency files to provide header dependencies
$(BUILD_PATH)/%.o: $(SRC_PATH)/%.$(SRC_EXT)
@echo "Compiling: $< -> $@"
$(CXX) $(CXXFLAGS) $(INCLUDES) -MP -MMD -c $< -o $@

View file

@ -0,0 +1,79 @@
#ifndef H_TreeNodeDiskUsage
#define H_TreeNodeDiskUsage
#include <iostream>
#include <string>
#include <vector>
#include <sys/stat.h>
#include <dirent.h>
#define PRINT_VAR(x); std::cout << #x << "\t= " << (x) << "\n";
/// \struct PathFilters Stores the filters on paths.
struct PathFilters
{
std::vector<std::string> const& pathFiltersInclude;
std::vector<std::string> const& pathFiltersExclude;
};
/// \class TreeNodeDiskUsage Implements a node of a tree of the files and folders on a system.
class TreeNodeDiskUsage
{
public:
TreeNodeDiskUsage(std::string const& path_, PathFilters const* pathFilters_ = NULL);
~TreeNodeDiskUsage();
/// Returns true if the path of the node matches the filters given to it. If no filters have been defined, all paths are valid.
bool MatchesFilter() const;
/// Builds the whole tree
void BuildTree();
/// Prints the tree to the standard output only including the names of the nodes
void PrintTree(unsigned int maxDepth = 0xFFFFFFFF, unsigned int depth = 0) const;
/// Returns the number of children of the node.
size_t GetChildrenCount() const;
/// Returns the total size of the node, including all its children
size_t GetTotalSize() const;
/// Returns the total size of the node on disk, including all its children
size_t GetTotalSizeOnDisk() const;
/// Returns the name of the node : /path/to/node.bla -> node.bla
std::string GetNodeName() const;
/// Returns the path of the node : /path/to/node.bla
std::string GetNodePath() const;
protected:
/// Cleans the path to remove double slashes, trailing slashes and leading and trailing whitespaces.
void SanitizePath();
/// Explores the whole tree to compute every node's size, including all their children.
void ComputeTotalSize();
/// Removes the leading character 'c'.
static std::string RemoveLeadingCharacter(std::string const& str, char c);
/// Removes the trailing character 'c'.
static std::string RemoveTrailingCharacter(std::string const& str, char c);
/// Removes contiguous occurrences of the character 'c'.
static std::string RemoveDoublesCharacter(std::string const& str, char c);
/// Replaces all occurrences of c1 by c2 in str.
static std::string ReplaceCharacterInStr(std::string str, char c1, char c2);
protected:
PathFilters const* pathFilters; //<! Pointer to filters applied to the paths.
std::string path; //<! Path of the node
std::vector<TreeNodeDiskUsage> children; //<! Stores all the children contained within the folder.
bool isFolder; //<! Indicates whether the node is a folder or a file.
size_t totalSize; //<! Total size of the node, taking all children into account.
size_t totalSizeOnDisk; //<! Total size of the node on the disk, taking all children into account.
};
#endif

204
src/TreeNodeDiskUsage.cpp Normal file
View file

@ -0,0 +1,204 @@
#include <TreeNodeDiskUsage.hpp>
TreeNodeDiskUsage::TreeNodeDiskUsage(std::string const& path_, PathFilters const* pathFilters_)
: pathFilters(pathFilters_),
path(path_),
isFolder(false),
totalSize(0),
totalSizeOnDisk(0)
{
SanitizePath();
}
TreeNodeDiskUsage::~TreeNodeDiskUsage()
{
}
bool TreeNodeDiskUsage::MatchesFilter() const
{
if(pathFilters == NULL)
return true;
if(!pathFilters->pathFiltersInclude.size() && !pathFilters->pathFiltersExclude.size())
return true;
/*
bool included = false;
for(unsigned int i = 0 ; i < pathFilters.pathFiltersInclude.size() ; i++)
//*/
return true;// Temporary
}
void TreeNodeDiskUsage::BuildTree()
{
// Build the entire tree starting from the current node
// Check that the node is included in the current search
if(!MatchesFilter())
return;
// Acquire stats on the current path
struct stat s;
// Check that path exists
if(stat(path.c_str(), &s) != 0)
return;
std::cout << "BuildTree:path = " << path << "\n";// DEBUG
isFolder = (s.st_mode & S_IFMT) == S_IFDIR;// True if the path corresponds to a folder
//PRINT_VAR((s.st_mode & S_IFMT) == S_IFLNK);// symbolic link
//PRINT_VAR((s.st_mode & S_IFMT) == S_IFREG);// regular file
if(!isFolder)
{
totalSize = s.st_size;
totalSizeOnDisk = s.st_blocks*512;// st_blocks is given in number of blocks of 512 bytes
}
else
{
totalSize = 0;
totalSizeOnDisk = 0;
// List the contents of the folder
DIR *d;
dirent *dir;
d = opendir(path.c_str());
if(d)
{
while ((dir = readdir(d)) != NULL)
{
std::string elementname = std::string(dir->d_name);
if(elementname != "." && elementname != "..")
{
// Recursively build the sub-tree
TreeNodeDiskUsage node(path + "/" + elementname, pathFilters);
node.BuildTree();
totalSize += node.totalSize;
totalSizeOnDisk += node.totalSizeOnDisk;
children.push_back(node);
}
//printf("%s\n", dir->d_name);
}
closedir(d);
}
}
}
void TreeNodeDiskUsage::PrintTree(unsigned int maxDepth, unsigned int depth) const
{
if(depth > maxDepth)
return;
for(unsigned int i = 0 ; i < depth ; i++)
std::cout << " ";
//std::cout << path << "\n";
std::cout << path << "\t" << totalSize << "\t" << totalSizeOnDisk << "\n";
if(isFolder)
for(unsigned int i = 0 ; i < children.size() ; i++)
children[i].PrintTree(maxDepth, depth+1);
}
size_t TreeNodeDiskUsage::GetChildrenCount() const { return children.size(); }
size_t TreeNodeDiskUsage::GetTotalSize() const { return totalSize; }
size_t TreeNodeDiskUsage::GetTotalSizeOnDisk() const { return totalSizeOnDisk; }
std::string TreeNodeDiskUsage::GetNodePath() const{ return path; }
std::string TreeNodeDiskUsage::GetNodeName() const
{
if(path.size() <= 1) // covers "." and "/"
return path;
if(path == "..")
return path;
// Find last "/" in path
unsigned int lastSepPos = 0;
for(int i = (int)path.size() ; i >= 0 ; i--)
if(path[i] == '/')
{
lastSepPos = i;
break;
}
return path.substr(lastSepPos+1, path.size()-lastSepPos-1);
}
void TreeNodeDiskUsage::SanitizePath()
{
// Special cases handling
if(path == "/" || path == "\\" || path == "." || path == "..")
return;
path = TreeNodeDiskUsage::ReplaceCharacterInStr(path, '\\', '/');
path = TreeNodeDiskUsage::ReplaceCharacterInStr(path, '\t', ' ');
path = TreeNodeDiskUsage::RemoveTrailingCharacter(path, ' '); // Remove trailing white spaces
path = TreeNodeDiskUsage::RemoveTrailingCharacter(path, '/'); // Remove trailing slashes
path = TreeNodeDiskUsage::RemoveLeadingCharacter(path, ' '); // Remove leading white spaces
// Remove multiple occurrences of slashes
path = TreeNodeDiskUsage::RemoveDoublesCharacter(path, '/');
}
std::string TreeNodeDiskUsage::RemoveLeadingCharacter(std::string const& str, char c)
{
unsigned int slashCounter = 0;
for(unsigned int i = 0 ; i < str.size() ; i++)
{
if(str[i] == c)
slashCounter++;
else
break;
}
if(slashCounter)
return str.substr(slashCounter, str.size());
return str;
}
std::string TreeNodeDiskUsage::RemoveTrailingCharacter(std::string const& str, char c)
{
unsigned int slashCounter = 0;
for(int i = str.size()-1 ; i >= 0 ; i--)
{
if(str[i] == c)
slashCounter++;
else
break;
}
if(slashCounter)
return str.substr(0, str.size()-slashCounter);
return str;
}
std::string TreeNodeDiskUsage::RemoveDoublesCharacter(std::string const& str, char c)
{
std::string newStr;
unsigned int slashCounter = 0;
for(unsigned int i = 0 ; i < str.size() ; i++)
{
if(str[i] == c)
slashCounter++;
else
slashCounter = 0;
if(slashCounter < 2)
newStr += str[i];
}
return newStr;
}
std::string TreeNodeDiskUsage::ReplaceCharacterInStr(std::string str, char c1, char c2)
{
for(unsigned int i = 0 ; i < str.size() ; i++)
if(str[i] == c1)
str[i] = c2;
return str;
}
void TreeNodeDiskUsage::ComputeTotalSize()
{
// Recursively add all sizes to the current node's size
//if()
}

65
src/main.cpp Normal file
View file

@ -0,0 +1,65 @@
#include <iostream>
#include <TreeNodeDiskUsage.hpp>
using std::cout;
using std::cerr;
using std::endl;
int main(int argc, char *argv[])
{
if(argc > 1)
{
if(0)
{// stat test
cout << "Reading '" << argv[1] << "'\n";
struct stat s;
PRINT_VAR(stat(argv[1], &s));
PRINT_VAR(s.st_mode);
PRINT_VAR(s.st_mode & S_IFMT);
PRINT_VAR((s.st_mode & S_IFMT) == S_IFLNK);// symbolic link
PRINT_VAR((s.st_mode & S_IFMT) == S_IFREG);// regular file
PRINT_VAR((s.st_mode & S_IFMT) == S_IFDIR);// folder
PRINT_VAR(s.st_size);
PRINT_VAR(s.st_blksize);
PRINT_VAR(s.st_blocks);
PRINT_VAR(s.st_blocks*512);// Size on disk, in bytes
}
// if(0)
{// test GetNodeName()
PRINT_VAR(TreeNodeDiskUsage("/some/path/to/victory.tar.gz").GetNodeName());
PRINT_VAR(TreeNodeDiskUsage("/some/path/to/a/folder").GetNodeName());
PRINT_VAR(TreeNodeDiskUsage("/some/path/to/a/folder/").GetNodeName());
PRINT_VAR(TreeNodeDiskUsage("/").GetNodeName());
PRINT_VAR(TreeNodeDiskUsage(".").GetNodeName());
PRINT_VAR(TreeNodeDiskUsage("..").GetNodeName());
PRINT_VAR(TreeNodeDiskUsage("/a").GetNodeName());
PRINT_VAR(TreeNodeDiskUsage("/a/b/c//////////").GetNodeName());
PRINT_VAR(TreeNodeDiskUsage("/a///b//c/").GetNodeName());
}
// if(0)
{// test GetNodePath()
PRINT_VAR(TreeNodeDiskUsage("/a/b/c//////////").GetNodePath());
PRINT_VAR(TreeNodeDiskUsage("/a///b//c/").GetNodePath());
PRINT_VAR(TreeNodeDiskUsage("////a///b//c/").GetNodePath());
PRINT_VAR(TreeNodeDiskUsage(" /a/b/c ").GetNodePath());
PRINT_VAR(TreeNodeDiskUsage(" /a/b/c/// ").GetNodePath());
PRINT_VAR(TreeNodeDiskUsage(" \\this\\\\is/sparta/// ").GetNodePath());
PRINT_VAR(TreeNodeDiskUsage(" \\this\\\\is/sparta/with spaces// ").GetNodePath());
}
if(0)
{
TreeNodeDiskUsage tree(argv[1]);
tree.BuildTree();
tree.PrintTree();
PRINT_VAR(tree.GetTotalSize());
PRINT_VAR(tree.GetTotalSizeOnDisk());
tree.PrintTree(0);
tree.PrintTree(1);
tree.PrintTree(2);
}
}
else
cerr << "Error : No path given !\n";
return 0;
}