e8f1ec817632637c13f7b26061755af6ec32adc1
[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/filesystem.hpp> 
29 #include <boost/algorithm/string.hpp>
30
31 CCL_NAMESPACE_BEGIN
32
33 static string cached_path = "";
34 static string cached_user_path = "";
35
36 static boost::filesystem::path to_boost(const string& path)
37 {
38         return boost::filesystem::path(path.c_str());
39 }
40
41 static string from_boost(const boost::filesystem::path& path)
42 {
43         return path.string().c_str();
44 }
45
46 static char *path_specials(const string& sub)
47 {
48         static bool env_init = false;
49         static char *env_shader_path;
50         static char *env_kernel_path;
51         if(!env_init) {
52                 env_shader_path = getenv("CYCLES_SHADER_PATH");
53                 env_kernel_path = getenv("CYCLES_KERNEL_PATH");
54                 env_init = true;
55         }
56         if(env_shader_path != NULL && sub == "shader") {
57                 return env_shader_path;
58         }
59         else if(env_shader_path != NULL && sub == "kernel") {
60                 return env_kernel_path;
61         }
62         return NULL;
63 }
64
65 void path_init(const string& path, const string& user_path)
66 {
67         cached_path = path;
68         cached_user_path = user_path;
69
70 #ifdef _MSC_VER
71         // fix for https://svn.boost.org/trac/boost/ticket/6320
72         boost::filesystem::path::imbue( std::locale( "" ) );
73 #endif
74 }
75
76 string path_get(const string& sub)
77 {
78         char *special = path_specials(sub);
79         if(special != NULL)
80                 return special;
81
82         if(cached_path == "")
83                 cached_path = path_dirname(Sysutil::this_program_path());
84
85         return path_join(cached_path, sub);
86 }
87
88 string path_user_get(const string& sub)
89 {
90         if(cached_user_path == "")
91                 cached_user_path = path_dirname(Sysutil::this_program_path());
92
93         return path_join(cached_user_path, sub);
94 }
95
96 string path_filename(const string& path)
97 {
98         return from_boost(to_boost(path).filename());
99 }
100
101 string path_dirname(const string& path)
102 {
103         return from_boost(to_boost(path).parent_path());
104 }
105
106 string path_join(const string& dir, const string& file)
107 {
108         return from_boost((to_boost(dir) / to_boost(file)));
109 }
110
111 string path_escape(const string& path)
112 {
113         string result = path;
114         boost::replace_all(result, " ", "\\ ");
115         return result;
116 }
117
118 bool path_is_relative(const string& path)
119 {
120         return to_boost(path).is_relative();
121 }
122
123 bool path_exists(const string& path)
124 {
125         return boost::filesystem::exists(to_boost(path));
126 }
127
128 static void path_files_md5_hash_recursive(MD5Hash& hash, const string& dir)
129 {
130         boost::filesystem::path dirpath = to_boost(dir);
131
132         if(boost::filesystem::exists(dirpath)) {
133                 boost::filesystem::directory_iterator it(dirpath), it_end;
134
135                 for(; it != it_end; it++) {
136                         if(boost::filesystem::is_directory(it->status())) {
137                                 path_files_md5_hash_recursive(hash, from_boost(it->path()));
138                         }
139                         else {
140                                 string filepath = from_boost(it->path());
141
142                                 hash.append((const uint8_t*)filepath.c_str(), filepath.size());
143                                 hash.append_file(filepath);
144                         }
145                 }
146         }
147 }
148
149 string path_files_md5_hash(const string& dir)
150 {
151         /* computes md5 hash of all files in the directory */
152         MD5Hash hash;
153
154         path_files_md5_hash_recursive(hash, dir);
155
156         return hash.get_hex();
157 }
158
159 void path_create_directories(const string& path)
160 {
161         boost::filesystem::create_directories(to_boost(path_dirname(path)));
162 }
163
164 bool path_write_binary(const string& path, const vector<uint8_t>& binary)
165 {
166         path_create_directories(path);
167
168         /* write binary file from memory */
169         FILE *f = path_fopen(path, "wb");
170
171         if(!f)
172                 return false;
173
174         if(binary.size() > 0)
175                 fwrite(&binary[0], sizeof(uint8_t), binary.size(), f);
176
177         fclose(f);
178
179         return true;
180 }
181
182 bool path_write_text(const string& path, string& text)
183 {
184         vector<uint8_t> binary(text.length(), 0);
185         std::copy(text.begin(), text.end(), binary.begin());
186
187         return path_write_binary(path, binary);
188 }
189
190 bool path_read_binary(const string& path, vector<uint8_t>& binary)
191 {
192         binary.resize(boost::filesystem::file_size(to_boost(path)));
193
194         /* read binary file into memory */
195         FILE *f = path_fopen(path, "rb");
196
197         if(!f)
198                 return false;
199
200         if(binary.size() == 0) {
201                 fclose(f);
202                 return false;
203         }
204
205         if(fread(&binary[0], sizeof(uint8_t), binary.size(), f) != binary.size()) {
206                 fclose(f);
207                 return false;
208         }
209
210         fclose(f);
211
212         return true;
213 }
214
215 bool path_read_text(const string& path, string& text)
216 {
217         vector<uint8_t> binary;
218
219         if(!path_exists(path) || !path_read_binary(path, binary))
220                 return false;
221
222         const char *str = (const char*)&binary[0];
223         size_t size = binary.size();
224         text = string(str, size);
225
226         return true;
227 }
228
229 uint64_t path_modified_time(const string& path)
230 {
231         if(boost::filesystem::exists(to_boost(path)))
232                 return (uint64_t)boost::filesystem::last_write_time(to_boost(path));
233         
234         return 0;
235 }
236
237 string path_source_replace_includes(const string& source_, const string& path)
238 {
239         /* our own little c preprocessor that replaces #includes with the file
240          * contents, to work around issue of opencl drivers not supporting
241          * include paths with spaces in them */
242         string source = source_;
243         const string include = "#include \"";
244         size_t n, pos = 0;
245
246         while((n = source.find(include, pos)) != string::npos) {
247                 size_t n_start = n + include.size();
248                 size_t n_end = source.find("\"", n_start);
249                 string filename = source.substr(n_start, n_end - n_start);
250
251                 string text, filepath = path_join(path, filename);
252
253                 if(path_read_text(filepath, text)) {
254                         text = path_source_replace_includes(text, path_dirname(filepath));
255                         source.replace(n, n_end + 1 - n, "\n" + text + "\n");
256                 }
257                 else
258                         pos = n_end;
259         }
260
261         return source;
262 }
263
264 FILE *path_fopen(const string& path, const string& mode)
265 {
266         return fopen(path.c_str(), mode.c_str());
267 }
268
269 void path_cache_clear_except(const string& name, const set<string>& except)
270 {
271         string dir = path_user_get("cache");
272
273         if(boost::filesystem::exists(dir)) {
274                 boost::filesystem::directory_iterator it(dir), it_end;
275
276                 for(; it != it_end; it++) {
277                         string filename = from_boost(it->path().filename().string());
278
279                         if(boost::starts_with(filename, name))
280                                 if(except.find(filename) == except.end())
281                                         boost::filesystem::remove(to_boost(filename));
282                 }
283         }
284
285 }
286
287 CCL_NAMESPACE_END
288