Created
June 2, 2013 11:49
-
-
Save boo1ean/5693413 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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