Blender V4.3
appdir.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
11#include <cstdio>
12#include <cstdlib>
13#include <cstring>
14
15#include "BLI_fileops.h"
16#include "BLI_fileops_types.h"
17#include "BLI_listbase.h"
18#include "BLI_path_utils.hh"
19#include "BLI_string.h"
20#include "BLI_string_utf8.h"
21#include "BLI_string_utils.hh"
22#include "BLI_tempfile.h"
23#include "BLI_utildefines.h"
24#include "BLI_vector.hh"
25
26#include "BKE_appdir.hh" /* own include */
27#include "BKE_blender_version.h"
28
29#include "BLT_translation.hh"
30
31#include "GHOST_Path-api.hh"
32
33#include "MEM_guardedalloc.h"
34
35#include "CLG_log.h"
36
37#ifdef WIN32
38# include "utf_winfunc.hh"
39# include "utfconv.hh"
40# include <io.h>
41# ifdef _WIN32_IE
42# undef _WIN32_IE
43# endif
44# define _WIN32_IE 0x0501
45# include "BLI_winstuff.h"
46# include <shlobj.h>
47# include <windows.h>
48#else /* non windows */
49# ifdef WITH_BINRELOC
50# include "binreloc.h"
51# endif
52/* #mkdtemp on OSX (and probably all *BSD?), not worth making specific check for this OS. */
53# include <unistd.h>
54
55# include <pwd.h> /* For `passwd` access. */
56
57#endif /* !WIN32 */
58
59static const char _str_null[] = "(null)";
60#define STR_OR_FALLBACK(a) ((a) ? (a) : _str_null)
61
62/* -------------------------------------------------------------------- */
66/* local */
67static CLG_LogRef LOG = {"bke.appdir"};
68
69static struct {
79
82/* -------------------------------------------------------------------- */
86#ifndef NDEBUG
87static bool is_appdir_init = false;
88# define ASSERT_IS_INIT() BLI_assert(is_appdir_init)
89#else
90# define ASSERT_IS_INIT() ((void)0)
91#endif /* NDEBUG */
92
94{
95#ifndef NDEBUG
96 BLI_assert(is_appdir_init == false);
97 is_appdir_init = true;
98#endif
99}
100
102{
103 /* System paths can be created on-demand by calls to this API. So they need to be properly
104 * disposed of here. Note that there may be several calls to this in `exit` process
105 * (e.g. `wm_init/wm_exit` will currently both call GHOST API directly,
106 * & `BKE_appdir_init/_exit`). */
108#ifndef NDEBUG
109 BLI_assert(is_appdir_init == true);
110 is_appdir_init = false;
111#endif
112}
113
116/* -------------------------------------------------------------------- */
123static char *blender_version_decimal(const int version)
124{
125 static char version_str[5];
126 BLI_assert(version < 1000);
127 SNPRINTF(version_str, "%d.%d", version / 100, version % 100);
128 return version_str;
129}
130
133/* -------------------------------------------------------------------- */
138{
139#ifndef WIN32
140 return BKE_appdir_folder_home();
141#else /* Windows */
142 static char documentfolder[FILE_MAXDIR];
143
144 if (BKE_appdir_folder_documents(documentfolder)) {
145 return documentfolder;
146 }
147
148 return nullptr;
149#endif /* WIN32 */
150}
151
153{
154#ifndef WIN32
155 return "/";
156#else
157 static char root[4];
159 return root;
160#endif
161}
162
164{
165 const char *path = BKE_appdir_folder_default();
166 if (path == nullptr) {
167 path = BKE_appdir_folder_root();
168 }
169 return path;
170}
171
173{
174 const char *home_dir = nullptr;
175
176#ifdef WIN32
177 home_dir = BLI_getenv("userprofile");
178#else
179
180# if defined(__APPLE__)
181 home_dir = BLI_expand_tilde("~/");
182# endif
183 if (home_dir == nullptr) {
184 home_dir = BLI_getenv("HOME");
185 if (home_dir == nullptr) {
186 if (const passwd *pwuser = getpwuid(getuid())) {
187 home_dir = pwuser->pw_dir;
188 }
189 }
190 }
191#endif
192
193 return home_dir;
194}
195
197{
198 dir[0] = '\0';
199
200 const char *documents_path = GHOST_getUserSpecialDir(GHOST_kUserSpecialDirDocuments);
201
202 /* Usual case: Ghost gave us the documents path. We're done here. */
203 if (documents_path && BLI_is_dir(documents_path)) {
204 BLI_strncpy(dir, documents_path, FILE_MAXDIR);
205 return true;
206 }
207
208 /* Ghost couldn't give us a documents path, let's try if we can find it ourselves. */
209
210 const char *home_path = BKE_appdir_folder_home();
211 if (!home_path || !BLI_is_dir(home_path)) {
212 return false;
213 }
214
215 char try_documents_path[FILE_MAXDIR];
216 /* Own attempt at getting a valid Documents path. */
217 BLI_path_join(try_documents_path, sizeof(try_documents_path), home_path, N_("Documents"));
218 if (!BLI_is_dir(try_documents_path)) {
219 return false;
220 }
221
222 BLI_strncpy(dir, try_documents_path, FILE_MAXDIR);
223 return true;
224}
225
226bool BKE_appdir_folder_caches(char *path, const size_t path_maxncpy)
227{
228 path[0] = '\0';
229
230 const char *caches_root_path = GHOST_getUserSpecialDir(GHOST_kUserSpecialDirCaches);
231 if (caches_root_path == nullptr || !BLI_is_dir(caches_root_path)) {
232 caches_root_path = BKE_tempdir_base();
233 }
234 if (caches_root_path == nullptr || !BLI_is_dir(caches_root_path)) {
235 return false;
236 }
237
238#ifdef WIN32
240 path, path_maxncpy, caches_root_path, "Blender Foundation", "Blender", "Cache", SEP_STR);
241#elif defined(__APPLE__)
242 BLI_path_join(path, path_maxncpy, caches_root_path, "Blender", SEP_STR);
243#else /* __linux__ */
244 BLI_path_join(path, path_maxncpy, caches_root_path, "blender", SEP_STR);
245#endif
246
247 return true;
248}
249
250bool BKE_appdir_font_folder_default(char *dir, size_t dir_maxncpy)
251{
252 char test_dir[FILE_MAXDIR];
253 test_dir[0] = '\0';
254
255#ifdef WIN32
256 wchar_t wpath[MAX_PATH];
257 if (SHGetSpecialFolderPathW(0, wpath, CSIDL_FONTS, 0)) {
258 BLI_strncpy_wchar_as_utf8(test_dir, wpath, sizeof(test_dir));
259 }
260#elif defined(__APPLE__)
261 if (const char *fonts_dir = BLI_expand_tilde("~/Library/Fonts")) {
262 STRNCPY(test_dir, fonts_dir);
263 }
264#else
265 STRNCPY(test_dir, "/usr/share/fonts");
266#endif
267
268 if (test_dir[0] && BLI_exists(test_dir)) {
269 BLI_strncpy(dir, test_dir, dir_maxncpy);
270 return true;
271 }
272 return false;
273}
274
277/* -------------------------------------------------------------------- */
296static bool test_path(char *targetpath,
297 size_t targetpath_maxncpy,
298 const bool check_is_dir,
299 const char *path_base,
300 const char *folder_name,
301 const char *subfolder_name)
302{
304
305 /* Only the last argument should be nullptr. */
306 BLI_assert(!(folder_name == nullptr && (subfolder_name != nullptr)));
307 const char *path_array[] = {path_base, folder_name, subfolder_name};
308 const int path_array_num = (folder_name ? (subfolder_name ? 3 : 2) : 1);
309 BLI_path_join_array(targetpath, targetpath_maxncpy, path_array, path_array_num);
310 if (check_is_dir == false) {
311 CLOG_INFO(&LOG, 3, "using without test: '%s'", targetpath);
312 return true;
313 }
314
315 if (BLI_is_dir(targetpath)) {
316 CLOG_INFO(&LOG, 3, "found '%s'", targetpath);
317 return true;
318 }
319
320 CLOG_INFO(&LOG, 3, "missing '%s'", targetpath);
321
322 /* Path not found, don't accidentally use it,
323 * otherwise call this function with `check_is_dir` set to false. */
324 targetpath[0] = '\0';
325 return false;
326}
327
336static bool test_env_path(char *path, const char *envvar, const bool check_is_dir)
337{
339
340 const char *env_path = envvar ? BLI_getenv(envvar) : nullptr;
341 if (!env_path) {
342 return false;
343 }
344
345 BLI_strncpy(path, env_path, FILE_MAX);
346
347 if (check_is_dir == false) {
348 CLOG_INFO(&LOG, 3, "using env '%s' without test: '%s'", envvar, env_path);
349 return true;
350 }
351
352 if (BLI_is_dir(env_path)) {
353 CLOG_INFO(&LOG, 3, "env '%s' found: %s", envvar, env_path);
354 return true;
355 }
356
357 CLOG_INFO(&LOG, 3, "env '%s' missing: %s", envvar, env_path);
358
359 /* Path not found, don't accidentally use it,
360 * otherwise call this function with `check_is_dir` set to false. */
361 path[0] = '\0';
362 return false;
363}
364
377static bool get_path_local_ex(char *targetpath,
378 size_t targetpath_maxncpy,
379 const char *folder_name,
380 const char *subfolder_name,
381 const int version,
382 const bool check_is_dir)
383{
384 char relfolder[FILE_MAX];
385
386 CLOG_INFO(&LOG,
387 3,
388 "folder='%s', subfolder='%s'",
389 STR_OR_FALLBACK(folder_name),
390 STR_OR_FALLBACK(subfolder_name));
391
392 if (folder_name) { /* `subfolder_name` may be nullptr. */
393 const char *path_array[] = {folder_name, subfolder_name};
394 const int path_array_num = subfolder_name ? 2 : 1;
395 BLI_path_join_array(relfolder, sizeof(relfolder), path_array, path_array_num);
396 }
397 else {
398 relfolder[0] = '\0';
399 }
400
401 /* Try `{g_app.program_dirname}/3.xx/{folder_name}` the default directory
402 * for a portable distribution. See `WITH_INSTALL_PORTABLE` build-option. */
403 const char *path_base = g_app.program_dirname;
404#if defined(__APPLE__) && !defined(WITH_PYTHON_MODULE)
405 /* Due new code-sign situation in OSX > 10.9.5
406 * we must move the blender_version dir with contents to Resources.
407 * Add 4 + 9 for the temporary `/../` path & `Resources`. */
408 char osx_resourses[FILE_MAX + 4 + 9];
409 BLI_path_join(osx_resourses, sizeof(osx_resourses), g_app.program_dirname, "..", "Resources");
410 /* Remove the '/../' added above. */
411 BLI_path_normalize_native(osx_resourses);
412 path_base = osx_resourses;
413#endif
414 return test_path(targetpath,
415 targetpath_maxncpy,
416 check_is_dir,
417 path_base,
418 (version) ? blender_version_decimal(version) : relfolder,
419 (version) ? relfolder : nullptr);
420}
421static bool get_path_local(char *targetpath,
422 size_t targetpath_maxncpy,
423 const char *folder_name,
424 const char *subfolder_name)
425{
426 const int version = BLENDER_VERSION;
427 const bool check_is_dir = true;
428 return get_path_local_ex(
429 targetpath, targetpath_maxncpy, folder_name, subfolder_name, version, check_is_dir);
430}
431
441static bool get_path_environment_ex(char *targetpath,
442 size_t targetpath_maxncpy,
443 const char *subfolder_name,
444 const char *envvar,
445 const bool check_is_dir)
446{
447 char user_path[FILE_MAX];
448
449 if (test_env_path(user_path, envvar, check_is_dir)) {
450 /* Note that `subfolder_name` may be nullptr, in this case we use `user_path` as-is. */
451 return test_path(
452 targetpath, targetpath_maxncpy, check_is_dir, user_path, subfolder_name, nullptr);
453 }
454 return false;
455}
456static bool get_path_environment(char *targetpath,
457 size_t targetpath_maxncpy,
458 const char *subfolder_name,
459 const char *envvar)
460{
461 const bool check_is_dir = true;
463 targetpath, targetpath_maxncpy, subfolder_name, envvar, check_is_dir);
464}
465
476static bool get_path_user_ex(char *targetpath,
477 size_t targetpath_maxncpy,
478 const char *folder_name,
479 const char *subfolder_name,
480 const int version,
481 const bool check_is_dir)
482{
483 char user_path[FILE_MAX];
484
485 /* Environment variable override. */
486 if (test_env_path(user_path, "BLENDER_USER_RESOURCES", check_is_dir)) {
487 /* Pass. */
488 }
489 /* Portable install, to store user files next to Blender executable. */
490 else if (get_path_local_ex(user_path, sizeof(user_path), "portable", nullptr, 0, true)) {
491 /* Pass. */
492 }
493 else {
494 user_path[0] = '\0';
495
496 const char *user_base_path = GHOST_getUserDir(version, blender_version_decimal(version));
497 if (user_base_path) {
498 STRNCPY(user_path, user_base_path);
499 }
500 }
501
502 if (!user_path[0]) {
503 return false;
504 }
505
506 CLOG_INFO(&LOG,
507 3,
508 "'%s', folder='%s', subfolder='%s'",
509 user_path,
510 STR_OR_FALLBACK(folder_name),
511 STR_OR_FALLBACK(subfolder_name));
512
513 /* `subfolder_name` may be nullptr. */
514 return test_path(
515 targetpath, targetpath_maxncpy, check_is_dir, user_path, folder_name, subfolder_name);
516}
517static bool get_path_user(char *targetpath,
518 size_t targetpath_maxncpy,
519 const char *folder_name,
520 const char *subfolder_name)
521{
522 const int version = BLENDER_VERSION;
523 const bool check_is_dir = true;
524 return get_path_user_ex(
525 targetpath, targetpath_maxncpy, folder_name, subfolder_name, version, check_is_dir);
526}
527
538static bool get_path_system_ex(char *targetpath,
539 size_t targetpath_maxncpy,
540 const char *folder_name,
541 const char *subfolder_name,
542 const int version,
543 const bool check_is_dir)
544{
545 char system_path[FILE_MAX];
546
547 if (test_env_path(system_path, "BLENDER_SYSTEM_RESOURCES", check_is_dir)) {
548 /* Pass. */
549 }
550 else {
551 system_path[0] = '\0';
552 const char *system_base_path = GHOST_getSystemDir(version, blender_version_decimal(version));
553 if (system_base_path) {
554 STRNCPY(system_path, system_base_path);
555 }
556 }
557
558 if (!system_path[0]) {
559 return false;
560 }
561
562 CLOG_INFO(&LOG,
563 3,
564 "'%s', folder='%s', subfolder='%s'",
565 system_path,
566 STR_OR_FALLBACK(folder_name),
567 STR_OR_FALLBACK(subfolder_name));
568
569 /* Try `$BLENDERPATH/folder_name/subfolder_name`, `subfolder_name` may be nullptr. */
570 return test_path(
571 targetpath, targetpath_maxncpy, check_is_dir, system_path, folder_name, subfolder_name);
572}
573
574static bool get_path_system(char *targetpath,
575 size_t targetpath_maxncpy,
576 const char *folder_name,
577 const char *subfolder_name)
578{
579 const int version = BLENDER_VERSION;
580 const bool check_is_dir = true;
581 return get_path_system_ex(
582 targetpath, targetpath_maxncpy, folder_name, subfolder_name, version, check_is_dir);
583}
584
587/* -------------------------------------------------------------------- */
591bool BKE_appdir_folder_id_ex(const int folder_id,
592 const char *subfolder,
593 char *path,
594 size_t path_maxncpy)
595{
596 switch (folder_id) {
597 case BLENDER_DATAFILES: /* general case */
598 if (get_path_environment(path, path_maxncpy, subfolder, "BLENDER_USER_DATAFILES")) {
599 break;
600 }
601 if (get_path_user(path, path_maxncpy, "datafiles", subfolder)) {
602 break;
603 }
604 if (get_path_environment(path, path_maxncpy, subfolder, "BLENDER_SYSTEM_DATAFILES")) {
605 break;
606 }
607 if (get_path_system(path, path_maxncpy, "datafiles", subfolder)) {
608 break;
609 }
610 if (get_path_local(path, path_maxncpy, "datafiles", subfolder)) {
611 break;
612 }
613 return false;
614
616 if (get_path_environment(path, path_maxncpy, subfolder, "BLENDER_USER_DATAFILES")) {
617 break;
618 }
619 if (get_path_user(path, path_maxncpy, "datafiles", subfolder)) {
620 break;
621 }
622 return false;
623
625 if (get_path_environment(path, path_maxncpy, subfolder, "BLENDER_SYSTEM_DATAFILES")) {
626 break;
627 }
628 if (get_path_system(path, path_maxncpy, "datafiles", subfolder)) {
629 break;
630 }
631 if (get_path_local(path, path_maxncpy, "datafiles", subfolder)) {
632 break;
633 }
634 return false;
635
637 if (get_path_environment(path, path_maxncpy, subfolder, "BLENDER_USER_CONFIG")) {
638 break;
639 }
640 if (get_path_user(path, path_maxncpy, "config", subfolder)) {
641 break;
642 }
643 return false;
644
646 if (get_path_environment(path, path_maxncpy, subfolder, "BLENDER_USER_SCRIPTS")) {
647 break;
648 }
649 if (get_path_user(path, path_maxncpy, "scripts", subfolder)) {
650 break;
651 }
652 return false;
653
655 if (get_path_system(path, path_maxncpy, "scripts", subfolder)) {
656 break;
657 }
658 if (get_path_local(path, path_maxncpy, "scripts", subfolder)) {
659 break;
660 }
661 return false;
662
664 if (get_path_environment(path, path_maxncpy, subfolder, "BLENDER_USER_EXTENSIONS")) {
665 break;
666 }
667 if (get_path_user(path, path_maxncpy, "extensions", subfolder)) {
668 break;
669 }
670 return false;
671
673 if (get_path_environment(path, path_maxncpy, subfolder, "BLENDER_SYSTEM_EXTENSIONS")) {
674 break;
675 }
676 if (get_path_system(path, path_maxncpy, "extensions", subfolder)) {
677 break;
678 }
679 if (get_path_local(path, path_maxncpy, "extensions", subfolder)) {
680 break;
681 }
682 return false;
683
685 if (get_path_environment(path, path_maxncpy, subfolder, "BLENDER_SYSTEM_PYTHON")) {
686 break;
687 }
688 if (get_path_system(path, path_maxncpy, "python", subfolder)) {
689 break;
690 }
691 if (get_path_local(path, path_maxncpy, "python", subfolder)) {
692 break;
693 }
694 return false;
695
696 default:
698 break;
699 }
700
701 return true;
702}
703
704std::optional<std::string> BKE_appdir_folder_id(const int folder_id, const char *subfolder)
705{
706 char path[FILE_MAX] = "";
707 if (BKE_appdir_folder_id_ex(folder_id, subfolder, path, sizeof(path))) {
708 return path;
709 }
710 return std::nullopt;
711}
712
713std::optional<std::string> BKE_appdir_folder_id_user_notest(const int folder_id,
714 const char *subfolder)
715{
716 const int version = BLENDER_VERSION;
717 char path[FILE_MAX] = "";
718 const bool check_is_dir = false;
719
720 switch (folder_id) {
723 path, sizeof(path), subfolder, "BLENDER_USER_DATAFILES", check_is_dir))
724 {
725 break;
726 }
727 get_path_user_ex(path, sizeof(path), "datafiles", subfolder, version, check_is_dir);
728 break;
731 path, sizeof(path), subfolder, "BLENDER_USER_CONFIG", check_is_dir))
732 {
733 break;
734 }
735 get_path_user_ex(path, sizeof(path), "config", subfolder, version, check_is_dir);
736 break;
739 path, sizeof(path), subfolder, "BLENDER_USER_SCRIPTS", check_is_dir))
740 {
741 break;
742 }
743 get_path_user_ex(path, sizeof(path), "scripts", subfolder, version, check_is_dir);
744 break;
747 path, sizeof(path), subfolder, "BLENDER_USER_EXTENSIONS", check_is_dir))
748 {
749 break;
750 }
751 get_path_user_ex(path, sizeof(path), "extensions", subfolder, version, check_is_dir);
752 break;
753 default:
755 break;
756 }
757
758 if ('\0' == path[0]) {
759 return std::nullopt;
760 }
761 return path;
762}
763
764std::optional<std::string> BKE_appdir_folder_id_create(const int folder_id, const char *subfolder)
765{
766 /* Only for user folders. */
767 if (!ELEM(folder_id,
772 {
774 return std::nullopt;
775 }
776
777 std::optional<std::string> path = BKE_appdir_folder_id(folder_id, subfolder);
778
779 if (!path.has_value()) {
780 path = BKE_appdir_folder_id_user_notest(folder_id, subfolder);
781 if (path.has_value()) {
782 BLI_dir_create_recursive(path->c_str());
783 }
784 }
785
786 return path;
787}
788
789std::optional<std::string> BKE_appdir_resource_path_id_with_version(const int folder_id,
790 const bool check_is_dir,
791 const int version)
792{
793 char path[FILE_MAX] = "";
794 bool ok;
795 switch (folder_id) {
797 ok = get_path_user_ex(path, sizeof(path), nullptr, nullptr, version, check_is_dir);
798 break;
800 ok = get_path_local_ex(path, sizeof(path), nullptr, nullptr, version, check_is_dir);
801 break;
803 ok = get_path_system_ex(path, sizeof(path), nullptr, nullptr, version, check_is_dir);
804 break;
805 default:
806 path[0] = '\0'; /* in case check_is_dir is false */
807 ok = false;
808 BLI_assert_msg(0, "incorrect ID");
809 break;
810 }
811 if (!ok) {
812 return std::nullopt;
813 }
814 return path;
815}
816
817std::optional<std::string> BKE_appdir_resource_path_id(const int folder_id,
818 const bool check_is_dir)
819{
820 return BKE_appdir_resource_path_id_with_version(folder_id, check_is_dir, BLENDER_VERSION);
821}
822
825/* -------------------------------------------------------------------- */
831#ifndef WITH_PYTHON_MODULE
844static void where_am_i(char *program_filepath,
845 const size_t program_filepath_maxncpy,
846 const char *program_name)
847{
848# ifdef WITH_BINRELOC
849 /* Linux uses `binreloc` since `argv[0]` is not reliable, call `br_init(nullptr)` first. */
850 {
851 const char *path = nullptr;
852 path = br_find_exe(nullptr);
853 if (path) {
854 BLI_strncpy(program_filepath, path, program_filepath_maxncpy);
855 free((void *)path);
856 return;
857 }
858 }
859# endif
860
861# ifdef _WIN32
862 {
863 wchar_t *fullname_16 = static_cast<wchar_t *>(
864 MEM_mallocN(program_filepath_maxncpy * sizeof(wchar_t), "ProgramPath"));
865 if (GetModuleFileNameW(0, fullname_16, program_filepath_maxncpy)) {
866 conv_utf_16_to_8(fullname_16, program_filepath, program_filepath_maxncpy);
869 "path can't be found: \"%.*s\"",
870 int(program_filepath_maxncpy),
872 MessageBox(nullptr,
873 "path contains invalid characters or is too long (see console)",
874 "Error",
875 MB_OK);
876 }
877 MEM_freeN(fullname_16);
878 return;
879 }
880
881 MEM_freeN(fullname_16);
882 }
883# endif
884
885 /* Unix and non Linux. */
886 if (program_name && program_name[0]) {
887
888 BLI_strncpy(program_filepath, program_name, program_filepath_maxncpy);
889 if (program_name[0] == '.') {
890 BLI_path_abs_from_cwd(program_filepath, program_filepath_maxncpy);
891# ifdef _WIN32
892 BLI_path_program_extensions_add_win32(program_filepath, program_filepath_maxncpy);
893# endif
894 }
895 else if (BLI_path_slash_rfind(program_name)) {
896 /* Full path. */
897 BLI_strncpy(program_filepath, program_name, program_filepath_maxncpy);
898# ifdef _WIN32
899 BLI_path_program_extensions_add_win32(program_filepath, program_filepath_maxncpy);
900# endif
901 }
902 else {
903 BLI_path_program_search(program_filepath, program_filepath_maxncpy, program_name);
904 }
905 /* Remove "/./" and "/../" so string comparisons can be used on the path. */
907
908# ifndef NDEBUG
909 if (!STREQ(program_name, program_filepath)) {
910 CLOG_INFO(&LOG, 2, "guessing '%s' == '%s'", program_name, program_filepath);
911 }
912# endif
913 }
914}
915#endif /* WITH_PYTHON_MODULE */
916
917void BKE_appdir_program_path_init(const char *argv0)
918{
919#ifdef WITH_PYTHON_MODULE
920 /* NOTE(@ideasman42): Always use `argv[0]` as is, when building as a Python module.
921 * Otherwise other methods of detecting the binary that override this argument
922 * which must point to the Python module for data-files to be detected. */
923 STRNCPY(g_app.program_filepath, argv0);
924 BLI_path_canonicalize_native(g_app.program_filepath, sizeof(g_app.program_filepath));
925
926 if (g_app.program_dirname[0] == '\0') {
927 /* First time initializing, the file binary path isn't valid from a Python module.
928 * Calling again must set the `filepath` and leave the directory as-is. */
930 g_app.program_filepath, g_app.program_dirname, sizeof(g_app.program_dirname));
931 g_app.program_filepath[0] = '\0';
932 }
933#else
934 where_am_i(g_app.program_filepath, sizeof(g_app.program_filepath), argv0);
936 g_app.program_filepath, g_app.program_dirname, sizeof(g_app.program_dirname));
937#endif
938}
939
941{
942#ifndef WITH_PYTHON_MODULE /* Default's to empty when building as a Python module. */
943 BLI_assert(g_app.program_filepath[0]);
944#endif
945 return g_app.program_filepath;
946}
947
949{
950 BLI_assert(g_app.program_dirname[0]);
951 return g_app.program_dirname;
952}
953
955 const size_t program_filepath_maxncpy,
956 const int version_major,
957 const int version_minor)
958{
960
961#ifdef PYTHON_EXECUTABLE_NAME
962 /* Passed in from the build-systems 'PYTHON_EXECUTABLE'. */
963 const char *python_build_def = STRINGIFY(PYTHON_EXECUTABLE_NAME);
964#endif
965 const char *basename = "python";
966#if defined(WIN32) && !defined(NDEBUG)
967 const char *basename_debug = "python_d";
968#endif
969 char python_version[16];
970 /* Check both possible names. */
971 const char *python_names[] = {
972#ifdef PYTHON_EXECUTABLE_NAME
973 python_build_def,
974#endif
975#if defined(WIN32) && !defined(NDEBUG)
976 basename_debug,
977#endif
978 python_version,
979 basename,
980 };
981 bool is_found = false;
982
983 SNPRINTF(python_version, "%s%d.%d", basename, version_major, version_minor);
984
985 {
986 const std::optional<std::string> python_bin_dir = BKE_appdir_folder_id(BLENDER_SYSTEM_PYTHON,
987 "bin");
988 if (python_bin_dir.has_value()) {
989
990 for (int i = 0; i < ARRAY_SIZE(python_names); i++) {
992 program_filepath, program_filepath_maxncpy, python_bin_dir->c_str(), python_names[i]);
993
994 if (
995#ifdef _WIN32
996 BLI_path_program_extensions_add_win32(program_filepath, program_filepath_maxncpy)
997#else
999#endif
1000 )
1001 {
1002 is_found = true;
1003 break;
1004 }
1005 }
1006 }
1007 }
1008
1009 if (is_found == false) {
1010 for (int i = 0; i < ARRAY_SIZE(python_names); i++) {
1011 if (BLI_path_program_search(program_filepath, program_filepath_maxncpy, python_names[i])) {
1012 is_found = true;
1013 break;
1014 }
1015 }
1016 }
1017
1018 if (is_found == false) {
1019 *program_filepath = '\0';
1020 }
1021
1022 return is_found;
1023}
1024
1027/* -------------------------------------------------------------------- */
1032{
1033 blender::Vector<std::string> directories;
1034
1036 char temp_dir[FILE_MAX];
1038 "startup" SEP_STR "bl_app_templates_user",
1039 temp_dir,
1040 sizeof(temp_dir)))
1041 {
1042 directories.append(temp_dir);
1043 }
1044
1045 /* Environment variable. */
1046 if (get_path_environment(temp_dir,
1047 sizeof(temp_dir),
1048 "startup" SEP_STR "bl_app_templates_system",
1049 "BLENDER_SYSTEM_SCRIPTS"))
1050 {
1051 directories.append(temp_dir);
1052 }
1053
1054 /* Local or system directory. */
1056 "startup" SEP_STR "bl_app_templates_system",
1057 temp_dir,
1058 sizeof(temp_dir)))
1059 {
1060 directories.append(temp_dir);
1061 }
1062
1063 return directories;
1064}
1065
1070
1071bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_maxncpy)
1072{
1074
1075 for (const std::string &directory : directories) {
1076 BLI_path_join(path, path_maxncpy, directory.c_str(), app_template);
1077 if (BLI_is_dir(path)) {
1078 return true;
1079 }
1080 }
1081
1082 return false;
1083}
1084
1086{
1087 /* Test if app template provides a `userpref.blend`.
1088 * If not, we will share user preferences with the rest of Blender. */
1089 if (app_template[0] == '\0') {
1090 return false;
1091 }
1092
1093 char app_template_path[FILE_MAX];
1095 app_template, app_template_path, sizeof(app_template_path)))
1096 {
1097 return false;
1098 }
1099
1100 char userpref_path[FILE_MAX];
1101 BLI_path_join(userpref_path, sizeof(userpref_path), app_template_path, BLENDER_USERPREF_FILE);
1102 return BLI_exists(userpref_path);
1103}
1104
1106{
1107 BLI_listbase_clear(templates);
1108
1110
1111 for (const std::string &subdir : directories) {
1112 direntry *dirs;
1113 const uint dir_num = BLI_filelist_dir_contents(subdir.c_str(), &dirs);
1114 for (int f = 0; f < dir_num; f++) {
1115 if (!FILENAME_IS_CURRPAR(dirs[f].relname) && S_ISDIR(dirs[f].type)) {
1116 char *app_template = BLI_strdup(dirs[f].relname);
1118 }
1119 }
1120
1121 BLI_filelist_free(dirs, dir_num);
1122 }
1123}
1124
1127/* -------------------------------------------------------------------- */
1142static void where_is_temp(char *tempdir, const size_t tempdir_maxncpy, const char *userdir)
1143{
1144 if (userdir && BLI_temp_directory_path_copy_if_valid(tempdir, tempdir_maxncpy, userdir)) {
1145 return;
1146 }
1147 BLI_temp_directory_path_get(tempdir, tempdir_maxncpy);
1148}
1149
1150static void tempdir_session_create(char *tempdir_session,
1151 const size_t tempdir_session_maxncpy,
1152 const char *tempdir)
1153{
1154 tempdir_session[0] = '\0';
1155
1156 const int tempdir_len = strlen(tempdir);
1157 /* 'XXXXXX' is kind of tag to be replaced by `mktemp-family` by an UUID. */
1158 const char *session_name = "blender_XXXXXX";
1159 const int session_name_len = strlen(session_name);
1160
1161 /* +1 as a slash is added,
1162 * #_mktemp_s also requires the last null character is included. */
1163 const int tempdir_session_len_required = tempdir_len + session_name_len + 1;
1164
1165 if (tempdir_session_len_required <= tempdir_session_maxncpy) {
1166 /* No need to use path joining utility as we know the last character of #tempdir is a slash. */
1167 BLI_string_join(tempdir_session, tempdir_session_maxncpy, tempdir, session_name);
1168#ifdef WIN32
1169 const bool needs_create = (_mktemp_s(tempdir_session, tempdir_session_len_required) == 0);
1170#else
1171 const bool needs_create = (mkdtemp(tempdir_session) == nullptr);
1172#endif
1173 if (needs_create) {
1174 BLI_dir_create_recursive(tempdir_session);
1175 }
1176 if (BLI_is_dir(tempdir_session)) {
1177 BLI_path_slash_ensure(tempdir_session, tempdir_session_maxncpy);
1178 /* Success. */
1179 return;
1180 }
1181 }
1182
1183 CLOG_WARN(&LOG,
1184 "Could not generate a temp file name for '%s', falling back to '%s'",
1185 tempdir_session,
1186 tempdir);
1187 BLI_strncpy(tempdir_session, tempdir, tempdir_session_maxncpy);
1188}
1189
1190void BKE_tempdir_init(const char *userdir)
1191{
1192 /* Sets #g_app.temp_dirname_base to `userdir` if specified and is a valid directory,
1193 * otherwise chooses a suitable OS-specific temporary directory.
1194 * Sets #g_app.temp_dirname_session to a #mkdtemp
1195 * generated sub-dir of #g_app.temp_dirname_base. */
1196
1197 where_is_temp(g_app.temp_dirname_base, sizeof(g_app.temp_dirname_base), userdir);
1198
1199 /* Clear existing temp dir, if needed. */
1201 /* Now that we have a valid temp dir, add system-generated unique sub-dir. */
1203 g_app.temp_dirname_session, sizeof(g_app.temp_dirname_session), g_app.temp_dirname_base);
1204}
1205
1207{
1208 return g_app.temp_dirname_session[0] ? g_app.temp_dirname_session : BKE_tempdir_base();
1209}
1210
1211const char *BKE_tempdir_base()
1212{
1213 return g_app.temp_dirname_base;
1214}
1215
1217{
1218 if (g_app.temp_dirname_session[0] && BLI_is_dir(g_app.temp_dirname_session)) {
1219 BLI_delete(g_app.temp_dirname_session, true, true);
1220 }
1221}
1222
#define BLENDER_USERPREF_FILE
@ BLENDER_USER_DATAFILES
@ BLENDER_USER_EXTENSIONS
@ BLENDER_SYSTEM_DATAFILES
@ BLENDER_SYSTEM_EXTENSIONS
@ BLENDER_DATAFILES
@ BLENDER_SYSTEM_PYTHON
@ BLENDER_SYSTEM_SCRIPTS
@ BLENDER_USER_CONFIG
@ BLENDER_USER_SCRIPTS
@ BLENDER_RESOURCE_PATH_SYSTEM
@ BLENDER_RESOURCE_PATH_LOCAL
@ BLENDER_RESOURCE_PATH_USER
#define BLENDER_VERSION
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
#define BLI_assert_msg(a, msg)
Definition BLI_assert.h:57
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:350
bool BLI_dir_create_recursive(const char *dirname) ATTR_NONNULL()
Definition fileops_c.cc:391
unsigned int BLI_filelist_dir_contents(const char *dirname, struct direntry **r_filelist)
int BLI_delete(const char *path, bool dir, bool recursive) ATTR_NONNULL()
bool BLI_is_dir(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition storage.cc:433
void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries)
Some types for dealing with directories.
void BLI_kdtree_nd_ free(KDTree *tree)
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition listbase.cc:110
struct LinkData * BLI_genericNodeN(void *data)
Definition listbase.cc:909
#define FILE_MAX
bool BLI_path_program_search(char *program_filepath, size_t program_filepath_maxncpy, const char *program_name) ATTR_NONNULL(1
#define BLI_path_join(...)
const char * BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
#define FILENAME_IS_CURRPAR(_n)
int BLI_path_canonicalize_native(char *path, int path_maxncpy)
void void BLI_path_split_dir_part(const char *filepath, char *dir, size_t dir_maxncpy) ATTR_NONNULL(1
size_t BLI_path_join_array(char *__restrict dst, const size_t dst_maxncpy, const char *path_array[], const int path_array_num) ATTR_NONNULL(1
int BLI_path_slash_ensure(char *path, size_t path_maxncpy) ATTR_NONNULL(1)
bool BLI_path_abs_from_cwd(char *path, size_t path_maxncpy) ATTR_NONNULL(1)
const char * BLI_path_slash_rfind(const char *path) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
int BLI_path_normalize_native(char *path) ATTR_NONNULL(1)
#define FILE_MAXDIR
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
#define STRNCPY(dst, src)
Definition BLI_string.h:593
#define SNPRINTF(dst, format,...)
Definition BLI_string.h:597
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
size_t BLI_strncpy_wchar_as_utf8(char *__restrict dst, const wchar_t *__restrict src, size_t dst_maxncpy) ATTR_NONNULL(1
#define BLI_string_join(...)
unsigned int uint
bool void BLI_temp_directory_path_get(char *tempdir, const size_t tempdir_maxncpy) ATTR_NONNULL(1)
Definition tempfile.cc:53
bool BLI_temp_directory_path_copy_if_valid(char *tempdir, const size_t tempdir_maxncpy, const char *dirpath) ATTR_NONNULL(1
#define ARRAY_SIZE(arr)
#define STRINGIFY(x)
#define ELEM(...)
#define STREQ(a, b)
Compatibility-like things for windows.
void BLI_windows_get_default_root_dir(char root_dir[4])
#define S_ISDIR(x)
#define CLOG_ERROR(clg_ref,...)
Definition CLG_log.h:182
#define CLOG_WARN(clg_ref,...)
Definition CLG_log.h:181
#define CLOG_INFO(clg_ref, level,...)
Definition CLG_log.h:179
const char * GHOST_getUserSpecialDir(GHOST_TUserSpecialDirTypes type)
const char * GHOST_getSystemDir(int version, const char *versionstr)
GHOST_TSuccess GHOST_DisposeSystemPaths()
const char * GHOST_getUserDir(int version, const char *versionstr)
@ GHOST_kUserSpecialDirCaches
@ GHOST_kUserSpecialDirDocuments
Read Guarded memory(de)allocation.
#define ASSERT_IS_INIT()
Definition appdir.cc:88
static const char _str_null[]
Definition appdir.cc:59
void BKE_tempdir_init(const char *userdir)
Definition appdir.cc:1190
static bool is_appdir_init
Definition appdir.cc:87
void BKE_appdir_init()
Definition appdir.cc:93
std::optional< std::string > BKE_appdir_folder_id_create(const int folder_id, const char *subfolder)
Definition appdir.cc:764
char program_dirname[FILE_MAX]
Definition appdir.cc:73
static bool get_path_user_ex(char *targetpath, size_t targetpath_maxncpy, const char *folder_name, const char *subfolder_name, const int version, const bool check_is_dir)
Definition appdir.cc:476
static bool get_path_environment(char *targetpath, size_t targetpath_maxncpy, const char *subfolder_name, const char *envvar)
Definition appdir.cc:456
std::optional< std::string > BKE_appdir_resource_path_id(const int folder_id, const bool check_is_dir)
Definition appdir.cc:817
const char * BKE_appdir_folder_root()
Definition appdir.cc:152
const char * BKE_tempdir_session()
Definition appdir.cc:1206
static struct @74 g_app
static void tempdir_session_create(char *tempdir_session, const size_t tempdir_session_maxncpy, const char *tempdir)
Definition appdir.cc:1150
static void where_is_temp(char *tempdir, const size_t tempdir_maxncpy, const char *userdir)
Definition appdir.cc:1142
char temp_dirname_session[FILE_MAX]
Definition appdir.cc:77
std::optional< std::string > BKE_appdir_folder_id_user_notest(const int folder_id, const char *subfolder)
Definition appdir.cc:713
bool BKE_appdir_folder_documents(char *dir)
Definition appdir.cc:196
static bool test_env_path(char *path, const char *envvar, const bool check_is_dir)
Definition appdir.cc:336
bool BKE_appdir_app_template_has_userpref(const char *app_template)
Definition appdir.cc:1085
const char * BKE_appdir_program_dir()
Definition appdir.cc:948
void BKE_appdir_exit()
Definition appdir.cc:101
static bool test_path(char *targetpath, size_t targetpath_maxncpy, const bool check_is_dir, const char *path_base, const char *folder_name, const char *subfolder_name)
Definition appdir.cc:296
bool BKE_appdir_program_python_search(char *program_filepath, const size_t program_filepath_maxncpy, const int version_major, const int version_minor)
Definition appdir.cc:954
static bool get_path_system_ex(char *targetpath, size_t targetpath_maxncpy, const char *folder_name, const char *subfolder_name, const int version, const bool check_is_dir)
Definition appdir.cc:538
void BKE_appdir_program_path_init(const char *argv0)
Definition appdir.cc:917
std::optional< std::string > BKE_appdir_resource_path_id_with_version(const int folder_id, const bool check_is_dir, const int version)
Definition appdir.cc:789
const char * BKE_appdir_folder_default_or_root()
Definition appdir.cc:163
bool BKE_appdir_folder_id_ex(const int folder_id, const char *subfolder, char *path, size_t path_maxncpy)
Definition appdir.cc:591
static char * blender_version_decimal(const int version)
Definition appdir.cc:123
const char * BKE_appdir_folder_home()
Definition appdir.cc:172
static blender::Vector< std::string > appdir_app_template_directories()
Definition appdir.cc:1031
#define STR_OR_FALLBACK(a)
Definition appdir.cc:60
static bool get_path_local(char *targetpath, size_t targetpath_maxncpy, const char *folder_name, const char *subfolder_name)
Definition appdir.cc:421
std::optional< std::string > BKE_appdir_folder_id(const int folder_id, const char *subfolder)
Definition appdir.cc:704
bool BKE_appdir_app_template_any()
Definition appdir.cc:1066
static CLG_LogRef LOG
Definition appdir.cc:67
void BKE_appdir_app_templates(ListBase *templates)
Definition appdir.cc:1105
bool BKE_appdir_font_folder_default(char *dir, size_t dir_maxncpy)
Definition appdir.cc:250
bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_maxncpy)
Definition appdir.cc:1071
const char * BKE_appdir_folder_default()
Definition appdir.cc:137
static bool get_path_local_ex(char *targetpath, size_t targetpath_maxncpy, const char *folder_name, const char *subfolder_name, const int version, const bool check_is_dir)
Definition appdir.cc:377
const char * BKE_appdir_program_path()
Definition appdir.cc:940
char temp_dirname_base[FILE_MAX]
Definition appdir.cc:75
void BKE_tempdir_session_purge()
Definition appdir.cc:1216
static void where_am_i(char *program_filepath, const size_t program_filepath_maxncpy, const char *program_name)
Definition appdir.cc:844
static bool get_path_system(char *targetpath, size_t targetpath_maxncpy, const char *folder_name, const char *subfolder_name)
Definition appdir.cc:574
static bool get_path_user(char *targetpath, size_t targetpath_maxncpy, const char *folder_name, const char *subfolder_name)
Definition appdir.cc:517
char program_filepath[FILE_MAX]
Definition appdir.cc:71
const char * BKE_tempdir_base()
Definition appdir.cc:1211
static bool get_path_environment_ex(char *targetpath, size_t targetpath_maxncpy, const char *subfolder_name, const char *envvar, const bool check_is_dir)
Definition appdir.cc:441
bool BKE_appdir_folder_caches(char *path, const size_t path_maxncpy)
Definition appdir.cc:226
void append(const T &value)
bool is_empty() const
void *(* MEM_mallocN)(size_t len, const char *str)
Definition mallocn.cc:44
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
const char * BLI_expand_tilde(const char *path_with_tilde)
#define SEP_STR
Definition unit.cc:39
int conv_utf_16_to_8(const wchar_t *in16, char *out8, size_t size8)
Definition utfconv.cc:116
#define N_(msgid)
char app_template[64]
Definition wm_files.cc:1166