From 02961f16737594f8620dcd4f869288c3fabb115f Mon Sep 17 00:00:00 2001 From: qPCR4vir Date: Thu, 21 May 2015 22:06:14 +0200 Subject: [PATCH] add file_type (from type), bring all from fs_utility and mimic path, and directory_entry --- build/vc2013/nana.vcxproj | 1 + build/vc2013/nana.vcxproj.filters | 3 + include/nana/filesystem/filesystem.hpp | 81 ++++- source/filesystem/filesystem.cpp | 442 +++++++++++++++++++++++++ 4 files changed, 517 insertions(+), 10 deletions(-) create mode 100644 source/filesystem/filesystem.cpp 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