Blender V5.0
path.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
5#include "util/path.h"
6#include "util/algorithm.h"
7#include "util/map.h"
8#include "util/md5.h"
9#include "util/set.h"
10#include "util/string.h"
11#include "util/vector.h"
12
13#include <OpenImageIO/filesystem.h>
14#include <OpenImageIO/strutil.h>
15#include <OpenImageIO/sysutil.h>
16
17#include <cstdio>
18
19#include <sys/stat.h>
20
21#include <zstd.h>
22
23#if defined(_WIN32)
24# define DIR_SEP '\\'
25# define DIR_SEP_ALT '/'
26# include <direct.h>
27#else
28# define DIR_SEP '/'
29# include <dirent.h>
30# include <pwd.h>
31# include <sys/types.h>
32# include <unistd.h>
33#endif
34
35#ifdef HAVE_SHLWAPI_H
36# include <shlwapi.h>
37#endif
38
39#ifdef _WIN32
40# include "util/windows.h"
41#endif
42
44
45#ifdef _WIN32
46# if defined(_MSC_VER) || defined(__MINGW64__)
47typedef struct _stat64 path_stat_t;
48# elif defined(__MINGW32__)
49typedef struct _stati64 path_stat_t;
50# else
51typedef struct _stat path_stat_t;
52# endif
53# ifndef S_ISDIR
54# define S_ISDIR(x) (((x) & _S_IFDIR) == _S_IFDIR)
55# endif
56#else
57using path_stat_t = struct stat;
58#endif
59
60static string cached_path;
61static string cached_user_path;
63
64namespace {
65
66#ifdef _WIN32
67class directory_iterator {
68 public:
69 class path_info {
70 public:
71 path_info(const string &path, const WIN32_FIND_DATAW &find_data)
72 : path_(path), find_data_(find_data)
73 {
74 }
75
76 string path()
77 {
78 return path_join(path_, string_from_wstring(find_data_.cFileName));
79 }
80
81 protected:
82 const string &path_;
83 const WIN32_FIND_DATAW &find_data_;
84 };
85
86 directory_iterator() : path_info_("", find_data_), h_find_(INVALID_HANDLE_VALUE) {}
87
88 explicit directory_iterator(const string &path) : path_(path), path_info_(path, find_data_)
89 {
90 string wildcard = path;
91 if (wildcard[wildcard.size() - 1] != DIR_SEP) {
92 wildcard += DIR_SEP;
93 }
94 wildcard += "*";
95 h_find_ = FindFirstFileW(string_to_wstring(wildcard).c_str(), &find_data_);
96 if (h_find_ != INVALID_HANDLE_VALUE) {
97 skip_dots();
98 }
99 }
100
101 ~directory_iterator()
102 {
103 if (h_find_ != INVALID_HANDLE_VALUE) {
104 FindClose(h_find_);
105 }
106 }
107
108 directory_iterator &operator++()
109 {
110 step();
111 return *this;
112 }
113
114 path_info *operator->()
115 {
116 return &path_info_;
117 }
118
119 bool operator!=(const directory_iterator &other)
120 {
121 return h_find_ != other.h_find_;
122 }
123
124 protected:
125 bool step()
126 {
127 if (do_step()) {
128 return skip_dots();
129 }
130 return false;
131 }
132
133 bool do_step()
134 {
135 if (h_find_ != INVALID_HANDLE_VALUE) {
136 bool result = FindNextFileW(h_find_, &find_data_) == TRUE;
137 if (!result) {
138 FindClose(h_find_);
139 h_find_ = INVALID_HANDLE_VALUE;
140 }
141 return result;
142 }
143 return false;
144 }
145
146 bool skip_dots()
147 {
148 while (wcscmp(find_data_.cFileName, L".") == 0 || wcscmp(find_data_.cFileName, L"..") == 0) {
149 if (!do_step()) {
150 return false;
151 }
152 }
153 return true;
154 }
155
156 string path_;
157 path_info path_info_;
158 WIN32_FIND_DATAW find_data_;
159 HANDLE h_find_;
160};
161#else /* _WIN32 */
162
163class directory_iterator {
164 public:
165 class path_info {
166 public:
167 explicit path_info(const string &path) : path_(path) {}
168
169 string path()
170 {
171 return path_join(path_, entry_->d_name);
172 }
173
174 void current_entry_set(const struct dirent *entry)
175 {
176 entry_ = entry;
177 }
178
179 protected:
180 const string &path_;
181 const struct dirent *entry_ = nullptr;
182 };
183
184 directory_iterator() : path_info_(""), name_list_(nullptr), num_entries_(-1), cur_entry_(-1) {}
185
186 explicit directory_iterator(const string &path) : path_(path), path_info_(path_), cur_entry_(0)
187 {
188 num_entries_ = scandir(path.c_str(), &name_list_, nullptr, alphasort);
189 if (num_entries_ < 0) {
190 perror("scandir");
191 }
192 else {
193 skip_dots();
194 }
195 }
196
197 ~directory_iterator()
198 {
199 destroy_name_list();
200 }
201
202 directory_iterator &operator++()
203 {
204 step();
205 return *this;
206 }
207
208 path_info *operator->()
209 {
210 path_info_.current_entry_set(name_list_[cur_entry_]);
211 return &path_info_;
212 }
213
214 bool operator!=(const directory_iterator &other)
215 {
216 return name_list_ != other.name_list_;
217 }
218
219 protected:
220 bool step()
221 {
222 if (do_step()) {
223 return skip_dots();
224 }
225 return false;
226 }
227
228 bool do_step()
229 {
230 ++cur_entry_;
231 if (cur_entry_ >= num_entries_) {
232 destroy_name_list();
233 return false;
234 }
235 return true;
236 }
237
238 /* Skip . and .. folders. */
239 bool skip_dots()
240 {
241 while (strcmp(name_list_[cur_entry_]->d_name, ".") == 0 ||
242 strcmp(name_list_[cur_entry_]->d_name, "..") == 0)
243 {
244 if (!step()) {
245 return false;
246 }
247 }
248 return true;
249 }
250
251 void destroy_name_list()
252 {
253 if (name_list_ == nullptr) {
254 return;
255 }
256 for (int i = 0; i < num_entries_; ++i) {
257 free(name_list_[i]);
258 }
259 free((void *)name_list_);
260 name_list_ = nullptr;
261 }
262
263 string path_;
264 path_info path_info_;
265 struct dirent **name_list_;
266 int num_entries_, cur_entry_;
267};
268
269#endif /* _WIN32 */
270
271size_t find_last_slash(const string &path)
272{
273 for (size_t i = 0; i < path.size(); ++i) {
274 const size_t index = path.size() - 1 - i;
275#ifdef _WIN32
276 if (path[index] == DIR_SEP || path[index] == DIR_SEP_ALT)
277#else
278 if (path[index] == DIR_SEP)
279#endif
280 {
281 return index;
282 }
283 }
284 return string::npos;
285}
286
287} /* namespace */
288
289static char *path_specials(const string &sub)
290{
291 static bool env_init = false;
292 static char *env_shader_path;
293 static char *env_source_path;
294 if (!env_init) {
295 env_shader_path = getenv("CYCLES_SHADER_PATH");
296 /* NOTE: It is KERNEL in env variable for compatibility reasons. */
297 env_source_path = getenv("CYCLES_KERNEL_PATH");
298 env_init = true;
299 }
300 if (env_shader_path != nullptr && sub == "shader") {
301 return env_shader_path;
302 }
303 if (env_source_path != nullptr && sub == "source") {
304 return env_source_path;
305 }
306 return nullptr;
307}
308
309#if defined(__linux__) || defined(__APPLE__)
310static string path_xdg_cache_get()
311{
312 const char *home = getenv("XDG_CACHE_HOME");
313 if (home) {
314 return string(home);
315 }
316 home = getenv("HOME");
317 if (home == nullptr) {
318 home = getpwuid(getuid())->pw_dir;
319 }
320 return path_join(string(home), ".cache");
321}
322#endif
323
324void path_init(const string &path, const string &user_path)
325{
326 cached_path = path;
327 cached_user_path = user_path;
328
329#ifdef _MSC_VER
330 // workaround for https://svn.boost.org/trac/boost/ticket/6320
331 // indirectly init boost codec here since it's not thread safe, and can
332 // cause crashes when it happens in multithreaded image load
333 OIIO::Filesystem::exists(path);
334#endif
335}
336
337string path_get(const string &sub)
338{
339 char *special = path_specials(sub);
340 if (special != nullptr) {
341 return special;
342 }
343
344 if (cached_path.empty()) {
345 cached_path = path_dirname(OIIO::Sysutil::this_program_path());
346 }
347
348 return path_join(cached_path, sub);
349}
350
351string path_user_get(const string &sub)
352{
353 if (cached_user_path.empty()) {
354 cached_user_path = path_dirname(OIIO::Sysutil::this_program_path());
355 }
356
357 return path_join(cached_user_path, sub);
358}
359
360string path_cache_get(const string &sub)
361{
362#if defined(__linux__) || defined(__APPLE__)
363 if (cached_xdg_cache_path.empty()) {
364 cached_xdg_cache_path = path_xdg_cache_get();
365 }
366 const string result = path_join(cached_xdg_cache_path, "cycles");
367 return path_join(result, sub);
368#else
369 /* TODO(sergey): What that should be on Windows? */
370 return path_user_get(path_join("cache", sub));
371#endif
372}
373
374#if defined(__linux__) || defined(__APPLE__)
375string path_xdg_home_get(const string &sub = "");
376#endif
377
378string path_filename(const string &path)
379{
380 const size_t index = find_last_slash(path);
381 if (index != string::npos) {
382 /* Corner cases to match boost behavior. */
383#ifndef _WIN32
384 if (index == 0 && path.size() == 1) {
385 return path;
386 }
387#endif
388 if (index == path.size() - 1) {
389#ifdef _WIN32
390 if (index == 2) {
391 return string(1, DIR_SEP);
392 }
393#endif
394 return ".";
395 }
396 return path.substr(index + 1, path.size() - index - 1);
397 }
398 return path;
399}
400
401string path_dirname(const string &path)
402{
403 const size_t index = find_last_slash(path);
404 if (index != string::npos) {
405#ifndef _WIN32
406 if (index == 0 && path.size() > 1) {
407 return string(1, DIR_SEP);
408 }
409#endif
410 return path.substr(0, index);
411 }
412 return "";
413}
414
415string path_join(const string &dir, const string &file)
416{
417 if (dir.empty()) {
418 return file;
419 }
420 if (file.empty()) {
421 return dir;
422 }
423 string result = dir;
424#ifndef _WIN32
425 if (result[result.size() - 1] != DIR_SEP && file[0] != DIR_SEP)
426#else
427 if (result[result.size() - 1] != DIR_SEP && result[result.size() - 1] != DIR_SEP_ALT &&
428 file[0] != DIR_SEP && file[0] != DIR_SEP_ALT)
429#endif
430 {
431 result += DIR_SEP;
432 }
433 result += file;
434 return result;
435}
436
437string path_escape(const string &path)
438{
439 string result = path;
440 string_replace(result, " ", "\\ ");
441 return result;
442}
443
444bool path_is_relative(const string &path)
445{
446#ifdef _WIN32
447# ifdef HAVE_SHLWAPI_H
448 return PathIsRelative(path.c_str());
449# else /* HAVE_SHLWAPI_H */
450 if (path.size() >= 3) {
451 return !(((path[0] >= 'a' && path[0] <= 'z') || (path[0] >= 'A' && path[0] <= 'Z')) &&
452 path[1] == ':' && path[2] == DIR_SEP);
453 }
454 return true;
455# endif /* HAVE_SHLWAPI_H */
456#else /* _WIN32 */
457 if (path.empty()) {
458 return true;
459 }
460 return path[0] != DIR_SEP;
461#endif /* _WIN32 */
462}
463
464#ifdef _WIN32
465/* Add a slash if the UNC path points to a share. */
466static string path_unc_add_slash_to_share(const string &path)
467{
468 size_t slash_after_server = path.find(DIR_SEP, 2);
469 if (slash_after_server != string::npos) {
470 size_t slash_after_share = path.find(DIR_SEP, slash_after_server + 1);
471 if (slash_after_share == string::npos) {
472 return path + DIR_SEP;
473 }
474 }
475 return path;
476}
477
478/* Convert:
479 * \\?\UNC\server\share\folder\... to \\server\share\folder\...
480 * \\?\C:\ to C:\ and \\?\C:\folder\... to C:\folder\...
481 */
482static string path_unc_to_short(const string &path)
483{
484 size_t len = path.size();
485 if ((len > 3) && (path[0] == DIR_SEP) && (path[1] == DIR_SEP) && (path[2] == '?') &&
486 ((path[3] == DIR_SEP) || (path[3] == DIR_SEP_ALT)))
487 {
488 if ((len > 5) && (path[5] == ':')) {
489 return path.substr(4, len - 4);
490 }
491 else if ((len > 7) && (path.substr(4, 3) == "UNC") &&
492 ((path[7] == DIR_SEP) || (path[7] == DIR_SEP_ALT)))
493 {
494 return "\\\\" + path.substr(8, len - 8);
495 }
496 }
497 return path;
498}
499
500static string path_cleanup_unc(const string &path)
501{
502 string result = path_unc_to_short(path);
503 if (path.size() > 2) {
504 /* It's possible path is now a non-UNC. */
505 if (result[0] == DIR_SEP && result[1] == DIR_SEP) {
506 return path_unc_add_slash_to_share(result);
507 }
508 }
509 return result;
510}
511
512/* Make path compatible for stat() functions. */
513static string path_make_compatible(const string &path)
514{
515 string result = path;
516 /* In Windows stat() doesn't recognize dir ending on a slash. */
517 if (result.size() > 3 && result[result.size() - 1] == DIR_SEP) {
518 result.resize(result.size() - 1);
519 }
520 /* Clean up UNC path. */
521 if ((path.size() >= 3) && (path[0] == DIR_SEP) && (path[1] == DIR_SEP)) {
522 result = path_cleanup_unc(result);
523 }
524 /* Make sure volume-only path ends up wit a directory separator. */
525 if (result.size() == 2 && result[1] == ':') {
526 result += DIR_SEP;
527 }
528 return result;
529}
530
531static int path_wstat(const wstring &path_wc, path_stat_t *st)
532{
533# if defined(_MSC_VER) || defined(__MINGW64__)
534 return _wstat64(path_wc.c_str(), st);
535# elif defined(__MINGW32__)
536 return _wstati64(path_wc.c_str(), st);
537# else
538 return _wstat(path_wc.c_str(), st);
539# endif
540}
541
542static int path_stat(const string &path, path_stat_t *st)
543{
544 wstring path_wc = string_to_wstring(path);
545 return path_wstat(path_wc, st);
546}
547#else /* _WIN32 */
548static int path_stat(const string &path, path_stat_t *st)
549{
550 return stat(path.c_str(), st);
551}
552#endif /* _WIN32 */
553
554size_t path_file_size(const string &path)
555{
556 path_stat_t st;
557 if (path_stat(path, &st) != 0) {
558 return -1;
559 }
560 return st.st_size;
561}
562
563bool path_exists(const string &path)
564{
565#ifdef _WIN32
566 string fixed_path = path_make_compatible(path);
567 wstring path_wc = string_to_wstring(fixed_path);
568 path_stat_t st;
569 if (path_wstat(path_wc, &st) != 0) {
570 return false;
571 }
572 return st.st_mode != 0;
573#else /* _WIN32 */
574 struct stat st;
575 if (stat(path.c_str(), &st) != 0) {
576 return false;
577 }
578 return st.st_mode != 0;
579#endif /* _WIN32 */
580}
581
582bool path_is_directory(const string &path)
583{
584 path_stat_t st;
585 if (path_stat(path, &st) != 0) {
586 return false;
587 }
588 return S_ISDIR(st.st_mode);
589}
590
591static void path_files_md5_hash_recursive(MD5Hash &hash, const string &dir)
592{
593 if (path_exists(dir)) {
594 directory_iterator it(dir);
595 const directory_iterator it_end;
596
597 for (; it != it_end; ++it) {
598 if (path_is_directory(it->path())) {
600 }
601 else {
602 const string filepath = it->path();
603
604 hash.append((const uint8_t *)filepath.c_str(), filepath.size());
605 hash.append_file(filepath);
606 }
607 }
608 }
609}
610
611string path_files_md5_hash(const string &dir)
612{
613 /* computes md5 hash of all files in the directory */
615
617
618 return hash.get_hex();
619}
620
621static bool create_directories_recursivey(const string &path)
622{
623 if (path_exists(path)) {
624 /* Either directory exists and there is nothing to do, or it's a file and we fail. */
625 return path_is_directory(path);
626 }
627
628 const string parent = path_dirname(path);
629 if (!parent.empty() && parent != path) {
630 if (!create_directories_recursivey(parent)) {
631 return false;
632 }
633 }
634
635#ifdef _WIN32
636 wstring path_wc = string_to_wstring(path);
637 _wmkdir(path_wc.c_str());
638#else
639 mkdir(path.c_str(), 0777);
640#endif
641
642 /* If another thread creates this in the meantime mkdir will return an error,
643 * so instead check if the directory exists. */
644 return path_is_directory(path);
645}
646
647void path_create_directories(const string &filepath)
648{
649 const string path = path_dirname(filepath);
651}
652
653bool path_write_binary(const string &path, const vector<uint8_t> &binary)
654{
656
657 /* write binary file from memory */
658 FILE *f = path_fopen(path, "wb");
659
660 if (!f) {
661 return false;
662 }
663
664 if (!binary.empty()) {
665 fwrite(binary.data(), sizeof(uint8_t), binary.size(), f);
666 }
667
668 fclose(f);
669
670 return true;
671}
672
673bool path_write_text(const string &path, string &text)
674{
675 vector<uint8_t> binary(text.length(), 0);
676 std::copy(text.begin(), text.end(), binary.begin());
677
678 return path_write_binary(path, binary);
679}
680
681bool path_read_binary(const string &path, vector<uint8_t> &binary)
682{
683 /* read binary file into memory */
684 FILE *f = path_fopen(path, "rb");
685
686 if (!f) {
687 binary.resize(0);
688 return false;
689 }
690
691 binary.resize(path_file_size(path));
692
693 if (binary.empty()) {
694 fclose(f);
695 return false;
696 }
697
698 if (fread(binary.data(), sizeof(uint8_t), binary.size(), f) != binary.size()) {
699 fclose(f);
700 return false;
701 }
702
703 fclose(f);
704
705 return true;
706}
707
708bool path_read_compressed_binary(const string &path, vector<uint8_t> &binary)
709{
710 if (!string_endswith(path, ".zst")) {
711 return path_read_binary(path, binary);
712 }
713
714 vector<uint8_t> compressed;
715 if (!path_read_binary(path, compressed)) {
716 return false;
717 }
718
719 const size_t full_size = ZSTD_getFrameContentSize(compressed.data(), compressed.size());
720
721 if (full_size == ZSTD_CONTENTSIZE_ERROR) {
722 /* Potentially corrupted file? */
723 return false;
724 }
725 if (full_size == ZSTD_CONTENTSIZE_UNKNOWN) {
726 /* Technically this is an optional field, but we can expect it to be set for now.
727 * Otherwise we'd need streaming decompression and repeated resizing of the vector. */
728 return false;
729 }
730
731 binary.resize(full_size);
732
733 const size_t err = ZSTD_decompress(
734 binary.data(), binary.size(), compressed.data(), compressed.size());
735
736 return ZSTD_isError(err) == 0;
737}
738
739bool path_read_text(const string &path, string &text)
740{
741 vector<uint8_t> binary;
742
743 if (!path_exists(path) || !path_read_binary(path, binary)) {
744 return false;
745 }
746
747 const char *str = (const char *)binary.data();
748 const size_t size = binary.size();
749 text = string(str, size);
750
751 return true;
752}
753
754bool path_read_compressed_text(const string &path, string &text)
755{
756 vector<uint8_t> binary;
757
758 if (!path_exists(path) || !path_read_compressed_binary(path, binary)) {
759 return false;
760 }
761
762 const char *str = (const char *)binary.data();
763 const size_t size = binary.size();
764 text = string(str, size);
765
766 return true;
767}
768
769uint64_t path_modified_time(const string &path)
770{
771 path_stat_t st;
772 if (path_stat(path, &st) != 0) {
773 return 0;
774 }
775 return st.st_mtime;
776}
777
778bool path_remove(const string &path)
779{
780 return remove(path.c_str()) == 0;
781}
782
784 using ProcessedMapping = map<string, string>;
785 /* Base director for all relative include headers. */
786 string base;
787 /* Result of processed files. */
789 /* Set of files containing #pragma once which have been included. */
790 set<string> pragma_onced;
791};
792
793static string path_source_replace_includes_recursive(const string &source,
794 const string &source_filepath,
796
798 const string &path,
799 const size_t line_number)
800{
801 string unescaped_path = path;
802 /* First we make path relative. */
803 if (string_startswith(unescaped_path, state.base)) {
804 const string base_file = path_filename(state.base);
805 const size_t base_len = state.base.length();
806 unescaped_path = base_file +
807 unescaped_path.substr(base_len, unescaped_path.length() - base_len);
808 }
809 /* Second, we replace all unsafe characters. */
810 const size_t length = unescaped_path.length();
811 string escaped_path;
812 for (size_t i = 0; i < length; ++i) {
813 const char ch = unescaped_path[i];
814 if (strchr("\"\'\?\\", ch) != nullptr) {
815 escaped_path += "\\";
816 }
817 escaped_path += ch;
818 }
819 return "#line " + std::to_string(line_number) + '"' + escaped_path + '"';
820}
821
822static string path_source_handle_preprocessor(const string &preprocessor_line,
823 const string &source_filepath,
824 const size_t line_number,
826{
827 string result = preprocessor_line;
828
829 string rest_of_line = string_strip(preprocessor_line.substr(1));
830
831 if (0 == strncmp(rest_of_line.c_str(), "include", 7)) {
832 rest_of_line = string_strip(rest_of_line.substr(8));
833 if (rest_of_line[0] == '"') {
834 const size_t n_start = 1;
835 const size_t n_end = rest_of_line.find("\"", n_start);
836 const string filename = rest_of_line.substr(n_start, n_end - n_start);
837
838 string filepath = path_join(state->base, filename);
839 if (!path_exists(filepath)) {
840 filepath = path_join(path_dirname(source_filepath), filename);
841 }
842 string text;
843 if (path_read_text(filepath, text)) {
844 text = path_source_replace_includes_recursive(text, filepath, state);
845 /* Use line directives for better error messages. */
846 result = line_directive(*state, filepath, 1) + "\n" + text + "\n" +
847 line_directive(*state, source_filepath, line_number + 1);
848 }
849 }
850 }
851
852 return result;
853}
854
855/* Our own little c preprocessor that replaces #includes with the file
856 * contents, to work around issue of OpenCL drivers not supporting
857 * include paths with spaces in them.
858 */
859static string path_source_replace_includes_recursive(const string &_source,
860 const string &source_filepath,
862{
863 const string *psource = &_source;
864 string source_new;
865
866 auto pragma_once = _source.find("#pragma once");
867 if (pragma_once != string::npos) {
868 if (state->pragma_onced.find(source_filepath) != state->pragma_onced.end()) {
869 return "";
870 }
871 state->pragma_onced.insert(source_filepath);
872
873 // "#pragma once"
874 // "//prgma once"
875 source_new = _source;
876 memcpy(source_new.data() + pragma_once, "//pr", 4);
877 psource = &source_new;
878 }
879
880 /* Try to re-use processed file without spending time on replacing all
881 * include directives again.
882 */
883 const SourceReplaceState::ProcessedMapping::iterator replaced_file = state->processed_files.find(
884 source_filepath);
885 if (replaced_file != state->processed_files.end()) {
886 return replaced_file->second;
887 }
888
889 const string &source = *psource;
890
891 /* Perform full file processing. */
892 string result;
893 const size_t source_length = source.length();
894 size_t index = 0;
895 /* Information about where we are in the source. */
896 size_t line_number = 0;
897 size_t column_number = 1;
898 /* Currently gathered non-preprocessor token.
899 * Store as start/length rather than token itself to avoid overhead of
900 * memory re-allocations on each character concatenation.
901 */
902 size_t token_start = 0;
903 size_t token_length = 0;
904 /* Denotes whether we're inside of preprocessor line, together with
905 * preprocessor line itself.
906 *
907 * TODO(sergey): Investigate whether using token start/end position
908 * gives measurable speedup.
909 */
910 bool inside_preprocessor = false;
911 string preprocessor_line;
912 /* Actual loop over the whole source. */
913 while (index < source_length) {
914 const char ch = source[index];
915
916 if (ch == '\n') {
917 if (inside_preprocessor) {
918 const string block = path_source_handle_preprocessor(
919 preprocessor_line, source_filepath, line_number, state);
920
921 if (!block.empty()) {
922 result += block;
923 }
924
925 /* Start gathering net part of the token. */
926 token_start = index;
927 token_length = 0;
928 inside_preprocessor = false;
929 preprocessor_line = "";
930 }
931 column_number = 0;
932 ++line_number;
933 }
934 else if (ch == '#' && column_number == 1 && !inside_preprocessor) {
935 /* Append all possible non-preprocessor token to the result. */
936 if (token_length != 0) {
937 result.append(source, token_start, token_length);
938 token_start = index;
939 token_length = 0;
940 }
941 inside_preprocessor = true;
942 }
943
944 if (inside_preprocessor) {
945 preprocessor_line += ch;
946 }
947 else {
948 ++token_length;
949 }
950 ++index;
951 ++column_number;
952 }
953 /* Append possible tokens which happened before special events handled
954 * above.
955 */
956 if (token_length != 0) {
957 result.append(source, token_start, token_length);
958 }
959 if (inside_preprocessor) {
961 preprocessor_line, source_filepath, line_number, state);
962 }
963 /* Store result for further reuse. */
964 state->processed_files[source_filepath] = result;
965 return result;
966}
967
968string path_source_replace_includes(const string &source, const string &path)
969{
971 state.base = path;
972 return path_source_replace_includes_recursive(source, path, &state);
973}
974
975FILE *path_fopen(const string &path, const string &mode)
976{
977#ifdef _WIN32
978 wstring path_wc = string_to_wstring(path);
979 wstring mode_wc = string_to_wstring(mode);
980 return _wfopen(path_wc.c_str(), mode_wc.c_str());
981#else
982 return fopen(path.c_str(), mode.c_str());
983#endif
984}
985
986/* LRU Cache for Kernels */
987
988static void path_cache_kernel_mark_used(const string &path)
989{
990 const std::time_t current_time = std::time(nullptr);
991 OIIO::Filesystem::last_write_time(path, current_time);
992}
993
995{
996 if (path_exists(path)) {
998 return true;
999 }
1000 return false;
1001}
1002
1004 const size_t max_old_kernel_of_same_type)
1005{
1007
1008 const string dir = path_dirname(new_path);
1009 if (!path_exists(dir)) {
1010 return;
1011 }
1012
1013 /* Remove older kernels within the same directory. */
1014 directory_iterator it(dir);
1015 const directory_iterator it_end;
1016 vector<pair<std::time_t, string>> same_kernel_types;
1017
1018 for (; it != it_end; ++it) {
1019 const string &path = it->path();
1020 if (path == new_path) {
1021 continue;
1022 }
1023
1024 const std::time_t last_time = OIIO::Filesystem::last_write_time(path);
1025 same_kernel_types.emplace_back(last_time, path);
1026 }
1027
1028 if (same_kernel_types.size() > max_old_kernel_of_same_type) {
1029 sort(same_kernel_types.begin(), same_kernel_types.end());
1030
1031 for (int i = 0; i < same_kernel_types.size() - max_old_kernel_of_same_type; i++) {
1032 path_remove(same_kernel_types[i].second);
1033 }
1034 }
1035}
1036
void BLI_kdtree_nd_ free(KDTree *tree)
BLI_INLINE bool operator!=(const ListBase &a, const ListBase &b)
#define S_ISDIR(x)
unsigned long long int uint64_t
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static DBVT_INLINE btDbvtNode * sort(btDbvtNode *n, btDbvtNode *&r)
Definition btDbvt.cpp:418
Definition md5.h:19
#define CCL_NAMESPACE_END
#define str(s)
GPUAttachmentType & operator++(GPUAttachmentType &a)
VecBase< float, D > step(VecOp< float, D >, VecOp< float, D >) RET
float length(VecOp< float, D >) RET
static ulong state[N]
#define L
#define hash
Definition noise_c.cc:154
size_t path_file_size(const string &path)
Definition path.cpp:554
bool path_read_compressed_binary(const string &path, vector< uint8_t > &binary)
Definition path.cpp:708
static string path_source_replace_includes_recursive(const string &source, const string &source_filepath, SourceReplaceState *state)
Definition path.cpp:859
static string cached_user_path
Definition path.cpp:61
string path_user_get(const string &sub)
Definition path.cpp:351
string path_cache_get(const string &sub)
Definition path.cpp:360
bool path_is_directory(const string &path)
Definition path.cpp:582
FILE * path_fopen(const string &path, const string &mode)
Definition path.cpp:975
string path_dirname(const string &path)
Definition path.cpp:401
string path_source_replace_includes(const string &source, const string &path)
Definition path.cpp:968
static string path_source_handle_preprocessor(const string &preprocessor_line, const string &source_filepath, const size_t line_number, SourceReplaceState *state)
Definition path.cpp:822
static int path_stat(const string &path, path_stat_t *st)
Definition path.cpp:548
string path_get(const string &sub)
Definition path.cpp:337
string path_files_md5_hash(const string &dir)
Definition path.cpp:611
bool path_is_relative(const string &path)
Definition path.cpp:444
uint64_t path_modified_time(const string &path)
Definition path.cpp:769
string path_join(const string &dir, const string &file)
Definition path.cpp:415
#define DIR_SEP
Definition path.cpp:28
static void path_cache_kernel_mark_used(const string &path)
Definition path.cpp:988
bool path_exists(const string &path)
Definition path.cpp:563
static string line_directive(const SourceReplaceState &state, const string &path, const size_t line_number)
Definition path.cpp:797
bool path_cache_kernel_exists_and_mark_used(const string &path)
Definition path.cpp:994
static bool create_directories_recursivey(const string &path)
Definition path.cpp:621
string path_escape(const string &path)
Definition path.cpp:437
void path_cache_kernel_mark_added_and_clear_old(const string &new_path, const size_t max_old_kernel_of_same_type)
Definition path.cpp:1003
bool path_write_binary(const string &path, const vector< uint8_t > &binary)
Definition path.cpp:653
void path_create_directories(const string &filepath)
Definition path.cpp:647
bool path_write_text(const string &path, string &text)
Definition path.cpp:673
static void path_files_md5_hash_recursive(MD5Hash &hash, const string &dir)
Definition path.cpp:591
bool path_read_text(const string &path, string &text)
Definition path.cpp:739
static string cached_xdg_cache_path
Definition path.cpp:62
bool path_read_compressed_text(const string &path, string &text)
Definition path.cpp:754
string path_filename(const string &path)
Definition path.cpp:378
struct stat path_stat_t
Definition path.cpp:57
static char * path_specials(const string &sub)
Definition path.cpp:289
static string cached_path
Definition path.cpp:60
bool path_remove(const string &path)
Definition path.cpp:778
void path_init(const string &path, const string &user_path)
Definition path.cpp:324
bool path_read_binary(const string &path, vector< uint8_t > &binary)
Definition path.cpp:681
string string_strip(const string &s)
Definition string.cpp:137
bool string_startswith(const string_view s, const string_view start)
Definition string.cpp:104
void string_replace(string &haystack, const string &needle, const string &other)
Definition string.cpp:145
bool string_endswith(const string_view s, const string_view end)
Definition string.cpp:120
map< string, string > ProcessedMapping
Definition path.cpp:784
ProcessedMapping processed_files
Definition path.cpp:788
set< string > pragma_onced
Definition path.cpp:790
i
Definition text_draw.cc:230
uint len