bulkrename

Bulk file/directory renaming utility, similar to rangers built-in bulkrename command
git clone git://git.deurzen.net/bulkrename
Log | Files | Refs | LICENSE

commit 7166d5124f93a16ad7109c39f6d61f9983114ed1
parent c03721848b2ee6646dda993951bb93c7363b81a4
Author: deurzen <m.vandeurzen@student.ru.nl>
Date:   Fri,  5 Apr 2019 09:18:57 +0200

refactors code

Diffstat:
MMakefile | 5++---
DREADME.md | 1-
Asrc/bulkrename.cc | 32++++++++++++++++++++++++++++++++
Asrc/bulkrename.hh | 31+++++++++++++++++++++++++++++++
Asrc/file.cc | 74++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/file.hh | 44++++++++++++++++++++++++++++++++++++++++++++
Dsrc/filehandler.cc | 40----------------------------------------
Dsrc/filehandler.hh | 34----------------------------------
Msrc/main.cc | 26++++++--------------------
Asrc/node.cc | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/node.hh | 107++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Dsrc/statehandler.cc | 32--------------------------------
Dsrc/statehandler.hh | 25-------------------------
13 files changed, 361 insertions(+), 188 deletions(-)

diff --git a/Makefile b/Makefile @@ -6,11 +6,10 @@ CXXFLAGS += -lboost_system -lboost_filesystem OBJDIR = obj SRCDIR = src +TARGET = bin/$(PROJECT) DEPS = $(OBJ_FILES:%.o=%.d) -TARGET = bin/$(PROJECT) - H_FILES := $(shell find $(SRCDIR) -name '*.hh') SRC_FILES := $(shell find $(SRCDIR) -name '*.cc') OBJ_FILES := $(patsubst src/%.cc,obj/%.o,${SRC_FILES}) @@ -18,7 +17,7 @@ OBJ_FILES := $(patsubst src/%.cc,obj/%.o,${SRC_FILES}) all: build install: - install $(RELEASE) /usr/$(BIN) + install $(TARGET) /usr/bin/$(PROJECT) bin: @[ -d bin ] || mkdir bin diff --git a/README.md b/README.md @@ -1 +0,0 @@ -# bulkrename diff --git a/src/bulkrename.cc b/src/bulkrename.cc @@ -0,0 +1,32 @@ +#include "bulkrename.hh" + +#include <fstream> +#include <iostream> + +::std::unique_ptr<bulkrename_t> +bulkrename_t::init(const ::std::string& dir) +{ + return ::std::make_unique<bulkrename_t>(dir); +} + +void +bulkrename_t::setup() +{ + tree.populate(dir_it); +} + +void +bulkrename_t::run() +{ + filehandler.write_out(tree); + + try { + filehandler.edit(); + filehandler.read_in(tree); + } catch(const ::std::runtime_error& e) { + ::std::cerr << "bulkrename: " << e.what() << ::std::endl; + exit(1); + } + + filehandler.propagate_rename(tree); +} diff --git a/src/bulkrename.hh b/src/bulkrename.hh @@ -0,0 +1,31 @@ +#ifndef __BULKRENAME_BULKRENAME_GUARD__ +#define __BULKRENAME_BULKRENAME_GUARD__ + +#include "node.hh" +#include "file.hh" + +#include <filesystem> +#include <boost/filesystem.hpp> +#include <memory> + + +class bulkrename_t +{ +public: + explicit bulkrename_t(const ::std::string& dir) + : dir_it(dir), tree(dir) + {} + + void setup(); + void run(); + + static ::std::unique_ptr<bulkrename_t> init(const ::std::string&); + +private: + ::boost::filesystem::directory_iterator dir_it; + nodetree_t tree; + filehandler_t filehandler; + +}; + +#endif//__BULKRENAME_BULKRENAME_GUARD__ diff --git a/src/file.cc b/src/file.cc @@ -0,0 +1,74 @@ +#include "file.hh" + +#include <stdio.h> +#include <sys/wait.h> +#include <set> +#include <unordered_map> + + +void +filehandler_t::write_out(const nodetree_t& tree) +{ + tree.print(out); + out.close(); +} + +void +filehandler_t::read_in(const nodetree_t& tree) +{ + static ::std::unordered_map<::std::string, ::std::set<::std::string>> names_per_directory; + + ::std::vector<file_ptr_t> files = tree.get_files(); + ::std::vector<::std::string> filenames; + + in.open(tmpfile.c_str()); + + bool conflict_found = false; + + for (auto& file : files) { + ::std::string name; + ::std::getline(in, name); + + if (!in) + throw ::std::runtime_error("amount of names does not match amount of files"); + + if (name.empty()) + throw ::std::runtime_error("file name must be non-empty"); + + auto dirname = file->get_path().parent_path().string(); + auto filename = file->get_path().filename().string(); + if (names_per_directory.count(dirname)) { + if (names_per_directory[dirname].find(name) != names_per_directory[dirname].end()) + conflict_found = true; + + names_per_directory[dirname].insert(filename); + } else { + ::std::set<::std::string> set; + set.insert(filename); + names_per_directory[dirname] = set; + } + + file->set_name(name); + } + + in.close(); + + if (conflict_found) + throw ::std::runtime_error("multiple files within a directory are given the same name"); +} + +void +filehandler_t::edit() const +{ + int ret = system((::std::string("$EDITOR ") + tmpfile).c_str()); + if (WEXITSTATUS(ret) != 0) + throw ::std::runtime_error("renaming canceled"); +} + +void +filehandler_t::propagate_rename(const nodetree_t& tree) const +{ + ::std::vector<file_ptr_t> files = tree.get_files(); + for (auto& file : files) + file->rename(); +} diff --git a/src/file.hh b/src/file.hh @@ -0,0 +1,44 @@ +#ifndef __BULKRENAME_FILE_GUARD__ +#define __BULKRENAME_FILE_GUARD__ + +#include "node.hh" + +#include <fstream> +#include <string.h> +#include <iostream> +#include <string> +#include <cstdio> + + +class filehandler_t +{ +public: + filehandler_t() + { + char* tmpname = strdup("/tmp/tmpfileXXXXXXXXXX"); + mkstemp(tmpname); + tmpfile = tmpname; + free(tmpname); + out = ::std::ofstream(tmpfile); + } + + ~filehandler_t() + { + out.close(); + in.close(); + } + + void write_out(const nodetree_t&); + void read_in(const nodetree_t&); + + void edit() const; + void propagate_rename(const nodetree_t&) const; + +private: + ::std::string tmpfile; + ::std::ifstream in; + ::std::ofstream out; + +}; + +#endif//__BULKRENAME_FILE_GUARD__ diff --git a/src/filehandler.cc b/src/filehandler.cc @@ -1,40 +0,0 @@ -#include "filehandler.hh" - -#include <cstdlib> -#include <cstring> - - -void -FileHandler::write_out(const ::std::vector<Node*> &nodes) -{ - for (Node* n : nodes) { - out << n->getfile()->name() << ::std::endl; - } - out.close(); -} - -void -FileHandler::read_in(const ::std::vector<Node*> &nodes) -{ - in.open(tmpfile.c_str()); - - for (Node* n : nodes) { - ::std::string name; - ::std::getline(in, name); - if(!in) - throw ::std::runtime_error("amount of new files does not match amount of old files"); - - n->getfile()->setnewname(name); - } - - in.close(); -} - -void -FileHandler::rename(const ::std::vector<Node*> &nodes) const -{ - for (Node* n : nodes) { - File* file = n->getfile(); - if (file->valid()) file->move(n->name()); - } -} diff --git a/src/filehandler.hh b/src/filehandler.hh @@ -1,34 +0,0 @@ -#ifndef __BULKRENAME_FILEHANDLER_GUARD__ -#define __BULKRENAME_FILEHANDLER_GUARD__ - -#include "node.hh" - -#include <fstream> -#include <iostream> -#include <string> -#include <vector> -#include <cstdio> - -class FileHandler { -public: - FileHandler() { - char *tmpname = strdup("/tmp/tmpfileXXXXXXXXXX"); - mkstemp(tmpname); - out = ::std::ofstream(tmpname); - tmpfile = tmpname; - } - - ~FileHandler() { remove(tmpfile.c_str()); }; - - void write_out(const ::std::vector<Node*>& nodes); - void edit() const { system((::std::string("vim ")+tmpfile).c_str()); } - void read_in(const ::std::vector<Node*>& nodes); - void rename(const ::std::vector<Node*>& nodes) const; - -private: - ::std::string tmpfile; - ::std::ifstream in; - ::std::ofstream out; -}; - -#endif//__BULKRENAME_FILEHANDLER_GUARD__ diff --git a/src/main.cc b/src/main.cc @@ -1,30 +1,16 @@ -#include "statehandler.hh" -#include "filehandler.hh" +#include "bulkrename.hh" #include <string> -#include <boost/filesystem.hpp> +#include <filesystem> int -main(int argc, char **argv) +main(int argc, char** argv) { - using ::boost::filesystem::recursive_directory_iterator; + auto brn = bulkrename_t::init(argc > 1 ? argv[1] : "."); - StateHandler sh; - recursive_directory_iterator dir( (argc > 1) ? argv[1] : "." ); - sh.populate(dir); - - FileHandler fh; - fh.write_out(sh.getnodes()); - fh.edit(); - - try { - fh.read_in(sh.getnodes()); - } catch(const ::std::runtime_error& e) { - ::std::cerr << "bulkrename: " << e.what() << ::std::endl; - } - - fh.rename(sh.getnodes()); + brn->setup(); + brn->run(); return 0; } diff --git a/src/node.cc b/src/node.cc @@ -0,0 +1,98 @@ +#include "node.hh" + +void +dir_t::populate(::boost::filesystem::directory_iterator dir_it) +{ + for (auto& entry : dir_it) { + if (::boost::filesystem::is_directory(entry)) { + dir_ptr_t dir = new dir_t(entry.path(), parent); + children.push_back(dir); + dir->populate(::boost::filesystem::directory_iterator(entry)); + } else if (::boost::filesystem::is_regular_file(entry)) { + file_ptr_t file = new file_t(entry.path(), parent); + children.push_back(file); + } + } +} + + +void +nodetree_t::populate(::boost::filesystem::directory_iterator dir_it) +{ + root->populate(dir_it); +} + +void +file_t::print(::std::ostream& out) const +{ + out << path.filename().string() << ::std::endl; +} + +void +dir_t::print(::std::ostream& out) const +{ + for (auto& child : children) + child->print(out); +} + +void +nodetree_t::print(::std::ostream& out) const +{ + root->print(out); +} + +::std::vector<file_ptr_t> +dir_t::file_leaves() const +{ + ::std::vector<file_ptr_t> files; + for (auto& child : children) + if (child->get_type() == nodetype_t::dir) + for (auto& file : dynamic_cast<dir_ptr_t>(child)->file_leaves()) + files.push_back(file); + else + files.push_back(dynamic_cast<file_ptr_t>(child)); + + return files; +} + +void +file_t::rename() const +{ + if (name != new_name) + ::boost::filesystem::rename(path, path.parent_path().string() + "/" + new_name); +} + +::std::vector<file_ptr_t> +nodetree_t::get_files() const +{ + return root->file_leaves(); +} + + +::std::ostream& +operator<<(::std::ostream& out, node_ptr_t node) +{ + node->print(out); + return out; +} + +::std::ostream& +operator<<(::std::ostream& out, file_ptr_t file) +{ + + return out; +} + +::std::ostream& +operator<<(::std::ostream& out, dir_ptr_t dir) +{ + + return out; +} + +::std::ostream& +operator<<(::std::ostream& out, nodetree_t tree) +{ + tree.print(out); + return out; +} diff --git a/src/node.hh b/src/node.hh @@ -1,69 +1,110 @@ #ifndef __BULKRENAME_NODE_GUARD__ #define __BULKRENAME_NODE_GUARD__ -#include <string> -#include <utility> -#include <boost/filesystem/operations.hpp> +#include <boost/filesystem.hpp> +#include <fstream> #include <iostream> + enum class nodetype_t { file, dir }; -class File; -class Directory; -class Node { +typedef class node_t* node_ptr_t; + +typedef class node_t +{ public: - nodetype_t type() const { return t; } - ::std::string name() const { return n; } + /* node_t(nodetype_t _type, node_ptr_t _parent, const ::std::string& _name) */ + node_t(nodetype_t _type, node_ptr_t _parent, const ::boost::filesystem::path& _path) + : type(_type), parent(_parent), path(_path) + {} - virtual const Node* fwd() const = 0; - virtual File* getfile() = 0; - virtual void setnewname(const ::std::string& s) = 0; + virtual bool has_next() const = 0; + virtual void print(::std::ostream& out) const = 0; + + const nodetype_t get_type() const { return type; } + const ::boost::filesystem::path get_path() const { return path; } protected: - explicit Node(nodetype_t type, ::std::string s): t(type), n(::std::move(s)) {} + nodetype_t type; + node_ptr_t parent; + ::boost::filesystem::path path; + ::std::string name; - nodetype_t t{}; - ::std::string n{}; +}* node_ptr_t; + +::std::ostream& operator<<(::std::ostream&, node_ptr_t); -}; -class File: public Node { +typedef class file_t : public node_t +{ public: - explicit File(const ::std::string& s): Node(nodetype_t::file, s), nn(Node::name()) {} + explicit file_t(const ::boost::filesystem::path& path, node_ptr_t parent) + : node_t(nodetype_t::file, parent, path), new_name(path.string()) + {} - const Node* fwd() const override { return nullptr; }; - File* getfile() override { return this; } + bool has_next() const override { return false; } + void print(::std::ostream& out) const override; - void setnewname(const ::std::string& s) override { nn = s; } - ::std::string getnewname() { return nn; } - bool valid() { return nn != name(); } + void set_name(const ::std::string& name) { new_name = name; } - void move(const ::std::string& prefix) const { - ::boost::filesystem::rename(prefix + name(), prefix + nn); - } + void rename() const; private: - ::std::string nn{}; + ::std::string new_name; -}; +}* file_ptr_t; + +::std::ostream& operator<<(::std::ostream&, file_ptr_t); + + +typedef class dir_t : public node_t +{ +public: + explicit dir_t(const ::boost::filesystem::path& path, node_ptr_t parent) + : node_t(nodetype_t::dir, parent, path) + {} + + void populate(::boost::filesystem::directory_iterator); + bool has_next() const override { return !children.empty(); } + void print(::std::ostream& out) const override; + + ::std::vector<file_ptr_t> file_leaves() const; + +private: + ::std::vector<node_ptr_t> children; + +}* dir_ptr_t; -class Directory: public Node { +::std::ostream& operator<<(::std::ostream&, dir_ptr_t); + + +class nodetree_t +{ public: - explicit Directory(const ::std::string& s, Node& n): Node(nodetype_t::dir, s), next(&n) {} + explicit nodetree_t(const ::std::string& _root_name) + : root_name(_root_name), root(new dir_t(_root_name, nullptr)) + {} - const Node* fwd() const override { return next; } - File* getfile() override { return next->getfile(); } + nodetree_t& operator=(const nodetree_t&) = delete; + nodetree_t(const nodetree_t&) = delete; - void setnewname(const ::std::string& s) override { } + void populate(::boost::filesystem::directory_iterator); + void print(::std::ostream& out) const; + + ::std::vector<file_ptr_t> get_files() const; private: - Node* next; + ::std::string root_name; + dir_ptr_t root; }; +::std::ostream& operator<<(::std::ostream&, nodetree_t); + + #endif//__BULKRENAME_NODE_GUARD__ diff --git a/src/statehandler.cc b/src/statehandler.cc @@ -1,32 +0,0 @@ -#include "statehandler.hh" -#include "node.hh" - -#include <iostream> - - -void -StateHandler::populate(::boost::filesystem::recursive_directory_iterator dir) -{ - ::boost::filesystem::recursive_directory_iterator end; - for (; dir != end; ++dir) - if (is_regular_file(dir->path())) - nodes.push_back(convert(dir->path())); -} - -Node* -StateHandler::convert(::boost::filesystem::path path) -{ - ::std::string dir, file; - dir = path.parent_path().string() + "/"; - file = path.filename().string(); - - File* f = new File(file); - return new Directory(dir, *f); -} - -void -StateHandler::print() -{ - for (Node* f : nodes) - ::std::cout << f->name() << f->fwd()->name() << ::std::endl; -} diff --git a/src/statehandler.hh b/src/statehandler.hh @@ -1,25 +0,0 @@ -#ifndef __BULKRENAME_STATEHANDLER_GUARD__ -#define __BULKRENAME_STATEHANDLER_GUARD__ - -#include <boost/filesystem/operations.hpp> -#include "node.hh" - -#include <vector> -#include <string> - -class StateHandler { -public: - StateHandler() = default; - ~StateHandler() = default; - - void populate(::boost::filesystem::recursive_directory_iterator dir); - Node* convert(::boost::filesystem::path path); - - void print(); - const std::vector<Node*> getnodes() const { return nodes; } - -private: - std::vector<Node*> nodes; -}; - -#endif//__BULKRENAME_STATEHANDLER_GUARD__