|
|
|
|
|
#ifndef DLIB_DIR_NAV_KERNEl_2_ |
|
#define DLIB_DIR_NAV_KERNEl_2_ |
|
|
|
#ifdef DLIB_ISO_CPP_ONLY |
|
#error "DLIB_ISO_CPP_ONLY is defined so you can't use this OS dependent code. Turn DLIB_ISO_CPP_ONLY off if you want to use it." |
|
#endif |
|
|
|
|
|
#include "dir_nav_kernel_abstract.h" |
|
|
|
#include <string> |
|
#include "../uintn.h" |
|
#include "../algs.h" |
|
|
|
#include <sys/types.h> |
|
#include <dirent.h> |
|
#include <libgen.h> |
|
#include <limits.h> |
|
#include <unistd.h> |
|
#include <sys/stat.h> |
|
#include <errno.h> |
|
#include <stdlib.h> |
|
#include <chrono> |
|
|
|
#if !defined(__USE_LARGEFILE64 ) && !defined(_LARGEFILE64_SOURCE) |
|
#define stat64 stat |
|
#endif |
|
|
|
#include <vector> |
|
#include "../stl_checked.h" |
|
#include "../enable_if.h" |
|
#include "../queue.h" |
|
|
|
namespace dlib |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class file |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
friend class directory; |
|
|
|
struct data |
|
{ |
|
uint64 file_size; |
|
std::string name; |
|
std::string full_name; |
|
std::chrono::time_point<std::chrono::system_clock> last_modified; |
|
}; |
|
|
|
void init(const std::string& name); |
|
|
|
public: |
|
|
|
struct private_constructor{}; |
|
inline file ( |
|
const std::string& name, |
|
const std::string& full_name, |
|
const uint64 file_size, |
|
const std::chrono::time_point<std::chrono::system_clock>& last_modified, |
|
private_constructor |
|
) |
|
{ |
|
state.file_size = file_size; |
|
state.name = name; |
|
state.full_name = full_name; |
|
state.last_modified = last_modified; |
|
} |
|
|
|
|
|
class file_not_found : public error { |
|
public: file_not_found(const std::string& s): error(s){} |
|
}; |
|
|
|
inline file ( |
|
) |
|
{ |
|
state.file_size = 0; |
|
} |
|
|
|
file ( |
|
const std::string& name |
|
) { init(name); } |
|
|
|
file ( |
|
const char* name |
|
) { init(name); } |
|
|
|
inline const std::string& name ( |
|
) const { return state.name; } |
|
|
|
inline const std::string& full_name ( |
|
) const { return state.full_name; } |
|
|
|
inline uint64 size ( |
|
) const { return state.file_size; } |
|
|
|
inline std::chrono::time_point<std::chrono::system_clock> last_modified ( |
|
) const { return state.last_modified; } |
|
|
|
operator std::string ( |
|
) const { return full_name(); } |
|
|
|
bool operator == ( |
|
const file& rhs |
|
) const; |
|
|
|
bool operator != ( |
|
const file& rhs |
|
) const { return !(*this == rhs); } |
|
|
|
inline bool operator < ( |
|
const file& item |
|
) const { return full_name() < item.full_name(); } |
|
|
|
inline void swap ( |
|
file& item |
|
) |
|
{ |
|
exchange(state,item.state); |
|
} |
|
|
|
private: |
|
|
|
|
|
data state; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class directory |
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void init(const std::string& name); |
|
|
|
public: |
|
struct private_constructor{}; |
|
inline directory ( |
|
const std::string& name, |
|
const std::string& full_name, |
|
private_constructor |
|
) |
|
{ |
|
state.name = name; |
|
state.full_name = full_name; |
|
} |
|
|
|
struct data |
|
{ |
|
std::string name; |
|
std::string full_name; |
|
}; |
|
|
|
class dir_not_found : public error { |
|
public: dir_not_found(const std::string& s):error(s){} |
|
}; |
|
class listing_error : public error { |
|
public: listing_error(const std::string& s):error(s){} |
|
}; |
|
|
|
inline directory ( |
|
) |
|
{ |
|
} |
|
|
|
directory ( |
|
const std::string& name |
|
) { init(name); } |
|
|
|
directory ( |
|
const char* name |
|
) { init(name); } |
|
|
|
static char get_separator ( |
|
); |
|
|
|
template < |
|
typename queue_of_files |
|
> |
|
void get_files ( |
|
queue_of_files& files |
|
) const; |
|
|
|
template < |
|
typename queue_of_dirs |
|
> |
|
void get_dirs ( |
|
queue_of_dirs& dirs |
|
) const; |
|
|
|
std::vector<file> get_files ( |
|
) const |
|
{ |
|
std::vector<file> temp_vector; |
|
get_files(temp_vector); |
|
return temp_vector; |
|
} |
|
|
|
std::vector<directory> get_dirs ( |
|
) const |
|
{ |
|
std::vector<directory> temp_vector; |
|
get_dirs(temp_vector); |
|
return temp_vector; |
|
} |
|
|
|
const directory get_parent ( |
|
) const; |
|
|
|
inline bool is_root ( |
|
) const { return state.name.size() == 0; } |
|
|
|
inline const std::string& name ( |
|
) const { return state.name; } |
|
|
|
inline const std::string& full_name ( |
|
) const { return state.full_name; } |
|
|
|
operator std::string ( |
|
) const { return full_name(); } |
|
|
|
bool operator == ( |
|
const directory& rhs |
|
) const; |
|
|
|
bool operator != ( |
|
const directory& rhs |
|
) const { return !(*this == rhs); } |
|
|
|
inline bool operator < ( |
|
const directory& item |
|
) const { return full_name() < item.full_name(); } |
|
|
|
inline void swap ( |
|
directory& item |
|
) |
|
{ |
|
exchange(state,item.state); |
|
} |
|
|
|
private: |
|
|
|
|
|
data state; |
|
|
|
bool is_root_path ( |
|
const std::string& path |
|
) const; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
inline std::ostream& operator<< ( |
|
std::ostream& out, |
|
const directory& item |
|
) { out << (std::string)item; return out; } |
|
|
|
inline std::ostream& operator<< ( |
|
std::ostream& out, |
|
const file& item |
|
) { out << (std::string)item; return out; } |
|
|
|
|
|
|
|
inline void swap ( |
|
file& a, |
|
file& b |
|
) { a.swap(b); } |
|
|
|
|
|
|
|
inline void swap ( |
|
directory& a, |
|
directory& b |
|
) { a.swap(b); } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template < |
|
typename queue_of_files |
|
> |
|
typename disable_if<is_std_vector<queue_of_files>,void>::type |
|
directory_helper_get_files ( |
|
const directory::data& state, |
|
queue_of_files& files |
|
) |
|
{ |
|
using namespace std; |
|
|
|
files.clear(); |
|
if (state.full_name.size() == 0) |
|
throw directory::listing_error("This directory object currently doesn't represent any directory."); |
|
|
|
DIR* ffind = 0; |
|
struct dirent* data; |
|
struct stat64 buffer; |
|
|
|
try |
|
{ |
|
string path = state.full_name; |
|
|
|
if (path[path.size()-1] != directory::get_separator()) |
|
path += directory::get_separator(); |
|
|
|
|
|
ffind = opendir(state.full_name.c_str()); |
|
if (ffind == 0) |
|
{ |
|
throw directory::listing_error("Unable to list the contents of " + state.full_name); |
|
} |
|
|
|
while(true) |
|
{ |
|
errno = 0; |
|
if ( (data = readdir(ffind)) == 0) |
|
{ |
|
|
|
if ( errno == 0) |
|
{ |
|
|
|
break; |
|
} |
|
else |
|
{ |
|
|
|
throw directory::listing_error("Unable to list the contents of " + state.full_name); |
|
} |
|
} |
|
|
|
uint64 file_size; |
|
|
|
if (::stat64((path+data->d_name).c_str(), &buffer) != 0) |
|
{ |
|
|
|
|
|
char buf[PATH_MAX]; |
|
ssize_t temp = readlink((path+data->d_name).c_str(),buf,sizeof(buf)); |
|
if (temp == -1) |
|
throw directory::listing_error("Unable to list the contents of " + state.full_name); |
|
else |
|
file_size = static_cast<uint64>(temp); |
|
} |
|
else |
|
{ |
|
file_size = static_cast<uint64>(buffer.st_size); |
|
} |
|
auto last_modified = std::chrono::system_clock::from_time_t(buffer.st_mtime); |
|
#ifdef _BSD_SOURCE |
|
last_modified += std::chrono::duration_cast<std::chrono::system_clock::duration>(std::chrono::nanoseconds(buffer.st_atim.tv_nsec)); |
|
#endif |
|
|
|
if (S_ISDIR(buffer.st_mode) == 0) |
|
{ |
|
|
|
file temp( |
|
data->d_name, |
|
path+data->d_name, |
|
file_size, |
|
last_modified, |
|
file::private_constructor() |
|
); |
|
files.enqueue(temp); |
|
} |
|
} |
|
|
|
if (ffind != 0) |
|
{ |
|
while (closedir(ffind)) |
|
{ |
|
if (errno != EINTR) |
|
break; |
|
} |
|
ffind = 0; |
|
} |
|
|
|
} |
|
catch (...) |
|
{ |
|
if (ffind != 0) |
|
{ |
|
while (closedir(ffind)) |
|
{ |
|
if (errno != EINTR) |
|
break; |
|
} |
|
ffind = 0; |
|
} |
|
files.clear(); |
|
throw; |
|
} |
|
} |
|
|
|
|
|
|
|
template < |
|
typename queue_of_files |
|
> |
|
typename enable_if<is_std_vector<queue_of_files>,void>::type |
|
directory_helper_get_files ( |
|
const directory::data& state, |
|
queue_of_files& files |
|
) |
|
{ |
|
queue<file>::kernel_2a temp_files; |
|
directory_helper_get_files(state,temp_files); |
|
|
|
files.clear(); |
|
|
|
|
|
temp_files.reset(); |
|
while (temp_files.move_next()) |
|
{ |
|
files.push_back(temp_files.element()); |
|
} |
|
} |
|
|
|
|
|
|
|
template < |
|
typename queue_of_files |
|
> |
|
void directory:: |
|
get_files ( |
|
queue_of_files& files |
|
) const |
|
{ |
|
|
|
|
|
directory_helper_get_files(state,files); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
template < |
|
typename queue_of_dirs |
|
> |
|
typename disable_if<is_std_vector<queue_of_dirs>,void>::type |
|
directory_helper_get_dirs ( |
|
const directory::data& state, |
|
queue_of_dirs& dirs |
|
) |
|
{ |
|
using namespace std; |
|
|
|
dirs.clear(); |
|
if (state.full_name.size() == 0) |
|
throw directory::listing_error("This directory object currently doesn't represent any directory."); |
|
|
|
DIR* ffind = 0; |
|
struct dirent* data; |
|
struct stat64 buffer; |
|
|
|
try |
|
{ |
|
string path = state.full_name; |
|
|
|
if (path[path.size()-1] != directory::get_separator()) |
|
path += directory::get_separator(); |
|
|
|
|
|
ffind = opendir(state.full_name.c_str()); |
|
if (ffind == 0) |
|
{ |
|
throw directory::listing_error("Unable to list the contents of " + state.full_name); |
|
} |
|
|
|
while(true) |
|
{ |
|
errno = 0; |
|
if ( (data = readdir(ffind)) == 0) |
|
{ |
|
|
|
if ( errno == 0) |
|
{ |
|
|
|
break; |
|
} |
|
else |
|
{ |
|
|
|
throw directory::listing_error("Unable to list the contents of " + state.full_name); |
|
} |
|
} |
|
|
|
|
|
if (::stat64((path+data->d_name).c_str(), &buffer) != 0) |
|
{ |
|
|
|
|
|
continue; |
|
} |
|
|
|
string dtemp(data->d_name); |
|
if (S_ISDIR(buffer.st_mode) && |
|
dtemp != "." && |
|
dtemp != ".." ) |
|
{ |
|
|
|
directory temp(dtemp,path+dtemp, directory::private_constructor()); |
|
dirs.enqueue(temp); |
|
} |
|
} |
|
|
|
if (ffind != 0) |
|
{ |
|
while (closedir(ffind)) |
|
{ |
|
if (errno != EINTR) |
|
break; |
|
} |
|
ffind = 0; |
|
} |
|
|
|
} |
|
catch (...) |
|
{ |
|
if (ffind != 0) |
|
{ |
|
while (closedir(ffind)) |
|
{ |
|
if (errno != EINTR) |
|
break; |
|
} |
|
ffind = 0; |
|
} |
|
dirs.clear(); |
|
throw; |
|
} |
|
} |
|
|
|
|
|
|
|
template < |
|
typename queue_of_dirs |
|
> |
|
typename enable_if<is_std_vector<queue_of_dirs>,void>::type |
|
directory_helper_get_dirs ( |
|
const directory::data& state, |
|
queue_of_dirs& dirs |
|
) |
|
{ |
|
queue<directory>::kernel_2a temp_dirs; |
|
directory_helper_get_dirs(state,temp_dirs); |
|
|
|
dirs.clear(); |
|
|
|
|
|
temp_dirs.reset(); |
|
while (temp_dirs.move_next()) |
|
{ |
|
dirs.push_back(temp_dirs.element()); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
template < |
|
typename queue_of_dirs |
|
> |
|
void directory:: |
|
get_dirs ( |
|
queue_of_dirs& dirs |
|
) const |
|
{ |
|
|
|
|
|
directory_helper_get_dirs(state,dirs); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
template < |
|
typename queue_of_dir |
|
> |
|
typename disable_if<is_std_vector<queue_of_dir>,void>::type get_filesystem_roots ( |
|
queue_of_dir& roots |
|
) |
|
{ |
|
roots.clear(); |
|
directory dir("/"); |
|
roots.enqueue(dir); |
|
} |
|
|
|
template < |
|
typename queue_of_dir |
|
> |
|
typename enable_if<is_std_vector<queue_of_dir>,void>::type get_filesystem_roots ( |
|
std::vector<directory>& roots |
|
) |
|
{ |
|
roots.clear(); |
|
directory dir("/"); |
|
roots.push_back(dir); |
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
#ifdef NO_MAKEFILE |
|
#include "dir_nav_kernel_2.cpp" |
|
#endif |
|
|
|
#endif |
|
|
|
|