Fix incorrect FLT_MIN use
[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/filesystem.h>
23 #include <OpenImageIO/strutil.h>
24 #include <OpenImageIO/sysutil.h>
25
26 OIIO_NAMESPACE_USING
27
28 #include <stdio.h>
29
30 #include <sys/stat.h>
31
32 #if defined(_WIN32)
33 #  define DIR_SEP '\\'
34 #  define DIR_SEP_ALT '/'
35 #  include <direct.h>
36 #else
37 #  define DIR_SEP '/'
38 #  include <dirent.h>
39 #endif
40
41 #ifdef HAVE_SHLWAPI_H
42 #  include <shlwapi.h>
43 #endif
44
45 #include "util_windows.h"
46
47 CCL_NAMESPACE_BEGIN
48
49 #ifdef _WIN32
50 #  if defined(_MSC_VER) || defined(__MINGW64__)
51 typedef struct _stat64 path_stat_t;
52 #  elif defined(__MINGW32__)
53 typedef struct _stati64 path_stat_t;
54 #  else
55 typedef struct _stat path_stat_t;
56 #  endif
57 #  ifndef S_ISDIR
58 #    define S_ISDIR(x) (((x) & _S_IFDIR) == _S_IFDIR)
59 #  endif
60 #else
61 typedef struct stat path_stat_t;
62 #endif
63
64 static string cached_path = "";
65 static string cached_user_path = "";
66
67 namespace {
68
69 #ifdef _WIN32
70 class directory_iterator {
71 public:
72         class path_info {
73         public:
74                 path_info(const string& path,
75                           const WIN32_FIND_DATAW& find_data)
76                 : path_(path),
77                   find_data_(find_data)
78                 {
79                 }
80
81                 string path() {
82                         return path_join(path_, string_from_wstring(find_data_.cFileName));
83                 }
84         protected:
85                 const string& path_;
86                 const WIN32_FIND_DATAW& find_data_;
87         };
88
89         directory_iterator()
90         : path_info_("", find_data_),
91           h_find_(INVALID_HANDLE_VALUE)
92         {
93         }
94
95         directory_iterator(const string& path)
96         : path_(path),
97           path_info_(path, find_data_)
98         {
99                 string wildcard = path;
100                 if(wildcard[wildcard.size() - 1] != DIR_SEP) {
101                         wildcard += DIR_SEP;
102                 }
103                 wildcard += "*";
104                 h_find_ = FindFirstFileW(string_to_wstring(wildcard).c_str(),
105                                          &find_data_);
106                 if(h_find_ != INVALID_HANDLE_VALUE) {
107                         skip_dots();
108                 }
109         }
110
111         ~directory_iterator()
112         {
113                 if(h_find_ != INVALID_HANDLE_VALUE) {
114                         FindClose(h_find_);
115                 }
116         }
117
118         directory_iterator& operator++()
119         {
120                 step();
121                 return *this;
122         }
123
124         path_info* operator-> ()
125         {
126                 return &path_info_;
127         }
128
129         bool operator!=(const directory_iterator& other)
130         {
131                 return h_find_ != other.h_find_;
132         }
133
134 protected:
135         bool step()
136         {
137                 if(do_step()) {
138                         return skip_dots();
139                 }
140                 return false;
141         }
142
143         bool do_step()
144         {
145                 if(h_find_ != INVALID_HANDLE_VALUE) {
146                         bool result = FindNextFileW(h_find_, &find_data_) == TRUE;
147                         if(!result) {
148                                 FindClose(h_find_);
149                                 h_find_ = INVALID_HANDLE_VALUE;
150                         }
151                         return result;
152                 }
153                 return false;
154         }
155
156         bool skip_dots()
157         {
158                 while(wcscmp(find_data_.cFileName, L".") == 0 ||
159                       wcscmp(find_data_.cFileName, L"..") == 0)
160                 {
161                         if(!do_step()) {
162                                 return false;
163                         }
164                 }
165                 return true;
166         }
167
168         string path_;
169         path_info path_info_;
170         WIN32_FIND_DATAW find_data_;
171         HANDLE h_find_;
172 };
173 #else  /* _WIN32 */
174
175 class directory_iterator {
176 public:
177         class path_info {
178         public:
179                 path_info(const string& path)
180                 : path_(path),
181                   entry_(NULL)
182                 {
183                 }
184
185                 string path() {
186                         return path_join(path_, entry_->d_name);
187                 }
188
189                 void current_entry_set(const struct dirent *entry)
190                 {
191                         entry_ = entry;
192                 }
193         protected:
194                 const string& path_;
195                 const struct dirent *entry_;
196         };
197
198         directory_iterator()
199         : path_info_(""),
200           name_list_(NULL),
201           num_entries_(-1),
202           cur_entry_(-1)
203         {
204         }
205
206         directory_iterator(const string& path)
207         : path_(path),
208           path_info_(path_),
209           cur_entry_(0)
210         {
211                 num_entries_ = scandir(path.c_str(),
212                                        &name_list_,
213                                        NULL,
214                                        alphasort);
215                 if(num_entries_ < 0) {
216                         perror("scandir");
217                 }
218                 else {
219                         skip_dots();
220                 }
221         }
222
223         ~directory_iterator()
224         {
225                 destroy_name_list();
226         }
227
228         directory_iterator& operator++()
229         {
230                 step();
231                 return *this;
232         }
233
234         path_info* operator-> ()
235         {
236                 path_info_.current_entry_set(name_list_[cur_entry_]);
237                 return &path_info_;
238         }
239
240         bool operator!=(const directory_iterator& other)
241         {
242                 return name_list_ != other.name_list_;
243         }
244
245 protected:
246         bool step()
247         {
248                 if(do_step()) {
249                         return skip_dots();
250                 }
251                 return false;
252         }
253
254         bool do_step()
255         {
256                 ++cur_entry_;
257                 if(cur_entry_ >= num_entries_) {
258                         destroy_name_list();
259                         return false;
260                 }
261                 return true;
262         }
263
264         /* Skip . and .. folders. */
265         bool skip_dots()
266         {
267                 while(strcmp(name_list_[cur_entry_]->d_name, ".") == 0 ||
268                       strcmp(name_list_[cur_entry_]->d_name, "..") == 0)
269                 {
270                         if(!step()) {
271                                 return false;
272                         }
273                 }
274                 return true;
275         }
276
277         void destroy_name_list()
278         {
279                 if(name_list_ == NULL) {
280                         return;
281                 }
282                 for(int i = 0; i < num_entries_; ++i) {
283                         free(name_list_[i]);
284                 }
285                 free(name_list_);
286                 name_list_ = NULL;
287         }
288
289         string path_;
290         path_info path_info_;
291         struct dirent **name_list_;
292         int num_entries_, cur_entry_;
293 };
294
295 #endif  /* _WIN32 */
296
297 size_t find_last_slash(const string& path)
298 {
299         for(size_t i = 0; i < path.size(); ++i) {
300                 size_t index = path.size() - 1 - i;
301 #ifdef _WIN32
302                 if(path[index] == DIR_SEP || path[index] == DIR_SEP_ALT)
303 #else
304                 if(path[index] == DIR_SEP)
305 #endif
306                 {
307                         return index;
308                 }
309         }
310         return string::npos;
311 }
312
313 }  /* namespace */
314
315 static char *path_specials(const string& sub)
316 {
317         static bool env_init = false;
318         static char *env_shader_path;
319         static char *env_kernel_path;
320         if(!env_init) {
321                 env_shader_path = getenv("CYCLES_SHADER_PATH");
322                 env_kernel_path = getenv("CYCLES_KERNEL_PATH");
323                 env_init = true;
324         }
325         if(env_shader_path != NULL && sub == "shader") {
326                 return env_shader_path;
327         }
328         else if(env_shader_path != NULL && sub == "kernel") {
329                 return env_kernel_path;
330         }
331         return NULL;
332 }
333
334 void path_init(const string& path, const string& user_path)
335 {
336         cached_path = path;
337         cached_user_path = user_path;
338
339 #ifdef _MSC_VER
340         // workaround for https://svn.boost.org/trac/boost/ticket/6320
341         // indirectly init boost codec here since it's not thread safe, and can
342         // cause crashes when it happens in multithreaded image load
343         OIIO::Filesystem::exists(path);
344 #endif
345 }
346
347 string path_get(const string& sub)
348 {
349         char *special = path_specials(sub);
350         if(special != NULL)
351                 return special;
352
353         if(cached_path == "")
354                 cached_path = path_dirname(Sysutil::this_program_path());
355
356         return path_join(cached_path, sub);
357 }
358
359 string path_user_get(const string& sub)
360 {
361         if(cached_user_path == "")
362                 cached_user_path = path_dirname(Sysutil::this_program_path());
363
364         return path_join(cached_user_path, sub);
365 }
366
367 string path_filename(const string& path)
368 {
369         size_t index = find_last_slash(path);
370         if(index != string::npos) {
371                 /* Corner cases to match boost behavior. */
372 #ifndef _WIN32
373                 if(index == 0 && path.size() == 1) {
374                         return path;
375                 }
376 #endif
377                 if(index == path.size() - 1) {
378 #ifdef _WIN32
379                         if(index == 2) {
380                                 return string(1, DIR_SEP);
381                         }
382 #endif
383                         return ".";
384                 }
385                 return path.substr(index + 1, path.size() - index - 1);
386         }
387         return path;
388 }
389
390 string path_dirname(const string& path)
391 {
392         size_t index = find_last_slash(path);
393         if(index != string::npos) {
394 #ifndef _WIN32
395                 if(index == 0 && path.size() > 1) {
396                         return string(1, DIR_SEP);
397                 }
398 #endif
399                 return path.substr(0, index);
400         }
401         return "";
402 }
403
404 string path_join(const string& dir, const string& file)
405 {
406         if(dir.size() == 0) {
407                 return file;
408         }
409         if(file.size() == 0) {
410                 return dir;
411         }
412         string result = dir;
413 #ifndef _WIN32
414         if(result[result.size() - 1] != DIR_SEP &&
415            file[0] != DIR_SEP)
416 #else
417         if(result[result.size() - 1] != DIR_SEP &&
418            result[result.size() - 1] != DIR_SEP_ALT &&
419            file[0] != DIR_SEP &&
420            file[0] != DIR_SEP_ALT)
421 #endif
422         {
423                 result += DIR_SEP;
424         }
425         result += file;
426         return result;
427 }
428
429 string path_escape(const string& path)
430 {
431         string result = path;
432         string_replace(result, " ", "\\ ");
433         return result;
434 }
435
436 bool path_is_relative(const string& path)
437 {
438 #ifdef _WIN32
439 #  ifdef HAVE_SHLWAPI_H
440         return PathIsRelative(path.c_str());
441 #  else  /* HAVE_SHLWAPI_H */
442         if(path.size() >= 3) {
443                 return !(((path[0] >= 'a' && path[0] <= 'z') ||
444                          (path[0] >= 'A' && path[0] <= 'Z')) &&
445                          path[1] == ':' && path[2] == DIR_SEP);
446         }
447         return true;
448 #  endif  /* HAVE_SHLWAPI_H */
449 #else  /* _WIN32 */
450         if(path.size() == 0) {
451                 return 1;
452         }
453         return path[0] != DIR_SEP;
454 #endif  /* _WIN32 */
455 }
456
457 #ifdef _WIN32
458 /* Add a slash if the UNC path points to a share. */
459 static string path_unc_add_slash_to_share(const string& path)
460 {
461         size_t slash_after_server = path.find(DIR_SEP, 2);
462         if(slash_after_server != string::npos) {
463                 size_t slash_after_share = path.find(DIR_SEP,
464                                                      slash_after_server + 1);
465                 if(slash_after_share == string::npos) {
466                         return path + DIR_SEP;
467                 }
468         }
469         return path;
470 }
471
472 /* Convert:
473  *    \\?\UNC\server\share\folder\... to \\server\share\folder\...
474  *    \\?\C:\ to C:\ and \\?\C:\folder\... to C:\folder\...
475  */
476 static string path_unc_to_short(const string& path)
477 {
478         size_t len = path.size();
479         if((len > 3) &&
480            (path[0] ==  DIR_SEP) &&
481            (path[1] ==  DIR_SEP) &&
482            (path[2] ==  '?') &&
483            ((path[3] ==  DIR_SEP) || (path[3] ==  DIR_SEP_ALT)))
484         {
485                 if((len > 5) && (path[5] ==  ':')) {
486                         return path.substr(4, len - 4);
487                 }
488                 else if((len > 7) &&
489                         (path.substr(4, 3) == "UNC") &&
490                         ((path[7] ==  DIR_SEP) || (path[7] ==  DIR_SEP_ALT)))
491                 {
492                         return "\\\\" + path.substr(8, len - 8);
493                 }
494         }
495         return path;
496 }
497
498 static string path_cleanup_unc(const string& path)
499 {
500         string result = path_unc_to_short(path);
501         if(path.size() > 2) {
502                 /* It's possible path is now a non-UNC. */
503                 if(result[0] == DIR_SEP && result[1] == DIR_SEP) {
504                         return path_unc_add_slash_to_share(result);
505                 }
506         }
507         return result;
508 }
509
510 /* Make path compatible for stat() functions. */
511 static string path_make_compatible(const string& path)
512 {
513         string result = path;
514         /* In Windows stat() doesn't recognize dir ending on a slash. */
515         if(result.size() > 3 && result[result.size() - 1] == DIR_SEP) {
516                 result.resize(result.size() - 1);
517         }
518         /* Clean up UNC path. */
519         if((path.size() >= 3) && (path[0] == DIR_SEP) && (path[1] == DIR_SEP)) {
520                 result = path_cleanup_unc(result);
521         }
522         /* Make sure volume-only path ends up wit ha directory separator. */
523         if(result.size() == 2 && result[1] == ':') {
524                 result += DIR_SEP;
525         }
526         return result;
527 }
528
529 static int path_wstat(const wstring& path_wc, path_stat_t *st)
530 {
531 #if defined(_MSC_VER) || defined(__MINGW64__)
532         return _wstat64(path_wc.c_str(), st);
533 #elif defined(__MINGW32__)
534         return _wstati64(path_wc.c_str(), st);
535 #else
536         return _wstat(path_wc.c_str(), st);
537 #endif
538 }
539
540 static int path_stat(const string& path, path_stat_t *st)
541 {
542         wstring path_wc = string_to_wstring(path);
543         return path_wstat(path_wc, st);
544 }
545 #else  /* _WIN32 */
546 static int path_stat(const string& path, path_stat_t *st)
547 {
548         return stat(path.c_str(), st);
549 }
550 #endif  /* _WIN32 */
551
552 size_t path_file_size(const string& path)
553 {
554         path_stat_t st;
555         if(path_stat(path, &st) != 0) {
556                 return -1;
557         }
558         return st.st_size;
559 }
560
561 bool path_exists(const string& path)
562 {
563 #ifdef _WIN32
564         string fixed_path = path_make_compatible(path);
565         wstring path_wc = string_to_wstring(fixed_path);
566         path_stat_t st;
567         if(path_wstat(path_wc, &st) != 0) {
568                 return false;
569         }
570         return st.st_mode != 0;
571 #else  /* _WIN32 */
572         struct stat st;
573         if(stat(path.c_str(), &st) != 0) {
574                 return 0;
575         }
576         return st.st_mode != 0;
577 #endif /* _WIN32 */
578 }
579
580 bool path_is_directory(const string& path)
581 {
582         path_stat_t st;
583         if(path_stat(path, &st) != 0) {
584                 return false;
585         }
586         return S_ISDIR(st.st_mode);
587 }
588
589 static void path_files_md5_hash_recursive(MD5Hash& hash, const string& dir)
590 {
591         if(path_exists(dir)) {
592                 directory_iterator it(dir), it_end;
593
594                 for(; it != it_end; ++it) {
595                         if(path_is_directory(it->path())) {
596                                 path_files_md5_hash_recursive(hash, it->path());
597                         }
598                         else {
599                                 string filepath = it->path();
600
601                                 hash.append((const uint8_t*)filepath.c_str(), filepath.size());
602                                 hash.append_file(filepath);
603                         }
604                 }
605         }
606 }
607
608 string path_files_md5_hash(const string& dir)
609 {
610         /* computes md5 hash of all files in the directory */
611         MD5Hash hash;
612
613         path_files_md5_hash_recursive(hash, dir);
614
615         return hash.get_hex();
616 }
617
618 static bool create_directories_recursivey(const string& path)
619 {
620         if(path_is_directory(path)) {
621                 /* Directory already exists, nothing to do. */
622                 return true;
623         }
624         if(path_exists(path)) {
625                 /* File exists and it's not a directory. */
626                 return false;
627         }
628
629         string parent = path_dirname(path);
630         if(parent.size() > 0 && parent != path) {
631                 if(!create_directories_recursivey(parent)) {
632                         return false;
633                 }
634         }
635
636 #ifdef _WIN32
637         wstring path_wc = string_to_wstring(path);
638         return _wmkdir(path_wc.c_str()) == 0;
639 #else
640         return mkdir(path.c_str(), 0777) == 0;
641 #endif
642 }
643
644 void path_create_directories(const string& filepath)
645 {
646         string path = path_dirname(filepath);
647         create_directories_recursivey(path);
648 }
649
650 bool path_write_binary(const string& path, const vector<uint8_t>& binary)
651 {
652         path_create_directories(path);
653
654         /* write binary file from memory */
655         FILE *f = path_fopen(path, "wb");
656
657         if(!f)
658                 return false;
659
660         if(binary.size() > 0)
661                 fwrite(&binary[0], sizeof(uint8_t), binary.size(), f);
662
663         fclose(f);
664
665         return true;
666 }
667
668 bool path_write_text(const string& path, string& text)
669 {
670         vector<uint8_t> binary(text.length(), 0);
671         std::copy(text.begin(), text.end(), binary.begin());
672
673         return path_write_binary(path, binary);
674 }
675
676 bool path_read_binary(const string& path, vector<uint8_t>& binary)
677 {
678         /* read binary file into memory */
679         FILE *f = path_fopen(path, "rb");
680
681         if(!f) {
682                 binary.resize(0);
683                 return false;
684         }
685
686         binary.resize(path_file_size(path));
687
688         if(binary.size() == 0) {
689                 fclose(f);
690                 return false;
691         }
692
693         if(fread(&binary[0], sizeof(uint8_t), binary.size(), f) != binary.size()) {
694                 fclose(f);
695                 return false;
696         }
697
698         fclose(f);
699
700         return true;
701 }
702
703 bool path_read_text(const string& path, string& text)
704 {
705         vector<uint8_t> binary;
706
707         if(!path_exists(path) || !path_read_binary(path, binary))
708                 return false;
709
710         const char *str = (const char*)&binary[0];
711         size_t size = binary.size();
712         text = string(str, size);
713
714         return true;
715 }
716
717 uint64_t path_modified_time(const string& path)
718 {
719         path_stat_t st;
720         if(path_stat(path, &st) != 0) {
721                 return st.st_mtime;
722         }
723         return 0;
724 }
725
726 bool path_remove(const string& path)
727 {
728         return remove(path.c_str()) == 0;
729 }
730
731 string path_source_replace_includes(const string& source, const string& path)
732 {
733         /* Our own little c preprocessor that replaces #includes with the file
734          * contents, to work around issue of opencl drivers not supporting
735          * include paths with spaces in them.
736          */
737
738         string result = "";
739         vector<string> lines;
740         string_split(lines, source, "\n");
741
742         for(size_t i = 0; i < lines.size(); ++i) {
743                 string line = lines[i];
744                 if(line[0] == '#') {
745                         string token = string_strip(line.substr(1, line.size() - 1));
746                         if(string_startswith(token, "include")) {
747                                 token = string_strip(token.substr(7, token.size() - 7));
748                                 if(token[0] == '"') {
749                                         size_t n_start = 1;
750                                         size_t n_end = token.find("\"", n_start);
751                                         string filename = token.substr(n_start, n_end - n_start);
752                                         string text, filepath = path_join(path, filename);
753                                         if(path_read_text(filepath, text)) {
754                                                 /* Replace include directories with both current path
755                                                  * and path extracted from the include file.
756                                                  * Not totally robust, but works fine for Cycles kernel
757                                                  * and avoids having list of include directories.x
758                                                  */
759                                                 text = path_source_replace_includes(
760                                                         text, path_dirname(filepath));
761                                                 text = path_source_replace_includes(text, path);
762                                                 line = token.replace(0, n_end + 1, "\n" + text + "\n");
763                                         }
764                                 }
765                         }
766                 }
767                 result += line + "\n";
768         }
769
770         return result;
771 }
772
773 FILE *path_fopen(const string& path, const string& mode)
774 {
775 #ifdef _WIN32
776         wstring path_wc = string_to_wstring(path);
777         wstring mode_wc = string_to_wstring(mode);
778         return _wfopen(path_wc.c_str(), mode_wc.c_str());
779 #else
780         return fopen(path.c_str(), mode.c_str());
781 #endif
782 }
783
784 void path_cache_clear_except(const string& name, const set<string>& except)
785 {
786         string dir = path_user_get("cache");
787
788         if(path_exists(dir)) {
789                 directory_iterator it(dir), it_end;
790
791                 for(; it != it_end; ++it) {
792                         string filename = path_filename(it->path());
793
794                         if(string_startswith(filename, name.c_str()))
795                                 if(except.find(filename) == except.end())
796                                         path_remove(it->path());
797                 }
798         }
799
800 }
801
802 CCL_NAMESPACE_END
803