diff options
Diffstat (limited to 'intern/cycles/util/util_path.cpp')
-rw-r--r-- | intern/cycles/util/util_path.cpp | 631 |
1 files changed, 573 insertions, 58 deletions
diff --git a/intern/cycles/util/util_path.cpp b/intern/cycles/util/util_path.cpp index e8f1ec81763..83754728dbe 100644 --- a/intern/cycles/util/util_path.cpp +++ b/intern/cycles/util/util_path.cpp @@ -19,30 +19,299 @@ #include "util_path.h" #include "util_string.h" +#include <OpenImageIO/filesystem.h> #include <OpenImageIO/strutil.h> #include <OpenImageIO/sysutil.h> + OIIO_NAMESPACE_USING #include <stdio.h> -#include <boost/filesystem.hpp> -#include <boost/algorithm/string.hpp> +#include <sys/stat.h> + +#if defined(_WIN32) +# define DIR_SEP '\\' +# define DIR_SEP_ALT '/' +# include <direct.h> +#else +# define DIR_SEP '/' +# include <dirent.h> +#endif + +#ifdef HAVE_SHLWAPI_H +# include <shlwapi.h> +#endif + +#include "util_windows.h" CCL_NAMESPACE_BEGIN +#ifdef _WIN32 +# if defined(_MSC_VER) || defined(__MINGW64__) +typedef struct _stat64 path_stat_t; +# elif defined(__MINGW32__) +typedef struct _stati64 path_stat_t; +# else +typedef struct _stat path_stat_t; +# endif +# ifndef S_ISDIR +# define S_ISDIR(x) (((x) & _S_IFDIR) == _S_IFDIR) +# endif +#else +typedef struct stat path_stat_t; +#endif + static string cached_path = ""; static string cached_user_path = ""; -static boost::filesystem::path to_boost(const string& path) -{ - return boost::filesystem::path(path.c_str()); -} +namespace { + +#ifdef _WIN32 +class directory_iterator { +public: + class path_info { + public: + path_info(const string& path, + const WIN32_FIND_DATAW& find_data) + : path_(path), + find_data_(find_data) + { + } -static string from_boost(const boost::filesystem::path& path) + string path() { + return path_join(path_, string_from_wstring(find_data_.cFileName)); + } + protected: + const string& path_; + const WIN32_FIND_DATAW& find_data_; + }; + + directory_iterator() + : path_info_("", find_data_), + h_find_(INVALID_HANDLE_VALUE) + { + } + + directory_iterator(const string& path) + : path_(path), + path_info_(path, find_data_) + { + string wildcard = path; + if(wildcard[wildcard.size() - 1] != DIR_SEP) { + wildcard += DIR_SEP; + } + wildcard += "*"; + h_find_ = FindFirstFileW(string_to_wstring(wildcard).c_str(), + &find_data_); + if(h_find_ != INVALID_HANDLE_VALUE) { + skip_dots(); + } + } + + ~directory_iterator() + { + if(h_find_ != INVALID_HANDLE_VALUE) { + FindClose(h_find_); + } + } + + directory_iterator& operator++() + { + step(); + return *this; + } + + path_info* operator-> () + { + return &path_info_; + } + + bool operator!=(const directory_iterator& other) + { + return h_find_ != other.h_find_; + } + +protected: + bool step() + { + if(do_step()) { + return skip_dots(); + } + return false; + } + + bool do_step() + { + if(h_find_ != INVALID_HANDLE_VALUE) { + bool result = FindNextFileW(h_find_, &find_data_) == TRUE; + if(!result) { + FindClose(h_find_); + h_find_ = INVALID_HANDLE_VALUE; + } + return result; + } + return false; + } + + bool skip_dots() + { + while(wcscmp(find_data_.cFileName, L".") == 0 || + wcscmp(find_data_.cFileName, L"..") == 0) + { + if(!do_step()) { + return false; + } + } + return true; + } + + string path_; + path_info path_info_; + WIN32_FIND_DATAW find_data_; + HANDLE h_find_; +}; +#else /* _WIN32 */ + +class directory_iterator { +public: + class path_info { + public: + path_info(const string& path) + : path_(path), + entry_(NULL) + { + } + + string path() { + return path_join(path_, entry_->d_name); + } + + void current_entry_set(const struct dirent *entry) + { + entry_ = entry; + } + protected: + const string& path_; + const struct dirent *entry_; + }; + + directory_iterator() + : path_info_(""), + name_list_(NULL), + num_entries_(-1), + cur_entry_(-1) + { + } + + directory_iterator(const string& path) + : path_(path), + path_info_(path_), + cur_entry_(0) + { + num_entries_ = scandir(path.c_str(), + &name_list_, + NULL, + alphasort); + if(num_entries_ < 0) { + perror("scandir"); + } + else { + skip_dots(); + } + } + + ~directory_iterator() + { + destroy_name_list(); + } + + directory_iterator& operator++() + { + step(); + return *this; + } + + path_info* operator-> () + { + path_info_.current_entry_set(name_list_[cur_entry_]); + return &path_info_; + } + + bool operator!=(const directory_iterator& other) + { + return name_list_ != other.name_list_; + } + +protected: + bool step() + { + if(do_step()) { + return skip_dots(); + } + return false; + } + + bool do_step() + { + ++cur_entry_; + if(cur_entry_ >= num_entries_) { + destroy_name_list(); + return false; + } + return true; + } + + /* Skip . and .. folders. */ + bool skip_dots() + { + while(strcmp(name_list_[cur_entry_]->d_name, ".") == 0 || + strcmp(name_list_[cur_entry_]->d_name, "..") == 0) + { + if(!step()) { + return false; + } + } + return true; + } + + void destroy_name_list() + { + if(name_list_ == NULL) { + return; + } + for(int i = 0; i < num_entries_; ++i) { + free(name_list_[i]); + } + free(name_list_); + name_list_ = NULL; + } + + string path_; + path_info path_info_; + struct dirent **name_list_; + int num_entries_, cur_entry_; +}; + +#endif /* _WIN32 */ + +size_t find_last_slash(const string& path) { - return path.string().c_str(); + for(size_t i = 0; i < path.size(); ++i) { + size_t index = path.size() - 1 - i; +#ifdef _WIN32 + if(path[index] == DIR_SEP || path[index] == DIR_SEP_ALT) +#else + if(path[index] == DIR_SEP) +#endif + { + return index; + } + } + return string::npos; } +} /* namespace */ + static char *path_specials(const string& sub) { static bool env_init = false; @@ -68,8 +337,10 @@ void path_init(const string& path, const string& user_path) cached_user_path = user_path; #ifdef _MSC_VER - // fix for https://svn.boost.org/trac/boost/ticket/6320 - boost::filesystem::path::imbue( std::locale( "" ) ); + // workaround for https://svn.boost.org/trac/boost/ticket/6320 + // indirectly init boost codec here since it's not thread safe, and can + // cause crashes when it happens in multithreaded image load + OIIO::Filesystem::exists(path); #endif } @@ -95,49 +366,237 @@ string path_user_get(const string& sub) string path_filename(const string& path) { - return from_boost(to_boost(path).filename()); + size_t index = find_last_slash(path); + if(index != string::npos) { + /* Corner cases to match boost behavior. */ +#ifndef _WIN32 + if(index == 0 && path.size() == 1) { + return path; + } +#endif + if(index == path.size() - 1) { +#ifdef _WIN32 + if(index == 2) { + return string(1, DIR_SEP); + } +#endif + return "."; + } + return path.substr(index + 1, path.size() - index - 1); + } + return path; } string path_dirname(const string& path) { - return from_boost(to_boost(path).parent_path()); + size_t index = find_last_slash(path); + if(index != string::npos) { +#ifndef _WIN32 + if(index == 0 && path.size() > 1) { + return string(1, DIR_SEP); + } +#endif + return path.substr(0, index); + } + return ""; } string path_join(const string& dir, const string& file) { - return from_boost((to_boost(dir) / to_boost(file))); + if(dir.size() == 0) { + return file; + } + if(file.size() == 0) { + return dir; + } + string result = dir; +#ifndef _WIN32 + if(result[result.size() - 1] != DIR_SEP && + file[0] != DIR_SEP) +#else + if(result[result.size() - 1] != DIR_SEP && + result[result.size() - 1] != DIR_SEP_ALT && + file[0] != DIR_SEP && + file[0] != DIR_SEP_ALT) +#endif + { + result += DIR_SEP; + } + result += file; + return result; } string path_escape(const string& path) { string result = path; - boost::replace_all(result, " ", "\\ "); + string_replace(result, " ", "\\ "); return result; } bool path_is_relative(const string& path) { - return to_boost(path).is_relative(); +#ifdef _WIN32 +# ifdef HAVE_SHLWAPI_H + return PathIsRelative(path.c_str()); +# else /* HAVE_SHLWAPI_H */ + if(path.size() >= 3) { + return !(((path[0] >= 'a' && path[0] <= 'z') || + (path[0] >= 'A' && path[0] <= 'Z')) && + path[1] == ':' && path[2] == DIR_SEP); + } + return true; +# endif /* HAVE_SHLWAPI_H */ +#else /* _WIN32 */ + if(path.size() == 0) { + return 1; + } + return path[0] != DIR_SEP; +#endif /* _WIN32 */ +} + +#ifdef _WIN32 +/* Add a slash if the UNC path points to a share. */ +static string path_unc_add_slash_to_share(const string& path) +{ + size_t slash_after_server = path.find(DIR_SEP, 2); + if(slash_after_server != string::npos) { + size_t slash_after_share = path.find(DIR_SEP, + slash_after_server + 1); + if(slash_after_share == string::npos) { + return path + DIR_SEP; + } + } + return path; +} + +/* Convert: + * \\?\UNC\server\share\folder\... to \\server\share\folder\... + * \\?\C:\ to C:\ and \\?\C:\folder\... to C:\folder\... + */ +static string path_unc_to_short(const string& path) +{ + size_t len = path.size(); + if((len > 3) && + (path[0] == DIR_SEP) && + (path[1] == DIR_SEP) && + (path[2] == '?') && + ((path[3] == DIR_SEP) || (path[3] == DIR_SEP_ALT))) + { + if((len > 5) && (path[5] == ':')) { + return path.substr(4, len - 4); + } + else if((len > 7) && + (path.substr(4, 3) == "UNC") && + ((path[7] == DIR_SEP) || (path[7] == DIR_SEP_ALT))) + { + return "\\\\" + path.substr(8, len - 8); + } + } + return path; +} + +static string path_cleanup_unc(const string& path) +{ + string result = path_unc_to_short(path); + if(path.size() > 2) { + /* It's possible path is now a non-UNC. */ + if(result[0] == DIR_SEP && result[1] == DIR_SEP) { + return path_unc_add_slash_to_share(result); + } + } + return result; +} + +/* Make path compatible for stat() functions. */ +static string path_make_compatible(const string& path) +{ + string result = path; + /* In Windows stat() doesn't recognize dir ending on a slash. */ + if(result.size() > 3 && result[result.size() - 1] == DIR_SEP) { + result.resize(result.size() - 1); + } + /* Clean up UNC path. */ + if((path.size() >= 3) && (path[0] == DIR_SEP) && (path[1] == DIR_SEP)) { + result = path_cleanup_unc(result); + } + /* Make sure volume-only path ends up wit ha directory separator. */ + if(result.size() == 2 && result[1] == ':') { + result += DIR_SEP; + } + return result; +} + +static int path_wstat(const wstring& path_wc, path_stat_t *st) +{ +#if defined(_MSC_VER) || defined(__MINGW64__) + return _wstat64(path_wc.c_str(), st); +#elif defined(__MINGW32__) + return _wstati64(path_wc.c_str(), st); +#else + return _wstat(path_wc.c_str(), st); +#endif +} + +static int path_stat(const string& path, path_stat_t *st) +{ + wstring path_wc = string_to_wstring(path); + return path_wstat(path_wc, st); +} +#else /* _WIN32 */ +static int path_stat(const string& path, path_stat_t *st) +{ + return stat(path.c_str(), st); +} +#endif /* _WIN32 */ + +size_t path_file_size(const string& path) +{ + path_stat_t st; + if(path_stat(path, &st) != 0) { + return -1; + } + return st.st_size; } bool path_exists(const string& path) { - return boost::filesystem::exists(to_boost(path)); +#ifdef _WIN32 + string fixed_path = path_make_compatible(path); + wstring path_wc = string_to_wstring(fixed_path); + path_stat_t st; + if(path_wstat(path_wc, &st) != 0) { + return false; + } + return st.st_mode != 0; +#else /* _WIN32 */ + struct stat st; + if(stat(path.c_str(), &st) != 0) { + return 0; + } + return st.st_mode != 0; +#endif /* _WIN32 */ } -static void path_files_md5_hash_recursive(MD5Hash& hash, const string& dir) +bool path_is_directory(const string& path) { - boost::filesystem::path dirpath = to_boost(dir); + path_stat_t st; + if(path_stat(path, &st) != 0) { + return false; + } + return S_ISDIR(st.st_mode); +} - if(boost::filesystem::exists(dirpath)) { - boost::filesystem::directory_iterator it(dirpath), it_end; +static void path_files_md5_hash_recursive(MD5Hash& hash, const string& dir) +{ + if(path_exists(dir)) { + directory_iterator it(dir), it_end; - for(; it != it_end; it++) { - if(boost::filesystem::is_directory(it->status())) { - path_files_md5_hash_recursive(hash, from_boost(it->path())); + for(; it != it_end; ++it) { + if(path_is_directory(it->path())) { + path_files_md5_hash_recursive(hash, it->path()); } else { - string filepath = from_boost(it->path()); + string filepath = it->path(); hash.append((const uint8_t*)filepath.c_str(), filepath.size()); hash.append_file(filepath); @@ -156,9 +615,36 @@ string path_files_md5_hash(const string& dir) return hash.get_hex(); } -void path_create_directories(const string& path) +static bool create_directories_recursivey(const string& path) +{ + if(path_is_directory(path)) { + /* Directory already exists, nothing to do. */ + return true; + } + if(path_exists(path)) { + /* File exists and it's not a directory. */ + return false; + } + + string parent = path_dirname(path); + if(parent.size() > 0 && parent != path) { + if(!create_directories_recursivey(parent)) { + return false; + } + } + +#ifdef _WIN32 + wstring path_wc = string_to_wstring(path); + return _wmkdir(path_wc.c_str()) == 0; +#else + return mkdir(path.c_str(), 0777) == 0; +#endif +} + +void path_create_directories(const string& filepath) { - boost::filesystem::create_directories(to_boost(path_dirname(path))); + string path = path_dirname(filepath); + create_directories_recursivey(path); } bool path_write_binary(const string& path, const vector<uint8_t>& binary) @@ -189,13 +675,15 @@ bool path_write_text(const string& path, string& text) bool path_read_binary(const string& path, vector<uint8_t>& binary) { - binary.resize(boost::filesystem::file_size(to_boost(path))); - /* read binary file into memory */ FILE *f = path_fopen(path, "rb"); - if(!f) + if(!f) { + binary.resize(0); return false; + } + + binary.resize(path_file_size(path)); if(binary.size() == 0) { fclose(f); @@ -228,57 +716,84 @@ bool path_read_text(const string& path, string& text) uint64_t path_modified_time(const string& path) { - if(boost::filesystem::exists(to_boost(path))) - return (uint64_t)boost::filesystem::last_write_time(to_boost(path)); - + path_stat_t st; + if(path_stat(path, &st) != 0) { + return st.st_mtime; + } return 0; } -string path_source_replace_includes(const string& source_, const string& path) +bool path_remove(const string& path) { - /* our own little c preprocessor that replaces #includes with the file - * contents, to work around issue of opencl drivers not supporting - * include paths with spaces in them */ - string source = source_; - const string include = "#include \""; - size_t n, pos = 0; - - while((n = source.find(include, pos)) != string::npos) { - size_t n_start = n + include.size(); - size_t n_end = source.find("\"", n_start); - string filename = source.substr(n_start, n_end - n_start); - - string text, filepath = path_join(path, filename); + return remove(path.c_str()) == 0; +} - if(path_read_text(filepath, text)) { - text = path_source_replace_includes(text, path_dirname(filepath)); - source.replace(n, n_end + 1 - n, "\n" + text + "\n"); +string path_source_replace_includes(const string& source, const string& path) +{ + /* Our own little c preprocessor that replaces #includes with the file + * contents, to work around issue of opencl drivers not supporting + * include paths with spaces in them. + */ + + string result = ""; + vector<string> lines; + string_split(lines, source, "\n"); + + for(size_t i = 0; i < lines.size(); ++i) { + string line = lines[i]; + if(line[0] == '#') { + string token = string_strip(line.substr(1, line.size() - 1)); + if(string_startswith(token, "include")) { + token = string_strip(token.substr(7, token.size() - 7)); + if(token[0] == '"') { + size_t n_start = 1; + size_t n_end = token.find("\"", n_start); + string filename = token.substr(n_start, n_end - n_start); + string text, filepath = path_join(path, filename); + if(path_read_text(filepath, text)) { + /* Replace include directories with both current path + * and path extracted from the include file. + * Not totally robust, but works fine for Cycles kernel + * and avoids having list of include directories.x + */ + text = path_source_replace_includes( + text, path_dirname(filepath)); + text = path_source_replace_includes(text, path); + line = token.replace(0, n_end + 1, "\n" + text + "\n"); + } + } + } } - else - pos = n_end; + result += line + "\n"; } - return source; + return result; } FILE *path_fopen(const string& path, const string& mode) { +#ifdef _WIN32 + wstring path_wc = string_to_wstring(path); + wstring mode_wc = string_to_wstring(mode); + return _wfopen(path_wc.c_str(), mode_wc.c_str()); +#else return fopen(path.c_str(), mode.c_str()); +#endif } void path_cache_clear_except(const string& name, const set<string>& except) { string dir = path_user_get("cache"); - if(boost::filesystem::exists(dir)) { - boost::filesystem::directory_iterator it(dir), it_end; + if(path_exists(dir)) { + directory_iterator it(dir), it_end; - for(; it != it_end; it++) { - string filename = from_boost(it->path().filename().string()); + for(; it != it_end; ++it) { + string filename = path_filename(it->path()); - if(boost::starts_with(filename, name)) + if(string_startswith(filename, name.c_str())) if(except.find(filename) == except.end()) - boost::filesystem::remove(to_boost(filename)); + path_remove(it->path()); } } |