Fix T38311: cycles BVH cache crash on Windows.
[blender.git] / intern / cycles / util / util_path.cpp
1 /*
2  * Copyright 2011-2013 Blender Foundation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16
17 #include "util_debug.h"
18 #include "util_md5.h"
19 #include "util_path.h"
20 #include "util_string.h"
21
22 #include <OpenImageIO/strutil.h>
23 #include <OpenImageIO/sysutil.h>
24 OIIO_NAMESPACE_USING
25
26 #include <stdio.h>
27
28 #include <boost/version.hpp>
29
30 #if (BOOST_VERSION < 104400)
31 #  define BOOST_FILESYSTEM_VERSION 2
32 #endif
33
34 #include <boost/filesystem.hpp> 
35 #include <boost/algorithm/string.hpp>
36
37 CCL_NAMESPACE_BEGIN
38
39 static string cached_path = "";
40 static string cached_user_path = "";
41
42 static boost::filesystem::path to_boost(const string& path)
43 {
44 #ifdef _MSC_VER
45         std::wstring path_utf16 = Strutil::utf8_to_utf16(path.c_str());
46         return boost::filesystem::path(path_utf16.c_str());
47 #else
48         return boost::filesystem::path(path.c_str());
49 #endif
50 }
51
52 static string from_boost(const boost::filesystem::path& path)
53 {
54 #ifdef _MSC_VER
55         return Strutil::utf16_to_utf8(path.wstring().c_str());
56 #else
57         return path.string().c_str();
58 #endif
59 }
60
61 void path_init(const string& path, const string& user_path)
62 {
63         cached_path = path;
64         cached_user_path = user_path;
65
66 #ifdef _MSC_VER
67         // fix for https://svn.boost.org/trac/boost/ticket/6320
68         boost::filesystem::path::imbue( std::locale( "" ) );
69 #endif
70 }
71
72 string path_get(const string& sub)
73 {
74         if(cached_path == "")
75                 cached_path = path_dirname(Sysutil::this_program_path());
76
77         return path_join(cached_path, sub);
78 }
79
80 string path_user_get(const string& sub)
81 {
82         if(cached_user_path == "")
83                 cached_user_path = path_dirname(Sysutil::this_program_path());
84
85         return path_join(cached_user_path, sub);
86 }
87
88 string path_filename(const string& path)
89 {
90 #if (BOOST_FILESYSTEM_VERSION == 2)
91         return to_boost(path).filename();
92 #else
93         return from_boost(to_boost(path).filename());
94 #endif
95 }
96
97 string path_dirname(const string& path)
98 {
99         return from_boost(to_boost(path).parent_path());
100 }
101
102 string path_join(const string& dir, const string& file)
103 {
104         return from_boost((to_boost(dir) / to_boost(file)));
105 }
106
107 string path_escape(const string& path)
108 {
109         string result = path;
110         boost::replace_all(result, " ", "\\ ");
111         return result;
112 }
113
114 bool path_exists(const string& path)
115 {
116         return boost::filesystem::exists(to_boost(path));
117 }
118
119 static void path_files_md5_hash_recursive(MD5Hash& hash, const string& dir)
120 {
121         boost::filesystem::path dirpath = to_boost(dir);
122
123         if(boost::filesystem::exists(dirpath)) {
124                 boost::filesystem::directory_iterator it(dirpath), it_end;
125
126                 for(; it != it_end; it++) {
127                         if(boost::filesystem::is_directory(it->status())) {
128                                 path_files_md5_hash_recursive(hash, from_boost(it->path()));
129                         }
130                         else {
131                                 string filepath = from_boost(it->path());
132
133                                 hash.append((const uint8_t*)filepath.c_str(), filepath.size());
134                                 hash.append_file(filepath);
135                         }
136                 }
137         }
138 }
139
140 string path_files_md5_hash(const string& dir)
141 {
142         /* computes md5 hash of all files in the directory */
143         MD5Hash hash;
144
145         path_files_md5_hash_recursive(hash, dir);
146
147         return hash.get_hex();
148 }
149
150 void path_create_directories(const string& path)
151 {
152         boost::filesystem::create_directories(to_boost(path_dirname(path)));
153 }
154
155 bool path_write_binary(const string& path, const vector<uint8_t>& binary)
156 {
157         path_create_directories(path);
158
159         /* write binary file from memory */
160         FILE *f = path_fopen(path, "wb");
161
162         if(!f)
163                 return false;
164
165         if(binary.size() > 0)
166                 fwrite(&binary[0], sizeof(uint8_t), binary.size(), f);
167
168         fclose(f);
169
170         return true;
171 }
172
173 bool path_write_text(const string& path, string& text)
174 {
175         vector<uint8_t> binary(text.length(), 0);
176         std::copy(text.begin(), text.end(), binary.begin());
177
178         return path_write_binary(path, binary);
179 }
180
181 bool path_read_binary(const string& path, vector<uint8_t>& binary)
182 {
183         binary.resize(boost::filesystem::file_size(to_boost(path)));
184
185         /* read binary file into memory */
186         FILE *f = path_fopen(path, "rb");
187
188         if(!f)
189                 return false;
190
191         if(binary.size() == 0) {
192                 fclose(f);
193                 return false;
194         }
195
196         if(fread(&binary[0], sizeof(uint8_t), binary.size(), f) != binary.size()) {
197                 fclose(f);
198                 return false;
199         }
200
201         fclose(f);
202
203         return true;
204 }
205
206 bool path_read_text(const string& path, string& text)
207 {
208         vector<uint8_t> binary;
209
210         if(!path_exists(path) || !path_read_binary(path, binary))
211                 return false;
212
213         const char *str = (const char*)&binary[0];
214         size_t size = binary.size();
215         text = string(str, size);
216
217         return true;
218 }
219
220 uint64_t path_modified_time(const string& path)
221 {
222         if(boost::filesystem::exists(to_boost(path)))
223                 return (uint64_t)boost::filesystem::last_write_time(to_boost(path));
224         
225         return 0;
226 }
227
228 string path_source_replace_includes(const string& source_, const string& path)
229 {
230         /* our own little c preprocessor that replaces #includes with the file
231          * contents, to work around issue of opencl drivers not supporting
232          * include paths with spaces in them */
233         string source = source_;
234         const string include = "#include \"";
235         size_t n, pos = 0;
236
237         while((n = source.find(include, pos)) != string::npos) {
238                 size_t n_start = n + include.size();
239                 size_t n_end = source.find("\"", n_start);
240                 string filename = source.substr(n_start, n_end - n_start);
241
242                 string text, filepath = path_join(path, filename);
243
244                 if(path_read_text(filepath, text)) {
245                         text = path_source_replace_includes(text, path_dirname(filepath));
246                         source.replace(n, n_end + 1 - n, "\n" + text + "\n");
247                 }
248                 else
249                         pos = n_end;
250         }
251
252         return source;
253 }
254
255 FILE *path_fopen(const string& path, const string& mode)
256 {
257 #ifdef _WIN32
258         std::wstring path_utf16 = Strutil::utf8_to_utf16(path);
259         std::wstring mode_utf16 = Strutil::utf8_to_utf16(mode);
260
261         return _wfopen(path_utf16.c_str(), mode_utf16.c_str());
262 #else
263         return fopen(path.c_str(), mode.c_str());
264 #endif
265 }
266
267 void path_cache_clear_except(const string& name, const set<string>& except)
268 {
269         string dir = path_user_get("cache");
270
271         if(boost::filesystem::exists(dir)) {
272                 boost::filesystem::directory_iterator it(dir), it_end;
273
274                 for(; it != it_end; it++) {
275 #if (BOOST_FILESYSTEM_VERSION == 2)
276                         string filename = from_boost(it->path().filename());
277 #else
278                         string filename = from_boost(it->path().filename().string());
279 #endif
280
281                         if(boost::starts_with(filename, name))
282                                 if(except.find(filename) == except.end())
283                                         boost::filesystem::remove(to_boost(filename));
284                 }
285         }
286
287 }
288
289 CCL_NAMESPACE_END
290