diff --git a/build/vc2013/nana.vcxproj b/build/vc2013/nana.vcxproj
index 5f16d613..9547b689 100644
--- a/build/vc2013/nana.vcxproj
+++ b/build/vc2013/nana.vcxproj
@@ -177,6 +177,7 @@
+
diff --git a/build/vc2013/nana.vcxproj.filters b/build/vc2013/nana.vcxproj.filters
index 1b098164..cb51dab8 100644
--- a/build/vc2013/nana.vcxproj.filters
+++ b/build/vc2013/nana.vcxproj.filters
@@ -300,6 +300,9 @@
Source Files\nana\gui\widgets
+
+ Source Files\nana\filesystem
+
diff --git a/include/nana/filesystem/filesystem.hpp b/include/nana/filesystem/filesystem.hpp
index f71504b7..1f387006 100644
--- a/include/nana/filesystem/filesystem.hpp
+++ b/include/nana/filesystem/filesystem.hpp
@@ -49,20 +49,81 @@ namespace nana
{
namespace filesystem
{
- using path = nana::string;
+ enum class file_type
+ {
+ none = 0, ///< has not been determined or an error occurred while trying to determine
+ not_found = -1, ///< Pseudo-type: file was not found. Is not considered an error
+ regular = 1,
+ directory = 2 ,
+ symlink =3, ///< Symbolic link file
+ block =4, ///< Block special file
+ character= 5 , ///< Character special file
+ fifo = 6 , ///< FIFO or pipe file
+ socket =7,
+ unknown= 8 ///< The file does exist, but is of an operating system dependent type not covered by any of the other
+ };
+
+ enum class error { none = 0 };
+
+ struct attribute
+ {
+ long long bytes;
+ bool is_directory;
+ tm modified;
+ };
+
+ bool file_attrib(const nana::string& file, attribute&);
+ long long filesize(const nana::string& file);
+
+ bool mkdir(const nana::string& dir, bool & if_exist);
+ bool modified_file_time(const nana::string& file, struct tm&);
+
+ nana::string path_user();
+ nana::string path_current();
+
+ bool rmfile(const nana::char_t* file);
+ bool rmdir(const nana::char_t* dir, bool fails_if_not_empty);
+ nana::string root(const nana::string& path);
+
+ /// concerned only with lexical and syntactic aspects and does not necessarily exist in
+ /// external storage, and the pathname is not necessarily valid for the current operating system
+ /// or for a particular file system
+ /// A sequence of elements that identify the location of a file within a filesystem.
+ /// The elements are the:
+ /// rootname (opt), root-directory (opt), and an optional sequence of filenames.
+ /// The maximum number of elements in the sequence is operating system dependent.
+ class path
+ {
+ public:
+ path();
+ path(const nana::string&);
+
+ bool empty() const;
+ path root() const;
+ file_type what() const;
+
+ nana::string name() const;
+ private:
+#if defined(NANA_WINDOWS)
+ nana::string text_;
+#else
+ std::string text_;
+#endif
+ };
struct directory_entry
{
- directory_entry();
- directory_entry(const nana::string& filename, bool is_directory, unsigned long size)
- :name{filename}, size{size}, directory{is_directory}
- {}
- const path& path() const noexcept{return name;}
- //operator const path&() const noexcept;
-
- nana::string name;
+ path m_path;
unsigned long size;
bool directory;
+
+ directory_entry();
+ directory_entry(const nana::string& filename, bool is_directory, unsigned long size)
+ :m_path{filename}, size{size}, directory{is_directory}
+ {}
+ operator const path&() const noexcept;
+ const path& path() const noexcept{return m_path;}
+
};
@@ -100,7 +161,7 @@ namespace filesystem
bool equal(const directory_iterator& x) const
{
if(end_ && (end_ == x.end_)) return true;
- return (value_.name == x.value_.name);
+ return (value_.path().name() == x.value_.path().name());
}
private:
template
diff --git a/source/filesystem/filesystem.cpp b/source/filesystem/filesystem.cpp
new file mode 100644
index 00000000..e79f786a
--- /dev/null
+++ b/source/filesystem/filesystem.cpp
@@ -0,0 +1,442 @@
+/*
+ * A FileSystem Utility Implementation
+ * Copyright(C) 2003-2013 Jinhao(cnjinhao@hotmail.com)
+ *
+ * Distributed under the Boost Software License, Version 1.0.
+ * (See accompanying file LICENSE_1_0.txt or copy at
+ * http://www.boost.org/LICENSE_1_0.txt)
+ *
+ * @file: nana/filesystem/filesystem.cpp
+ * @description:
+ * provide some interface for file managment
+ */
+
+#include
+#include
+#if defined(NANA_WINDOWS)
+ #include
+
+ #if defined(NANA_MINGW)
+ #ifndef _WIN32_IE
+ #define _WIN32_IE 0x0500
+ #endif
+ #endif
+
+ #include
+ #include
+#elif defined(NANA_LINUX)
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+ #include
+#endif
+
+namespace nana
+{
+namespace filesystem
+{
+//Because of No wide character version of POSIX
+#if defined(NANA_LINUX)
+ typedef std::string string_t;
+ const char* splstr = "/\\";
+#else
+ typedef nana::string string_t;
+ const nana::char_t* splstr = STR("/\\");
+#endif
+ //class path
+ path::path(){}
+
+ path::path(const nana::string& text)
+#if defined(NANA_WINDOWS)
+ :text_(text)
+ {
+#else
+ :text_(nana::charset(text))
+ {
+#endif
+ auto pos = text_.find_last_of(splstr);
+ for(; (pos != string_t::npos) && (pos + 1 == text_.size()); pos = text_.find_last_of(splstr))
+ text_.erase(pos);
+ }
+
+ bool path::empty() const
+ {
+#if defined(NANA_WINDOWS)
+ return (::GetFileAttributes(text_.c_str()) == INVALID_FILE_ATTRIBUTES);
+#elif defined(NANA_LINUX)
+ struct stat sta;
+ return (::stat(text_.c_str(), &sta) == -1);
+#endif
+ }
+
+ path path::root() const
+ {
+ #if defined(NANA_WINDOWS)
+ return path(filesystem::root(text_));
+ #elif defined(NANA_LINUX)
+ return path(filesystem::root(nana::charset(text_)));
+ #endif
+ }
+
+ file_type path::what() const
+ {
+#if defined(NANA_WINDOWS)
+ unsigned long attr = ::GetFileAttributes(text_.c_str());
+ if(INVALID_FILE_ATTRIBUTES == attr)
+ return file_type:: not_found ; //??
+
+ if(FILE_ATTRIBUTE_DIRECTORY & attr)
+ return file_type::directory;
+
+ return file_type::regular;
+#elif defined(NANA_LINUX)
+ struct stat sta;
+ if(-1 == ::stat(text_.c_str(), &sta))
+ return file_type:: not_found ; //??
+
+ if((S_IFDIR & sta.st_mode) == S_IFDIR)
+ return file_type::directory;
+
+ if((S_IFREG & sta.st_mode) == S_IFREG)
+ return file_type::regular;
+
+ return file_type::none;
+#endif
+ }
+
+ nana::string path::name() const
+ {
+ string_t::size_type pos = text_.find_last_of(splstr);
+#if defined(NANA_WINDOWS)
+ return text_.substr(pos + 1);
+#else
+ return nana::charset(text_.substr(pos + 1));
+#endif
+ }
+ //end class path
+
+ namespace detail
+ {
+ //rm_dir_recursive
+ //@brief: remove a directory, if it is not empty, recursively remove it's subfiles and sub directories
+ bool rm_dir_recursive(nana::string&& dir)
+ {
+ std::vector files;
+ nana::string path = dir;
+ path += '\\';
+
+ std::copy(directory_iterator(dir), directory_iterator(), std::back_inserter(files));
+
+ for(auto & f : files)
+ {
+ if(f.directory)
+ rm_dir_recursive(path + f.path().name());
+ else
+ rmfile((path + f.path().name()).c_str());
+ }
+
+ return rmdir(dir.c_str(), true);
+ }
+
+ bool mkdir_helper(const nana::string& dir, bool & if_exist)
+ {
+#if defined(NANA_WINDOWS)
+ if(::CreateDirectory(dir.c_str(), 0))
+ {
+ if_exist = false;
+ return true;
+ }
+
+ if_exist = (::GetLastError() == ERROR_ALREADY_EXISTS);
+#elif defined(NANA_LINUX)
+ if(0 == ::mkdir(static_cast(nana::charset(dir)).c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH))
+ {
+ if_exist = false;
+ return true;
+ }
+
+ if_exist = (errno == EEXIST);
+#endif
+ return false;
+ }
+
+#if defined(NANA_WINDOWS)
+ void filetime_to_c_tm(FILETIME& ft, struct tm& t)
+ {
+ FILETIME local_file_time;
+ if(::FileTimeToLocalFileTime(&ft, &local_file_time))
+ {
+ SYSTEMTIME st;
+ ::FileTimeToSystemTime(&local_file_time, &st);
+ t.tm_year = st.wYear - 1900;
+ t.tm_mon = st.wMonth - 1;
+ t.tm_mday = st.wDay;
+ t.tm_wday = st.wDayOfWeek - 1;
+ t.tm_yday = nana::date::day_in_year(st.wYear, st.wMonth, st.wDay);
+
+ t.tm_hour = st.wHour;
+ t.tm_min = st.wMinute;
+ t.tm_sec = st.wSecond;
+ }
+ }
+#endif
+ }//end namespace detail
+
+ bool file_attrib(const nana::string& file, attribute& attr)
+ {
+#if defined(NANA_WINDOWS)
+ WIN32_FILE_ATTRIBUTE_DATA fad;
+ if(::GetFileAttributesEx(file.c_str(), GetFileExInfoStandard, &fad))
+ {
+ LARGE_INTEGER li;
+ li.u.LowPart = fad.nFileSizeLow;
+ li.u.HighPart = fad.nFileSizeHigh;
+ attr.bytes = li.QuadPart;
+ attr.is_directory = (0 != (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY));
+ detail::filetime_to_c_tm(fad.ftLastWriteTime, attr.modified);
+ return true;
+ }
+#elif defined(NANA_LINUX)
+ struct stat fst;
+ if(0 == ::stat(static_cast(nana::charset(file)).c_str(), &fst))
+ {
+ attr.bytes = fst.st_size;
+ attr.is_directory = (0 != (040000 & fst.st_mode));
+ attr.modified = *(::localtime(&fst.st_ctime));
+ return true;
+ }
+#endif
+ return false;
+ }
+
+ long long filesize(const nana::string& file)
+ {
+#if defined(NANA_WINDOWS)
+ //Some compilation environment may fail to link to GetFileSizeEx
+ typedef BOOL (__stdcall *GetFileSizeEx_fptr_t)(HANDLE, PLARGE_INTEGER);
+ GetFileSizeEx_fptr_t get_file_size_ex = reinterpret_cast(::GetProcAddress(::GetModuleHandleA("Kernel32.DLL"), "GetFileSizeEx"));
+ if(get_file_size_ex)
+ {
+ HANDLE handle = ::CreateFile(file.c_str(), GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ if(INVALID_HANDLE_VALUE != handle)
+ {
+ LARGE_INTEGER li;
+ if(!get_file_size_ex(handle, &li))
+ li.QuadPart = 0;
+
+ ::CloseHandle(handle);
+ return li.QuadPart;
+ }
+ }
+ return 0;
+#elif defined(NANA_LINUX)
+ FILE * stream = ::fopen(static_cast(nana::charset(file)).c_str(), "rb");
+ long long size = 0;
+ if(stream)
+ {
+ fseeko64(stream, 0, SEEK_END);
+ size = ftello64(stream);
+ fclose(stream);
+ }
+ return size;
+#endif
+ }
+
+ bool modified_file_time(const nana::string& file, struct tm& t)
+ {
+#if defined(NANA_WINDOWS)
+ WIN32_FILE_ATTRIBUTE_DATA attr;
+ if(::GetFileAttributesEx(file.c_str(), GetFileExInfoStandard, &attr))
+ {
+ FILETIME local_file_time;
+ if(::FileTimeToLocalFileTime(&attr.ftLastWriteTime, &local_file_time))
+ {
+ SYSTEMTIME st;
+ ::FileTimeToSystemTime(&local_file_time, &st);
+ t.tm_year = st.wYear - 1900;
+ t.tm_mon = st.wMonth - 1;
+ t.tm_mday = st.wDay;
+ t.tm_wday = st.wDayOfWeek - 1;
+ t.tm_yday = nana::date::day_in_year(st.wYear, st.wMonth, st.wDay);
+
+ t.tm_hour = st.wHour;
+ t.tm_min = st.wMinute;
+ t.tm_sec = st.wSecond;
+ return true;
+ }
+ }
+#elif defined(NANA_LINUX)
+ struct stat attr;
+ if(0 == ::stat(static_cast(nana::charset(file)).c_str(), &attr))
+ {
+ t = *(::localtime(&attr.st_ctime));
+ return true;
+ }
+#endif
+ return false;
+ }
+
+ bool mkdir(const nana::string& path, bool & if_exist)
+ {
+ if_exist = false;
+ if(path.size() == 0) return false;
+
+ nana::string root;
+#if defined(NANA_WINDOWS)
+ if(path.size() > 3 && path[1] == STR(':'))
+ root = path.substr(0, 3);
+#elif defined(NANA_LINUX)
+ if(path[0] == STR('/'))
+ root = '/';
+#endif
+ bool mkstat = false;
+ std::size_t beg = root.size();
+
+ while(true)
+ {
+ beg = path.find_first_not_of(STR("/\\"), beg);
+ if(beg == path.npos)
+ break;
+
+ std::size_t pos = path.find_first_of(STR("/\\"), beg + 1);
+ if(pos != path.npos)
+ {
+ root += path.substr(beg, pos - beg);
+
+ mkstat = detail::mkdir_helper(root, if_exist);
+ if(mkstat == false && if_exist == false)
+ return false;
+
+#if defined(NANA_WINDOWS)
+ root += STR('\\');
+#elif defined(NANA_LINUX)
+ root += STR('/');
+#endif
+ }
+ else
+ {
+ if(beg + 1 < path.size())
+ {
+ root += path.substr(beg);
+ mkstat = detail::mkdir_helper(root, if_exist);
+ }
+ break;
+ }
+ beg = pos + 1;
+ }
+ return mkstat;
+ }
+
+ bool rmfile(const nana::char_t* file)
+ {
+#if defined(NANA_WINDOWS)
+ bool ret = false;
+ if(file)
+ {
+ ret = (::DeleteFile(file) == TRUE);
+ if(!ret)
+ ret = (ERROR_FILE_NOT_FOUND == ::GetLastError());
+ }
+
+ return ret;
+#elif defined(NANA_LINUX)
+ if(std::remove(static_cast(nana::charset(file)).c_str()))
+ return (errno == ENOENT);
+ return true;
+#endif
+ }
+
+ bool rmdir(const nana::char_t* dir, bool fails_if_not_empty)
+ {
+ bool ret = false;
+ if(dir)
+ {
+#if defined(NANA_WINDOWS)
+ ret = (::RemoveDirectory(dir) == TRUE);
+ if(!fails_if_not_empty && (::GetLastError() == ERROR_DIR_NOT_EMPTY))
+ ret = detail::rm_dir_recursive(dir);
+#elif defined(NANA_LINUX)
+ std::string mbstr = nana::charset(dir);
+ if(::rmdir(mbstr.c_str()))
+ {
+ if(!fails_if_not_empty && (errno == EEXIST || errno == ENOTEMPTY))
+ ret = detail::rm_dir_recursive(dir);
+ }
+ else
+ ret = true;
+#endif
+ }
+ return ret;
+ }
+
+ nana::string root(const nana::string& path)
+ {
+ std::size_t index = path.size();
+
+ if(index)
+ {
+ const nana::char_t * str = path.c_str();
+
+ for(--index; index > 0; --index)
+ {
+ nana::char_t c = str[index];
+ if(c != '\\' && c != '/')
+ break;
+ }
+
+ for(--index; index > 0; --index)
+ {
+ nana::char_t c = str[index];
+ if(c == '\\' || c == '/')
+ break;
+ }
+ }
+
+ return index?path.substr(0, index + 1):nana::string();
+ }
+
+ nana::string path_user()
+ {
+#if defined(NANA_WINDOWS)
+ nana::char_t path[MAX_PATH];
+ if(SUCCEEDED(SHGetFolderPath(0, CSIDL_PROFILE, 0, SHGFP_TYPE_CURRENT, path)))
+ return path;
+#elif defined(NANA_LINUX)
+ const char * s = ::getenv("HOME");
+ if(s)
+ return nana::charset(std::string(s, std::strlen(s)), nana::unicode::utf8);
+#endif
+ return nana::string();
+ }
+
+ nana::string path_current()
+ {
+#if defined(NANA_WINDOWS)
+ nana::char_t buf[MAX_PATH];
+ DWORD len = ::GetCurrentDirectory(MAX_PATH, buf);
+ if(len)
+ {
+ if(len > MAX_PATH)
+ {
+ nana::char_t * p = new nana::char_t[len + 1];
+ ::GetCurrentDirectory(len + 1, p);
+ nana::string s = p;
+ delete [] p;
+ return s;
+ }
+ return buf;
+ }
+#elif defined(NANA_LINUX)
+ const char * s = ::getenv("PWD");
+ if(s)
+ return nana::charset(std::string(s, std::strlen(s)), nana::unicode::utf8);
+#endif
+ return nana::string();
+ }
+}//end namespace filesystem
+}//end namespace nana