Skip to content

Instantly share code, notes, and snippets.

@boo1ean
Created June 3, 2013 17:25
Show Gist options
  • Save boo1ean/5699745 to your computer and use it in GitHub Desktop.
Save boo1ean/5699745 to your computer and use it in GitHub Desktop.
#include <stdlib.h>
#include <dirent.h>
#include <utime.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/sendfile.h>
#include <iostream>
#include <vector>
#include <string>
#include <sstream>
#include <fstream>
#include <cstring>
#include <algorithm>
using namespace std;
#define CURRENT_DIR "."
#define TARGET_DIR "./result"
#define YES "y"
#define REQUIRED_ARGS_NUMBER 1 + 1
#define HR "========================================================\n"
// Custom types
typedef vector<string> StringVector;
class File
{
enum
{
ACCESS_ERROR = -1,
BUFFER_SIZE = 1024
};
vector<string> &split(const string &s, char delim, vector<string> &elems) {
stringstream ss(s);
string item;
while (getline(ss, item, delim)) {
elems.push_back(item);
}
return elems;
}
vector<string> split(const string &s, char delim) {
vector<string> elems;
split(s, delim, elems);
return elems;
}
protected:
string path;
public:
enum
{
PATH_SEPARATOR = '/'
};
class NotExistException : exception
{
virtual const char * what() const throw()
{
return "Node doesn't exist.";
}
};
class RemoveException : exception
{
virtual const char * what() const throw()
{
return "Can't remove file.";
}
};
class CopyException : exception
{
virtual const char * what() const throw()
{
return "Can't complete copy operation.";
}
};
static bool exists(string path)
{
return ACCESS_ERROR != access(path.c_str(), F_OK);
}
static bool isDir(string path)
{
struct stat stat = File::stat(path);
return S_IFDIR & stat.st_mode;
}
static struct stat stat(string path)
{
struct stat stat;
::stat(path.c_str(), &stat);
return stat;
}
File(string path) : path(path)
{
// Throw exception if file doesn't exist
if (!File::exists(path)) {
throw NotExistException();
}
}
inline string getPath() const { return path; }
// Get file stats
struct stat stat() const
{
return File::stat(path);
}
// Get filename
string name()
{
return split(path, PATH_SEPARATOR).back();
}
void remove()
{
if (::remove(path.c_str()) != 0) {
throw RemoveException();
}
}
void copy(string dest)
{
dest += name();
int src_fd;
int dest_fd;
struct stat src_stat;
off_t offset = 0;
// Open the input file
src_fd = open(getPath().c_str(), O_RDONLY);
if (-1 == src_fd) {
throw CopyException();
}
// Stat the input file to obtain its size
fstat(src_fd, &src_stat);
//Open the output file for writing, with the same permissions as the source file
dest_fd = open(dest.c_str(), O_WRONLY | O_CREAT, src_stat.st_mode);
if (-1 == dest_fd) {
throw CopyException();
}
// Copy the bytes from one file to the other
if (src_stat.st_size != sendfile(dest_fd, src_fd, &offset, src_stat.st_size)) {
throw CopyException();
}
close(src_fd);
close(dest_fd);
}
bool operator==(const File &other) const
{
ifstream lFile(path.c_str(), ifstream::in | ifstream::binary);
ifstream rFile(other.getPath().c_str(), ifstream::in | ifstream::binary);
if(!lFile.is_open() || !rFile.is_open()) {
return false;
}
char *lBuffer = new char[BUFFER_SIZE]();
char *rBuffer = new char[BUFFER_SIZE]();
do {
lFile.read(lBuffer, BUFFER_SIZE);
rFile.read(rBuffer, BUFFER_SIZE);
if (memcmp(lBuffer, rBuffer, BUFFER_SIZE) != 0)
{
delete[] lBuffer;
delete[] rBuffer;
return false;
}
} while (lFile.good() || rFile.good());
delete[] lBuffer;
delete[] rBuffer;
return true;
}
bool operator<(const File &other) const
{
return stat().st_mtime < other.stat().st_mtime;
}
};
class Dir : File
{
public:
typedef vector<File> List;
class InvalidNodeTypeException : exception
{
virtual const char * what() const throw()
{
return "Specified object is not a directory.";
}
};
Dir(string path) : File(path)
{
if (!File::isDir(path)) {
throw InvalidNodeTypeException();
}
}
// Get list of filenames
List files()
{
List listing;
struct dirent * de = NULL;
DIR * dir = opendir(path.c_str());
while (de = readdir(dir))
{
if (DT_DIR & de->d_type) {
continue;
}
try {
listing.push_back(File(path + de->d_name));
} catch (NotExistException e) {
cout << "Something went wrong." << endl;
exit(EXIT_FAILURE);
}
}
closedir(dir);
return listing;
}
};
void slashify(string &target)
{
if (File::PATH_SEPARATOR != target.at(target.size() - 1)) {
target += File::PATH_SEPARATOR;
}
}
int main(int argc, char * argv[])
{
string source_dir = CURRENT_DIR;
slashify(source_dir);
Dir * dir;
try {
dir = new Dir(source_dir);
} catch (File::NotExistException e) {
cout << "Directory doesn't exist: \"" << source_dir << "\"" << endl;
return EXIT_FAILURE;
} catch (Dir::InvalidNodeTypeException e) {
cout << "Specified node is not a directory: \"" << source_dir << "\"" << endl;
return EXIT_FAILURE;
}
if (-1 == mkdir(TARGET_DIR, S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) {
cout << "Can't create result directory." << endl;
return EXIT_FAILURE;
}
string target_dir = TARGET_DIR;
slashify(target_dir);
Dir::List files = dir->files();
Dir::List to_remove;
Dir::List to_copy;
do {
Dir::List bundle;
bundle.push_back(files.at(0));
files.erase(files.begin());
if (!files.size()) {
break;
}
int j = 0;
do {
if (bundle.at(0) == files.at(j)) {
bundle.push_back(files.at(j));
files.erase(files.begin() + j);
} else {
j++;
}
} while (j < files.size());
if (bundle.size() < 2) {
continue;
}
sort(bundle.begin(), bundle.end());
cout << "Duplicates group:" << endl;
for (int i = 0; i < bundle.size(); ++i) {
cout << bundle.at(i).name() << endl;
}
cout << endl;
for (int i = 0; i < bundle.size() - 1; ++i) {
to_remove.push_back(bundle.at(i));
}
to_copy.push_back(bundle.back());
} while (files.size());
if (to_copy.size()) {
cout << "List of copied files:" << endl;
for (int i = 0; i < to_copy.size(); ++i) {
to_copy.at(i).copy(target_dir);
cout << to_copy.at(i).name() << endl;
}
} else {
cout << "There are no files to copy." << endl;
}
cout << endl;
// Copy latest files
if (to_remove.size()) {
cout << "List of files to delete:" << endl;
for (int i = 0; i < to_remove.size(); ++i) {
cout << to_remove.at(i).name() << endl;
}
} else {
cout << "There are no files to delete." << endl;
}
// Remove duplicates
for (int i = 0; i < to_remove.size(); ++i) {
string decision;
cout << "Remove \"" << to_remove.at(i).name() << "\"?(y/n): ";
cin >> decision;
if (YES == decision) {
to_remove.at(i).remove();
} else {
cout << "Skip: " << to_remove.at(i).getPath() << endl;
}
}
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment