Skip to content

Instantly share code, notes, and snippets.

@boo1ean
Created June 2, 2013 11:49
Show Gist options
  • Save boo1ean/5693413 to your computer and use it in GitHub Desktop.
Save boo1ean/5693413 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>
using namespace std;
#define YES "yes"
#define REQUIRED_ARGS_NUMBER 1 + 2
#define HR "========================================================"
// Custom types
typedef vector<string> StringVector;
class FileOperation
{
public:
enum
{
TYPE_COPY
};
enum
{
STATUS_SUCCESS
};
virtual int execute() = 0;
};
class File
{
enum
{
ACCESS_ERROR = -1
};
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.";
}
};
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; }
// Change file modification time
void touch()
{
utime(path.c_str(), NULL);
}
// Get file stats
struct stat stat()
{
return File::stat(path);
}
// Get file permissions
mode_t permissions()
{
return stat().st_mode;
}
// Get filename
string name()
{
return split(path, PATH_SEPARATOR).back();
}
};
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))
{
// Skip directories
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;
}
};
class Copy : FileOperation
{
File * source;
string dest;
public:
class Exception : exception
{
virtual const char * what() const throw()
{
return "Can't complete copy operation.";
}
};
Copy(File * source, string dest) : source(source), dest(dest) { }
int execute()
{
int src_fd;
int dest_fd;
struct stat src_stat;
off_t offset = 0;
// Open the input file
src_fd = open(source->getPath().c_str(), O_RDONLY);
if (-1 == src_fd) {
throw Exception();
}
// 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 Exception();
}
// 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 Exception();
}
close(src_fd);
close(dest_fd);
return STATUS_SUCCESS;
}
};
enum
{
OTHER_FULL_PERMISSIONS = S_IRWXO | S_IROTH | S_IXOTH
};
void copy(File &node, string dest_path)
{
Copy copy(&node, dest_path);
try {
copy.execute();
cout << "Copy: " << node.getPath() << endl;
} catch (Copy::Exception e) {
cout << "File copying error " << node.getPath() << endl;
}
}
void usage()
{
cout << "Usage:" << endl;
cout << "my_copy path/to/source/dir path/to/dest/dir" << endl;
}
void slashify(string &target)
{
if (File::PATH_SEPARATOR != target.at(target.size() - 1)) {
target += File::PATH_SEPARATOR;
}
}
bool copyCondition(File &node)
{
return S_IROTH == (OTHER_FULL_PERMISSIONS & node.permissions());
}
int main(int argc, char * argv[])
{
if (REQUIRED_ARGS_NUMBER != argc) {
usage();
return EXIT_FAILURE;
}
string source_dir = argv[1];
string dest_dir = argv[2];
slashify(source_dir);
slashify(dest_dir);
if (!File::isDir(dest_dir)) {
cout << "No such directory found: \"" << dest_dir << "\"" << endl;;
return EXIT_FAILURE;
}
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 source node is not a directory: \"" << source_dir << "\"" << endl;
return EXIT_FAILURE;
}
cout << "Source dir: \"" << source_dir << "\"" << endl;
cout << "Dest dir: \"" << dest_dir << "\"" << endl << HR << endl;
Dir::List files = dir->files();
for (int i = 0; i < files.size(); ++i)
{
File node = files.at(i);
if (copyCondition(node)) {
string dest_path = dest_dir + node.name();
// Copy if dest file doesn't exist
if (!File::exists(dest_path)) {
copy(node, dest_path);
} else {
// If file already exists ask if need to replace it
string decision;
cout << "File \"" << dest_path << "\" already exists, replace?(yes/no): ";
cin >> decision;
if (YES == decision) {
copy(node, dest_path);
} else {
cout << "Skip: " << node.getPath() << endl;
}
}
}
}
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment