Basic tree building and size measuring.
This commit is contained in:
commit
ad5eebba94
5 changed files with 427 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
DiskUsageInteractive
|
||||
workspace/*
|
||||
*.d
|
||||
build/*
|
||||
75
Makefile
Normal file
75
Makefile
Normal 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 $@
|
||||
79
include/TreeNodeDiskUsage.hpp
Normal file
79
include/TreeNodeDiskUsage.hpp
Normal 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
204
src/TreeNodeDiskUsage.cpp
Normal 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
65
src/main.cpp
Normal 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;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue