svn merge ^/trunk/blender -r47023:HEAD
[blender-staging.git] / intern / cycles / util / util_path.cpp
1 /*
2  * Copyright 2011, Blender Foundation.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  */
18
19 #include "util_debug.h"
20 #include "util_md5.h"
21 #include "util_path.h"
22 #include "util_string.h"
23
24 #include <OpenImageIO/sysutil.h>
25 OIIO_NAMESPACE_USING
26
27 #include <stdio.h>
28
29 #define BOOST_FILESYSTEM_VERSION 2
30
31 #include <boost/filesystem.hpp> 
32 #include <boost/algorithm/string.hpp>
33
34 CCL_NAMESPACE_BEGIN
35
36 static string cached_path = "";
37 static string cached_user_path = "";
38
39 void path_init(const string& path, const string& user_path)
40 {
41         cached_path = path;
42         cached_user_path = user_path;
43 }
44
45 string path_get(const string& sub)
46 {
47         if(cached_path == "")
48                 cached_path = path_dirname(Sysutil::this_program_path());
49
50         return path_join(cached_path, sub);
51 }
52
53 string path_user_get(const string& sub)
54 {
55         if(cached_user_path == "")
56                 cached_user_path = path_dirname(Sysutil::this_program_path());
57
58         return path_join(cached_user_path, sub);
59 }
60
61 string path_filename(const string& path)
62 {
63         return boost::filesystem::path(path).filename();
64 }
65
66 string path_dirname(const string& path)
67 {
68         return boost::filesystem::path(path).parent_path().string();
69 }
70
71 string path_join(const string& dir, const string& file)
72 {
73         return (boost::filesystem::path(dir) / boost::filesystem::path(file)).string();
74 }
75
76 string path_escape(const string& path)
77 {
78         string result = path;
79         boost::replace_all(result, " ", "\\ ");
80         return result;
81 }
82
83 bool path_exists(const string& path)
84 {
85         return boost::filesystem::exists(path);
86 }
87
88 static void path_files_md5_hash_recursive(MD5Hash& hash, const string& dir)
89 {
90         if(boost::filesystem::exists(dir)) {
91                 boost::filesystem::directory_iterator it(dir), it_end;
92
93                 for(; it != it_end; it++) {
94                         if(boost::filesystem::is_directory(it->status())) {
95                                 path_files_md5_hash_recursive(hash, it->path().string());
96                         }
97                         else {
98                                 string filepath = it->path().string();
99
100                                 hash.append((const uint8_t*)filepath.c_str(), filepath.size());
101                                 hash.append_file(filepath);
102                         }
103                 }
104         }
105 }
106
107 string path_files_md5_hash(const string& dir)
108 {
109         /* computes md5 hash of all files in the directory */
110         MD5Hash hash;
111
112         path_files_md5_hash_recursive(hash, dir);
113
114         return hash.get_hex();
115 }
116
117 void path_create_directories(const string& path)
118 {
119         boost::filesystem::create_directories(path_dirname(path));
120 }
121
122 bool path_write_binary(const string& path, const vector<uint8_t>& binary)
123 {
124         path_create_directories(path);
125
126         /* write binary file from memory */
127         FILE *f = fopen(path.c_str(), "wb");
128
129         if(!f)
130                 return false;
131
132         if(binary.size() > 0)
133                 fwrite(&binary[0], sizeof(uint8_t), binary.size(), f);
134
135         fclose(f);
136
137         return true;
138 }
139
140 bool path_read_binary(const string& path, vector<uint8_t>& binary)
141 {
142         binary.resize(boost::filesystem::file_size(path));
143
144         /* read binary file into memory */
145         FILE *f = fopen(path.c_str(), "rb");
146
147         if(!f)
148                 return false;
149
150         if(binary.size() == 0) {
151                 fclose(f);
152                 return false;
153         }
154
155         if(fread(&binary[0], sizeof(uint8_t), binary.size(), f) != binary.size()) {
156                 fclose(f);
157                 return false;
158         }
159
160         fclose(f);
161
162         return true;
163 }
164
165 static bool path_read_text(const string& path, string& text)
166 {
167         vector<uint8_t> binary;
168
169         if(!path_exists(path) || !path_read_binary(path, binary))
170                 return false;
171         
172         const char *str = (const char*)&binary[0];
173         size_t size = binary.size();
174         text = string(str, size);
175
176         return true;
177 }
178
179 string path_source_replace_includes(const string& source_, const string& path)
180 {
181         /* our own little c preprocessor that replaces #includes with the file
182          * contents, to work around issue of opencl drivers not supporting
183          * include paths with spaces in them */
184         string source = source_;
185         const string include = "#include \"";
186         size_t n, pos = 0;
187
188         while((n = source.find(include, pos)) != string::npos) {
189                 size_t n_start = n + include.size();
190                 size_t n_end = source.find("\"", n_start);
191                 string filename = source.substr(n_start, n_end - n_start);
192
193                 string text, filepath = path_join(path, filename);
194
195                 if(path_read_text(filepath, text)) {
196                         text = path_source_replace_includes(text, path_dirname(filepath));
197                         source.replace(n, n_end + 1 - n, "\n" + text + "\n");
198                 }
199                 else
200                         pos = n_end;
201         }
202
203         return source;
204 }
205
206 CCL_NAMESPACE_END
207