2011-04-27 11:58:34 +00:00
|
|
|
/*
|
2013-08-18 14:16:15 +00:00
|
|
|
* Copyright 2011-2013 Blender Foundation
|
2011-04-27 11:58:34 +00:00
|
|
|
*
|
2013-08-18 14:16:15 +00:00
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
2011-04-27 11:58:34 +00:00
|
|
|
*
|
2013-08-18 14:16:15 +00:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2011-04-27 11:58:34 +00:00
|
|
|
*
|
2013-08-18 14:16:15 +00:00
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
2014-12-25 01:50:24 +00:00
|
|
|
* limitations under the License.
|
2011-04-27 11:58:34 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include "util_debug.h"
|
2011-09-03 10:49:54 +00:00
|
|
|
#include "util_md5.h"
|
2011-04-27 11:58:34 +00:00
|
|
|
#include "util_path.h"
|
|
|
|
#include "util_string.h"
|
|
|
|
|
2016-04-01 18:37:24 +00:00
|
|
|
#include <OpenImageIO/filesystem.h>
|
2014-01-10 23:47:58 +00:00
|
|
|
#include <OpenImageIO/strutil.h>
|
2011-04-27 11:58:34 +00:00
|
|
|
#include <OpenImageIO/sysutil.h>
|
2016-02-05 08:09:39 +00:00
|
|
|
|
2011-04-27 11:58:34 +00:00
|
|
|
OIIO_NAMESPACE_USING
|
|
|
|
|
2011-09-09 12:04:39 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
|
2016-02-05 08:09:39 +00:00
|
|
|
#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"
|
2011-04-27 11:58:34 +00:00
|
|
|
|
|
|
|
CCL_NAMESPACE_BEGIN
|
|
|
|
|
2016-02-05 08:09:39 +00:00
|
|
|
#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
|
|
|
|
|
2011-04-27 11:58:34 +00:00
|
|
|
static string cached_path = "";
|
2011-09-09 12:04:39 +00:00
|
|
|
static string cached_user_path = "";
|
2011-04-27 11:58:34 +00:00
|
|
|
|
2016-02-05 08:09:39 +00:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-05-11 14:50:10 +00:00
|
|
|
explicit directory_iterator(const string& path)
|
2016-02-05 08:09:39 +00:00
|
|
|
: 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:
|
2016-05-11 14:50:10 +00:00
|
|
|
explicit path_info(const string& path)
|
2016-02-16 14:32:26 +00:00
|
|
|
: path_(path),
|
|
|
|
entry_(NULL)
|
2016-02-05 08:09:39 +00:00
|
|
|
{
|
|
|
|
}
|
2014-01-10 23:47:58 +00:00
|
|
|
|
2016-02-05 08:09:39 +00:00
|
|
|
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)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2016-05-11 14:50:10 +00:00
|
|
|
explicit directory_iterator(const string& path)
|
2016-02-05 08:09:39 +00:00
|
|
|
: 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)
|
2014-01-10 23:47:58 +00:00
|
|
|
{
|
2016-02-05 08:09:39 +00:00
|
|
|
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;
|
2014-01-10 23:47:58 +00:00
|
|
|
}
|
|
|
|
|
2016-02-05 08:09:39 +00:00
|
|
|
} /* namespace */
|
|
|
|
|
2015-01-31 10:40:09 +00:00
|
|
|
static char *path_specials(const string& sub)
|
|
|
|
{
|
|
|
|
static bool env_init = false;
|
|
|
|
static char *env_shader_path;
|
|
|
|
static char *env_kernel_path;
|
|
|
|
if(!env_init) {
|
|
|
|
env_shader_path = getenv("CYCLES_SHADER_PATH");
|
|
|
|
env_kernel_path = getenv("CYCLES_KERNEL_PATH");
|
|
|
|
env_init = true;
|
|
|
|
}
|
|
|
|
if(env_shader_path != NULL && sub == "shader") {
|
|
|
|
return env_shader_path;
|
|
|
|
}
|
|
|
|
else if(env_shader_path != NULL && sub == "kernel") {
|
|
|
|
return env_kernel_path;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-09-09 12:04:39 +00:00
|
|
|
void path_init(const string& path, const string& user_path)
|
2011-04-27 11:58:34 +00:00
|
|
|
{
|
|
|
|
cached_path = path;
|
2011-09-09 12:04:39 +00:00
|
|
|
cached_user_path = user_path;
|
2016-04-01 18:37:24 +00:00
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
// 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
|
2011-04-27 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
string path_get(const string& sub)
|
|
|
|
{
|
2015-01-31 10:40:09 +00:00
|
|
|
char *special = path_specials(sub);
|
|
|
|
if(special != NULL)
|
|
|
|
return special;
|
|
|
|
|
2011-04-27 11:58:34 +00:00
|
|
|
if(cached_path == "")
|
|
|
|
cached_path = path_dirname(Sysutil::this_program_path());
|
|
|
|
|
|
|
|
return path_join(cached_path, sub);
|
|
|
|
}
|
|
|
|
|
2011-09-09 12:04:39 +00:00
|
|
|
string path_user_get(const string& sub)
|
|
|
|
{
|
|
|
|
if(cached_user_path == "")
|
|
|
|
cached_user_path = path_dirname(Sysutil::this_program_path());
|
|
|
|
|
|
|
|
return path_join(cached_user_path, sub);
|
|
|
|
}
|
|
|
|
|
2011-04-27 11:58:34 +00:00
|
|
|
string path_filename(const string& path)
|
|
|
|
{
|
2016-02-05 08:09:39 +00:00
|
|
|
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;
|
2011-04-27 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
string path_dirname(const string& path)
|
|
|
|
{
|
2016-02-05 08:09:39 +00:00
|
|
|
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 "";
|
2011-04-27 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
string path_join(const string& dir, const string& file)
|
|
|
|
{
|
2016-02-05 08:09:39 +00:00
|
|
|
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;
|
2011-04-27 11:58:34 +00:00
|
|
|
}
|
|
|
|
|
2011-09-08 18:58:07 +00:00
|
|
|
string path_escape(const string& path)
|
|
|
|
{
|
|
|
|
string result = path;
|
2016-02-05 08:09:39 +00:00
|
|
|
string_replace(result, " ", "\\ ");
|
2011-09-08 18:58:07 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2014-03-21 16:22:41 +00:00
|
|
|
bool path_is_relative(const string& path)
|
|
|
|
{
|
2016-02-05 08:09:39 +00:00
|
|
|
#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);
|
|
|
|
}
|
2016-04-13 06:58:52 +00:00
|
|
|
else if((len > 7) &&
|
|
|
|
(path.substr(4, 3) == "UNC") &&
|
|
|
|
((path[7] == DIR_SEP) || (path[7] == DIR_SEP_ALT)))
|
2016-02-05 08:09:39 +00:00
|
|
|
{
|
|
|
|
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;
|
2016-02-12 19:21:13 +00:00
|
|
|
/* In Windows stat() doesn't recognize dir ending on a slash. */
|
2016-02-05 08:09:39 +00:00
|
|
|
if(result.size() > 3 && result[result.size() - 1] == DIR_SEP) {
|
2016-02-12 19:21:13 +00:00
|
|
|
result.resize(result.size() - 1);
|
2016-02-05 08:09:39 +00:00
|
|
|
}
|
|
|
|
/* 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;
|
2014-03-21 16:22:41 +00:00
|
|
|
}
|
|
|
|
|
2011-09-08 18:58:07 +00:00
|
|
|
bool path_exists(const string& path)
|
|
|
|
{
|
2016-02-05 08:09:39 +00:00
|
|
|
#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 */
|
2011-09-08 18:58:07 +00:00
|
|
|
}
|
|
|
|
|
2016-02-05 08:09:39 +00:00
|
|
|
bool path_is_directory(const string& path)
|
2011-09-03 10:49:54 +00:00
|
|
|
{
|
2016-02-05 08:09:39 +00:00
|
|
|
path_stat_t st;
|
|
|
|
if(path_stat(path, &st) != 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return S_ISDIR(st.st_mode);
|
|
|
|
}
|
2014-01-10 23:47:58 +00:00
|
|
|
|
2016-02-05 08:09:39 +00:00
|
|
|
static void path_files_md5_hash_recursive(MD5Hash& hash, const string& dir)
|
|
|
|
{
|
|
|
|
if(path_exists(dir)) {
|
|
|
|
directory_iterator it(dir), it_end;
|
2011-09-03 10:49:54 +00:00
|
|
|
|
2016-02-05 08:09:39 +00:00
|
|
|
for(; it != it_end; ++it) {
|
|
|
|
if(path_is_directory(it->path())) {
|
|
|
|
path_files_md5_hash_recursive(hash, it->path());
|
2011-09-03 10:49:54 +00:00
|
|
|
}
|
|
|
|
else {
|
2016-02-05 08:09:39 +00:00
|
|
|
string filepath = it->path();
|
2011-09-03 10:49:54 +00:00
|
|
|
|
|
|
|
hash.append((const uint8_t*)filepath.c_str(), filepath.size());
|
|
|
|
hash.append_file(filepath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-09-27 19:35:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
string path_files_md5_hash(const string& dir)
|
|
|
|
{
|
|
|
|
/* computes md5 hash of all files in the directory */
|
|
|
|
MD5Hash hash;
|
|
|
|
|
|
|
|
path_files_md5_hash_recursive(hash, dir);
|
2011-09-03 10:49:54 +00:00
|
|
|
|
|
|
|
return hash.get_hex();
|
|
|
|
}
|
|
|
|
|
2016-02-05 08:09:39 +00:00
|
|
|
static bool create_directories_recursivey(const string& path)
|
2011-09-09 12:04:39 +00:00
|
|
|
{
|
2016-02-05 08:09:39 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-23 12:58:31 +00:00
|
|
|
#ifdef _WIN32
|
|
|
|
wstring path_wc = string_to_wstring(path);
|
|
|
|
return _wmkdir(path_wc.c_str()) == 0;
|
|
|
|
#else
|
2016-02-05 08:09:39 +00:00
|
|
|
return mkdir(path.c_str(), 0777) == 0;
|
2016-03-23 12:58:31 +00:00
|
|
|
#endif
|
2016-02-05 08:09:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void path_create_directories(const string& filepath)
|
|
|
|
{
|
|
|
|
string path = path_dirname(filepath);
|
|
|
|
create_directories_recursivey(path);
|
2011-09-12 13:13:56 +00:00
|
|
|
}
|
2011-09-09 12:04:39 +00:00
|
|
|
|
2011-09-12 13:13:56 +00:00
|
|
|
bool path_write_binary(const string& path, const vector<uint8_t>& binary)
|
|
|
|
{
|
|
|
|
path_create_directories(path);
|
|
|
|
|
|
|
|
/* write binary file from memory */
|
2014-01-10 23:47:58 +00:00
|
|
|
FILE *f = path_fopen(path, "wb");
|
2011-09-09 12:04:39 +00:00
|
|
|
|
|
|
|
if(!f)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if(binary.size() > 0)
|
|
|
|
fwrite(&binary[0], sizeof(uint8_t), binary.size(), f);
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-05-27 16:21:07 +00:00
|
|
|
bool path_write_text(const string& path, string& text)
|
|
|
|
{
|
|
|
|
vector<uint8_t> binary(text.length(), 0);
|
|
|
|
std::copy(text.begin(), text.end(), binary.begin());
|
|
|
|
|
|
|
|
return path_write_binary(path, binary);
|
|
|
|
}
|
|
|
|
|
2011-09-09 12:04:39 +00:00
|
|
|
bool path_read_binary(const string& path, vector<uint8_t>& binary)
|
|
|
|
{
|
|
|
|
/* read binary file into memory */
|
2014-01-10 23:47:58 +00:00
|
|
|
FILE *f = path_fopen(path, "rb");
|
2011-09-09 12:04:39 +00:00
|
|
|
|
2016-02-05 08:09:39 +00:00
|
|
|
if(!f) {
|
|
|
|
binary.resize(0);
|
2011-09-09 12:04:39 +00:00
|
|
|
return false;
|
2016-02-05 08:09:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
binary.resize(path_file_size(path));
|
2011-09-09 12:04:39 +00:00
|
|
|
|
|
|
|
if(binary.size() == 0) {
|
|
|
|
fclose(f);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(fread(&binary[0], sizeof(uint8_t), binary.size(), f) != binary.size()) {
|
|
|
|
fclose(f);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(f);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-11-03 14:32:35 +00:00
|
|
|
bool path_read_text(const string& path, string& text)
|
2011-11-22 16:38:58 +00:00
|
|
|
{
|
|
|
|
vector<uint8_t> binary;
|
|
|
|
|
|
|
|
if(!path_exists(path) || !path_read_binary(path, binary))
|
|
|
|
return false;
|
2013-05-27 16:21:07 +00:00
|
|
|
|
2011-11-22 16:38:58 +00:00
|
|
|
const char *str = (const char*)&binary[0];
|
|
|
|
size_t size = binary.size();
|
|
|
|
text = string(str, size);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-11-03 14:32:35 +00:00
|
|
|
uint64_t path_modified_time(const string& path)
|
|
|
|
{
|
2016-02-05 08:09:39 +00:00
|
|
|
path_stat_t st;
|
|
|
|
if(path_stat(path, &st) != 0) {
|
|
|
|
return st.st_mtime;
|
|
|
|
}
|
2012-11-03 14:32:35 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-02-05 08:09:39 +00:00
|
|
|
bool path_remove(const string& path)
|
|
|
|
{
|
|
|
|
return remove(path.c_str()) == 0;
|
|
|
|
}
|
|
|
|
|
2016-02-12 17:33:43 +00:00
|
|
|
string path_source_replace_includes(const string& source, const string& path)
|
2011-11-22 16:38:58 +00:00
|
|
|
{
|
2016-02-12 17:33:43 +00:00
|
|
|
/* Our own little c preprocessor that replaces #includes with the file
|
2012-06-09 17:22:52 +00:00
|
|
|
* contents, to work around issue of opencl drivers not supporting
|
2016-02-12 17:33:43 +00:00
|
|
|
* 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");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-11-22 16:38:58 +00:00
|
|
|
}
|
2016-02-12 17:33:43 +00:00
|
|
|
result += line + "\n";
|
2011-11-22 16:38:58 +00:00
|
|
|
}
|
|
|
|
|
2016-02-12 17:33:43 +00:00
|
|
|
return result;
|
2011-11-22 16:38:58 +00:00
|
|
|
}
|
|
|
|
|
2014-01-10 23:47:58 +00:00
|
|
|
FILE *path_fopen(const string& path, const string& mode)
|
|
|
|
{
|
2016-03-23 12:58:31 +00:00
|
|
|
#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
|
2014-01-10 23:47:58 +00:00
|
|
|
return fopen(path.c_str(), mode.c_str());
|
2016-03-23 12:58:31 +00:00
|
|
|
#endif
|
2014-01-10 23:47:58 +00:00
|
|
|
}
|
|
|
|
|
2014-01-23 00:13:09 +00:00
|
|
|
void path_cache_clear_except(const string& name, const set<string>& except)
|
|
|
|
{
|
|
|
|
string dir = path_user_get("cache");
|
|
|
|
|
2016-02-05 08:09:39 +00:00
|
|
|
if(path_exists(dir)) {
|
|
|
|
directory_iterator it(dir), it_end;
|
2014-01-23 00:13:09 +00:00
|
|
|
|
2016-02-05 08:09:39 +00:00
|
|
|
for(; it != it_end; ++it) {
|
|
|
|
string filename = path_filename(it->path());
|
2014-01-23 00:13:09 +00:00
|
|
|
|
2016-02-05 08:09:39 +00:00
|
|
|
if(string_startswith(filename, name.c_str()))
|
2014-01-23 00:13:09 +00:00
|
|
|
if(except.find(filename) == except.end())
|
2016-02-05 08:09:39 +00:00
|
|
|
path_remove(it->path());
|
2014-01-23 00:13:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2011-04-27 11:58:34 +00:00
|
|
|
CCL_NAMESPACE_END
|
|
|
|
|