cgv
Loading...
Searching...
No Matches
import.cxx
1#include <cgv/base/register.h>
2
3#include "import.h"
4
5#include <cgv/utils/dir.h>
6#include <cgv/utils/file.h>
7#include <cgv/utils/tokenizer.h>
8#include <cstdlib>
9
10
11
12#include <stdlib.h>
13#include <map>
14
15using namespace cgv::utils;
16using namespace cgv::utils::file;
17
18namespace cgv {
19 namespace base {
20
21std::string find_data_file_rec(const std::string& path, const std::string& file_name)
22{
23 if (cgv::utils::file::exists(path+'/'+file_name))
24 return path+'/'+file_name;
25 void* h = find_first(path+"*");
26 while (h) {
27 if (find_directory(h) && find_name(h) != "." && find_name(h) != "..") {
28 std::string fn = path+find_name(h)+'/';
29 if (exists(fn+file_name))
30 return fn+file_name;
31 fn = find_data_file_rec(fn, file_name);
32 if (!fn.empty())
33 return fn;
34 }
35 h = find_next(h);
36 }
37 return "";
38}
39
40std::string find_data_file_1(const std::string& base_path, const std::string& sub_path, const std::string& file_name, bool recurse)
41{
42 std::string base_path_prefix = base_path;
43 if (!base_path_prefix.empty() && base_path_prefix.back() != '/')
44 base_path_prefix += "/";
45 std::string dir_name = base_path_prefix+sub_path;
46 if (!dir_name.empty()) {
47 if (!dir::exists(dir_name))
48 return "";
49 if (dir_name.back() != '/')
50 dir_name += "/";
51 }
52 if (exists(dir_name+file_name))
53 return dir_name+file_name;
54 if (!recurse)
55 return "";
56 return find_data_file_rec(dir_name, file_name);
57}
58
59std::string find_data_file(const std::string& file_name, const std::string& strategy, const std::string& sub_directory, const std::string& master_path)
60{
61 for (unsigned i=0; i<strategy.size(); ++i) {
62 switch (strategy[i]) {
63 case 'r' :
64 case 'R' :
65 {
66 std::map<std::string, resource_file_info>::const_iterator it = ref_resource_file_map().find(file_name);
67 if (it != ref_resource_file_map().end())
68 return std::string("res://")+file_name;
69 break;
70 }
71 case 'c' :
72 case 'C' :
73 {
74 std::string fn = find_data_file_1("", sub_directory, file_name, strategy[i] == 'C');
75 //std::cout << " current -> " << fn << std::endl;
76 if (!fn.empty())
77 return fn;
78 break;
79 }
80 case 'm' :
81 case 'M' :
82 {
83 std::string fn = find_data_file_1(master_path, sub_directory, file_name, strategy[i] == 'M');
84 if (!fn.empty())
85 return fn;
86 break;
87 }
88 case 'd' :
89 case 'D' :
90 {
91 const std::vector<std::string>& path_list = ref_data_path_list();
92 for (unsigned int i=0; i<path_list.size(); ++i) {
93 std::string fn = find_data_file_1(path_list[i], sub_directory, file_name, strategy[i] == 'D');
94 if (!fn.empty())
95 return fn;
96 }
97 break;
98 }
99 case 'p' :
100 case 'P' :
101 {
102 const std::vector<std::string>& parent_stack = ref_parent_file_stack();
103 if (!parent_stack.empty()) {
104 std::string fn = find_data_file_1(parent_stack.back(), sub_directory, file_name, strategy[i] == 'P');
105 if (!fn.empty())
106 return fn;
107 }
108 break;
109 }
110 case 'a' :
111 case 'A' :
112 {
113 const std::vector<std::string>& parent_stack = ref_parent_file_stack();
114 for (size_t i=parent_stack.size(); i>0; --i) {
115 std::string fn = find_data_file_1(parent_stack[i-1], sub_directory, file_name, strategy[i] == 'A');
116 if (!fn.empty())
117 return fn;
118 }
119 break;
120 }
121 }
122 }
123 return std::string();
124}
125
126void stdout_message(const std::string& text)
127{
128 std::cout << text << std::endl;
129}
130
131void stderr_message(const std::string& text)
132{
133 std::cerr << text << std::endl;
134}
135
136int std_query(const std::string& text, const std::string& answers, int default_answer)
137{
138 std::vector<std::string> answer_strings;
139 std::vector<int> answer_values;
140
141 std::vector<cgv::utils::token> tokens;
142 cgv::utils::tokenizer(answers).set_ws(",").bite_all(tokens);
143 std::cout << text << "\n select answer from {" << answers << "} with 0..." << tokens.size() - 1 << ":>";
144 std::cout.flush();
145 int answer;
146 std::cin >> answer;
147 return answer;
148}
149
150std::string std_ask_dir(const std::string& text, const std::string& path)
151{
152 std::cout << text << "\n:>";
153 std::cout.flush();
154 std::string answer;
155 std::cin >> answer;
156 return answer;
157}
158
159std::string find_or_download_data_file(const std::string& file_name, const std::string& find_strategy,
160 const std::string& url, const std::string& cache_strategy, const std::string& producer,
161 const std::string& sub_directory, const std::string& master_path, user_feedback uf)
162{
163 // first try to find file
165 if (!file_path.empty())
166 return file_path;
167
168 // if not found find directory in which to cache download according to \c cache_strategy
169 size_t i = 0;
170 while (i < cache_strategy.size() && file_path.empty()) {
171 switch (cache_strategy[i++]) {
172 case 'c':
173 case 'C': file_path = "."; break;
174 case 'm':
175 case 'M': if (cgv::utils::dir::exists(master_path))
177 break;
178 case 'd':
179 case 'D': {
180 const std::vector<std::string>& path_list = cgv::base::ref_data_path_list();
181 for (unsigned int i = 0; i < path_list.size(); ++i)
182 if (cgv::utils::dir::exists(path_list[i])) {
184 break;
185 }
186 break;
187 }
188 case 'p':
189 case 'P': {
190 const std::vector<std::string>& parent_stack = cgv::base::ref_parent_file_stack();
191 if (!parent_stack.empty()) {
192 if (cgv::utils::dir::exists(parent_stack.back()))
193 file_path = parent_stack.back();
194 }
195 break;
196 }
197 case 'a':
198 case 'A': {
199 const std::vector<std::string>& parent_stack = cgv::base::ref_parent_file_stack();
200 for (size_t i = parent_stack.size(); i > 0; --i) {
201 if (cgv::utils::dir::exists(parent_stack[i])) {
203 break;
204 }
205 }
206 break;
207 }
208 }
209 }
210 if (file_path.empty()) {
211 if (uf.query == 0)
212 return "";
213 if (uf.query(producer + " wants to download <" + file_name + "> but has no path to store it. Do you want to specify a path to store the download?",
214 "Yes,No", 0) != 1)
215 return "";
216 if (uf.ask_dir == 0)
217 return "";
218 file_path = uf.ask_dir(std::string("specify path to store ") + producer + " downloads", master_path);
219 if (file_path.empty())
220 return "";
221 }
222 // extend file path with subdirectory
223 if (!sub_directory.empty()) {
224 if (file_path.back() != '/' && file_path.back() != '\\')
225 file_path += '/';
227 if (!cgv::utils::dir::exists(file_path)) {
228 if (uf.query == 0)
229 return "";
230 if (uf.query(producer + " wants to create directory <" + file_path + "> to store downloads. Do you allow this?",
231 "Yes,No", 0) != 1)
232 return "";
233 if (!cgv::utils::dir::mkdir(file_path)) {
234 if (uf.message != 0)
235 uf.message(std::string("could not create subdirectory <") + sub_directory + "> in cache path");
236 return "";
237 }
238 }
239 }
240 // append file name
241 if (file_path.back() != '/' && file_path.back() != '\\')
242 file_path += '/';
243 file_path += file_name;
244 // try to download file
245 std::string cmd = "curl --output \"";
246 cmd += file_path + "\" " + url;
247 bool retry;
248 do {
249 retry = false;
250 int result = system(cmd.c_str());
251 if (result == -1 || !cgv::utils::file::exists(file_path)) {
252 if (uf.query == 0)
253 return "";
254 if (uf.query(std::string("download of <") + file_name + "> failed. Please check internet connectivity! Try again?",
255 "Yes,No", 0) != 1)
256 return "";
257 retry = true;
258 }
259 } while (retry);
260 return file_path;
261}
262
263std::string clean_data_path(const std::string& data_path)
264{
265 if (data_path.size() == 0)
266 return data_path;
267 unsigned int p = (unsigned int) data_path.size()-1;
268 if (data_path[p] == '/' || data_path[p] == '\\')
269 return clean_data_path(data_path.substr(0,p));
270 return data_path;
271}
272
274std::vector<std::string>& ref_data_path_list()
275{
276 static std::vector<std::string> data_path_list;
277 static bool initialized = false;
278 if (!initialized) {
279 char* cgv_data = getenv("CGV_DATA");
280 if (cgv_data) {
281 std::string cgv_data_str(cgv_data);
282 std::vector<token> data_path_tokens;
284 for (unsigned int i=0; i<data_path_tokens.size(); ++i) {
285 std::string data_path = to_string(data_path_tokens[i]);
286 data_path_list.push_back(clean_data_path(data_path));
287 }
288 }
289 initialized = true;
290 }
291 return data_path_list;
292}
293
295void push_file_parent(const std::string& path_or_file_name)
296{
297 bool is_path;
298 if (cgv::utils::dir::exists(path_or_file_name))
299 is_path = true;
300 else if (cgv::utils::file::exists(path_or_file_name))
301 is_path = false;
302 else if (cgv::utils::file::get_extension(path_or_file_name).empty())
303 is_path = true;
304 else
305 is_path = false;
306
307 if (is_path)
309 else
310 ref_parent_file_stack().push_back(cgv::utils::file::get_path(path_or_file_name));
311}
312
315{
316 if (!ref_parent_file_stack().empty())
317 ref_parent_file_stack().pop_back();
318}
319
321std::vector<std::string>& ref_parent_file_stack()
322{
323 static std::vector<std::string> parent_file_stack;
324 return parent_file_stack;
325}
326
327bool find_and_extend_system_path(std::string& file_name)
328{
329 std::string system_paths(std::getenv("PATH"));
330 std::vector<cgv::utils::token> path_tokens;
331 bite_all(tokenizer(system_paths).set_ws(
332#ifdef _WIN32
333 ";"
334#else
335 ":"
336#endif
337 ), path_tokens);
338
339 for (unsigned int i = 0; i<path_tokens.size(); ++i) {
340 std::string system_path = to_string(path_tokens[i]);
341 if (cgv::utils::file::exists(system_path + "/" + file_name)) {
342 file_name = system_path + "/" + file_name;
343 return true;
344 }
345 }
346 return false;
347}
348
350FILE* open_data_file(const std::string& file_name, const char* mode)
351{
352 if (file_name.substr(0,6) != "res://")
353 return fopen(file_name.c_str(), mode);
354 std::map<std::string, resource_file_info>::const_iterator i = ref_resource_file_map().find(file_name.substr(6));
355 if (i == ref_resource_file_map().end())
356 return 0;
357 std::string source_file = i->second.source_file;
358 if (source_file.empty())
359 source_file = ref_prog_path_prefix() + ref_prog_name();
360 else {
361 if (cgv::utils::file::exists(ref_prog_path_prefix() + source_file))
362 source_file = ref_prog_path_prefix() + source_file;
363 else {
364 if (!find_and_extend_system_path(source_file)) {
365 std::cerr << "ERROR: could not find " << source_file << " to import " << i->second.file_data << std::endl;
366 return 0;
367 }
368 }
369 }
370 unsigned int off = i->second.file_offset;
371 if (off == (unsigned int)-1) {
372 off = find_file_offset(source_file, i->second.file_data, i->second.file_length);
373 if (off == (unsigned int)-1) {
374 std::cerr << "ERROR: could not find offset of " << i->second.file_data << " to import from " << source_file << std::endl;
375 return 0;
376 }
377 }
378 FILE* fp = fopen(source_file.c_str(), mode);
379 if (!fp) {
380 std::cerr << "ERROR: could not open " << source_file << " to import " << i->second.file_data << std::endl;
381 return 0;
382 }
383 fseek(fp, off, SEEK_SET);
384 return fp;
385}
386
388bool read_data_file(const std::string& file_name, std::string& content, bool ascii)
389{
390 if (file_name.substr(0, 6) == "str://") {
391 std::map<std::string, resource_file_info>::const_iterator it = ref_resource_file_map().find(file_name.substr(6));
392 if (it == ref_resource_file_map().end())
393 return false;
394 content = it->second.file_data;
395 return true;
396 }
397 if (file_name.substr(0, 6) != "res://")
398 return file::read(file_name, content, ascii);
399 unsigned int n = data_file_size(file_name);
400 content.resize(n);
401 FILE* fp = open_data_file(file_name, ascii?"r":"rb");
402 if (!fp)
403 return false;
404 if (!fread(&content[0], 1, n, fp)) {
405 fclose(fp);
406 return false;
407 }
408 fclose(fp);
409 return true;
410}
411
413unsigned int data_file_size(const std::string& file_name)
414{
415 if (file_name.substr(0,6) != "res://")
416 return (unsigned int) file::size(file_name.c_str());
417 return ref_resource_file_map()[file_name.substr(6)].file_length;
418}
419
421unsigned int find_file_offset(const std::string& file_name, const char* data, unsigned int data_size)
422{
423 size_t file_size;
424 char* file_data = read(file_name, false, &file_size);
425 if (!file_data || file_size < data_size)
426 return -1;
427 unsigned int n = (unsigned int) (file_size-data_size);
428 char* fdata = file_data;
429 for (unsigned int off = 0; off < n; ++off, ++fdata) {
430 bool found = true;
431 for (unsigned int i = 0; i < data_size; ++i) {
432 if (data[i] != fdata[i]) {
433 found = false;
434 break;
435 }
436 }
437 if (found) {
438 delete [] file_data;
439 return off;
440 }
441 }
442 delete [] file_data;
443 return -1;
444}
445
446
447 }
448}
complete implementation of method actions that only call one method when entering a node
Definition action.h:113
the tokenizer allows to split text into tokens in a convenient way.
Definition tokenizer.h:68
tokenizer & set_ws(const std::string &ws)
set the list of white spaces, that separate tokens and are skipped
Definition tokenizer.cxx:38
std::string std_ask_dir(const std::string &text, const std::string &path)
std::cout and std::cin based implementation of the ask_dir function for third argument to the user_fe...
Definition import.cxx:150
std::string & ref_prog_path_prefix()
return a refence to the path prefix of the started executable, this can be prepended for example to d...
Definition register.cxx:131
std::string find_or_download_data_file(const std::string &file_name, const std::string &find_strategy, const std::string &url, const std::string &cache_strategy, const std::string &producer, const std::string &sub_directory, const std::string &master_path, user_feedback uf)
same as find_data_file() but in case file is not found, it is downloaded from the provided url and st...
Definition import.cxx:159
void stderr_message(const std::string &text)
std::cerr based implementation of the message function for first argument to the user_feedback constr...
Definition import.cxx:131
int std_query(const std::string &text, const std::string &answers, int default_answer)
std::cout and std::cin based implementation of the query function for second argument to the user_fee...
Definition import.cxx:136
unsigned int find_file_offset(const std::string &file_name, const char *data, unsigned int data_size)
find the offset of the given data block in the given file
Definition import.cxx:421
void pop_file_parent()
pop the latestly pushed parent path from the parent path stack.
Definition import.cxx:314
void push_file_parent(const std::string &path_or_file_name)
extract a valid path from the given argument and push it onto the stack of parent paths....
Definition import.cxx:295
std::vector< std::string > & ref_parent_file_stack()
return a reference to the data path list, which is constructed from the environment variable CGV_DATA
Definition import.cxx:321
bool read_data_file(const std::string &file_name, std::string &content, bool ascii)
read ascii file into a string
Definition import.cxx:388
std::vector< std::string > & ref_data_path_list()
return a reference to the data path list, which is constructed from the environment variable CGV_DATA
Definition import.cxx:274
FILE * open_data_file(const std::string &file_name, const char *mode)
open a file with fopen supporting resource files, that have the prefix "res://"
Definition import.cxx:350
std::map< std::string, resource_file_info > & ref_resource_file_map()
return a reference to a mapping of resource file names to resource file infos
Definition register.cxx:143
std::string find_data_file(const std::string &file_name, const std::string &strategy, const std::string &sub_directory, const std::string &master_path)
Find a file with the given strategy and return the file name extended by the necessary path.
Definition import.cxx:59
std::string & ref_prog_name()
return a refence to the name of the started executable
Definition register.cxx:125
unsigned int data_file_size(const std::string &file_name)
return the file size of a given file with support for resource files, that have the prefix "res://"
Definition import.cxx:413
void stdout_message(const std::string &text)
std::cout based implementation of the message function for first argument to the user_feedback constr...
Definition import.cxx:126
namespace that holds tools that dont fit any other namespace
std::string to_string(const std::string &v, unsigned int w, unsigned int p, bool)
specialization of conversion from string to strings
void bite_all(tokenizer &t, std::vector< token > &result)
bite all tokens into a token vector
Definition tokenizer.h:121
the cgv namespace
Definition print.h:11
function pointers implementing user feedback functionality of find_or_download_data_file() function
Definition import.h:56