Blender  V3.3
appdir.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 
13 #include "BLI_fileops.h"
14 #include "BLI_fileops_types.h"
15 #include "BLI_listbase.h"
16 #include "BLI_path_util.h"
17 #include "BLI_string.h"
18 #include "BLI_string_utf8.h"
19 #include "BLI_string_utils.h"
20 #include "BLI_utildefines.h"
21 
22 #include "BKE_appdir.h" /* own include */
23 #include "BKE_blender_version.h"
24 
25 #include "BLT_translation.h"
26 
27 #include "GHOST_Path-api.h"
28 
29 #include "MEM_guardedalloc.h"
30 
31 #include "CLG_log.h"
32 
33 #ifdef WIN32
34 # include "utf_winfunc.h"
35 # include "utfconv.h"
36 # include <io.h>
37 # ifdef _WIN32_IE
38 # undef _WIN32_IE
39 # endif
40 # define _WIN32_IE 0x0501
41 # include "BLI_winstuff.h"
42 # include <shlobj.h>
43 # include <windows.h>
44 #else /* non windows */
45 # ifdef WITH_BINRELOC
46 # include "binreloc.h"
47 # endif
48 /* #mkdtemp on OSX (and probably all *BSD?), not worth making specific check for this OS. */
49 # include <unistd.h>
50 #endif /* WIN32 */
51 
52 static const char _str_null[] = "(null)";
53 #define STR_OR_FALLBACK(a) ((a) ? (a) : _str_null)
54 
55 /* -------------------------------------------------------------------- */
59 /* local */
60 static CLG_LogRef LOG = {"bke.appdir"};
61 
62 static struct {
71 } g_app = {
72  .temp_dirname_session = "",
73 };
74 
77 /* -------------------------------------------------------------------- */
81 #ifndef NDEBUG
82 static bool is_appdir_init = false;
83 # define ASSERT_IS_INIT() BLI_assert(is_appdir_init)
84 #else
85 # define ASSERT_IS_INIT() ((void)0)
86 #endif
87 
88 void BKE_appdir_init(void)
89 {
90 #ifndef NDEBUG
91  BLI_assert(is_appdir_init == false);
92  is_appdir_init = true;
93 #endif
94 }
95 
96 void BKE_appdir_exit(void)
97 {
98 #ifndef NDEBUG
99  BLI_assert(is_appdir_init == true);
100  is_appdir_init = false;
101 #endif
102 }
103 
106 /* -------------------------------------------------------------------- */
113 static char *blender_version_decimal(const int version)
114 {
115  static char version_str[5];
116  BLI_assert(version < 1000);
117  BLI_snprintf(version_str, sizeof(version_str), "%d.%d", version / 100, version % 100);
118  return version_str;
119 }
120 
123 /* -------------------------------------------------------------------- */
127 const char *BKE_appdir_folder_default(void)
128 {
129 #ifndef WIN32
130  return BLI_getenv("HOME");
131 #else /* Windows */
132  static char documentfolder[MAXPATHLEN];
133 
134  if (BKE_appdir_folder_documents(documentfolder)) {
135  return documentfolder;
136  }
137 
138  return NULL;
139 #endif /* WIN32 */
140 }
141 
142 const char *BKE_appdir_folder_root(void)
143 {
144 #ifndef WIN32
145  return "/";
146 #else
147  static char root[4];
149  return root;
150 #endif
151 }
152 
154 {
155  const char *path = BKE_appdir_folder_default();
156  if (path == NULL) {
157  path = BKE_appdir_folder_root();
158  }
159  return path;
160 }
161 
162 const char *BKE_appdir_folder_home(void)
163 {
164 #ifdef WIN32
165  return BLI_getenv("userprofile");
166 #elif defined(__APPLE__)
167  return BLI_expand_tilde("~/");
168 #else
169  return BLI_getenv("HOME");
170 #endif
171 }
172 
174 {
175  dir[0] = '\0';
176 
177  const char *documents_path = GHOST_getUserSpecialDir(GHOST_kUserSpecialDirDocuments);
178 
179  /* Usual case: Ghost gave us the documents path. We're done here. */
180  if (documents_path && BLI_is_dir(documents_path)) {
181  BLI_strncpy(dir, documents_path, FILE_MAXDIR);
182  return true;
183  }
184 
185  /* Ghost couldn't give us a documents path, let's try if we can find it ourselves. */
186 
187  const char *home_path = BKE_appdir_folder_home();
188  if (!home_path || !BLI_is_dir(home_path)) {
189  return false;
190  }
191 
192  char try_documents_path[FILE_MAXDIR];
193  /* Own attempt at getting a valid Documents path. */
194  BLI_path_join(try_documents_path, sizeof(try_documents_path), home_path, N_("Documents"), NULL);
195  if (!BLI_is_dir(try_documents_path)) {
196  return false;
197  }
198 
199  BLI_strncpy(dir, try_documents_path, FILE_MAXDIR);
200  return true;
201 }
202 
203 bool BKE_appdir_folder_caches(char *r_path, const size_t path_len)
204 {
205  r_path[0] = '\0';
206 
207  const char *caches_root_path = GHOST_getUserSpecialDir(GHOST_kUserSpecialDirCaches);
208  if (caches_root_path == NULL || !BLI_is_dir(caches_root_path)) {
209  caches_root_path = BKE_tempdir_base();
210  }
211  if (caches_root_path == NULL || !BLI_is_dir(caches_root_path)) {
212  return false;
213  }
214 
215 #ifdef WIN32
217  r_path, path_len, caches_root_path, "Blender Foundation", "Blender", "Cache", SEP_STR, NULL);
218 #elif defined(__APPLE__)
219  BLI_path_join(r_path, path_len, caches_root_path, "Blender", SEP_STR, NULL);
220 #else /* __linux__ */
221  BLI_path_join(r_path, path_len, caches_root_path, "blender", SEP_STR, NULL);
222 #endif
223 
224  return true;
225 }
226 
228 {
229  char test_dir[FILE_MAXDIR];
230  test_dir[0] = '\0';
231 
232 #ifdef WIN32
233  wchar_t wpath[FILE_MAXDIR];
234  if (SHGetSpecialFolderPathW(0, wpath, CSIDL_FONTS, 0)) {
235  wcscat(wpath, L"\\");
236  BLI_strncpy_wchar_as_utf8(test_dir, wpath, sizeof(test_dir));
237  }
238 #elif defined(__APPLE__)
239  STRNCPY(test_dir, BLI_expand_tilde("~/Library/Fonts/"));
240  BLI_path_slash_ensure(test_dir);
241 #else
242  STRNCPY(test_dir, "/usr/share/fonts");
243 #endif
244 
245  if (test_dir[0] && BLI_exists(test_dir)) {
246  BLI_strncpy(dir, test_dir, FILE_MAXDIR);
247  return true;
248  }
249  return false;
250 }
251 
254 /* -------------------------------------------------------------------- */
273 static bool test_path(char *targetpath,
274  size_t targetpath_len,
275  const bool check_is_dir,
276  const char *path_base,
277  const char *folder_name,
278  const char *subfolder_name)
279 {
280  ASSERT_IS_INIT();
281 
282  /* Only the last argument should be NULL. */
283  BLI_assert(!(folder_name == NULL && (subfolder_name != NULL)));
284  BLI_path_join(targetpath, targetpath_len, path_base, folder_name, subfolder_name, NULL);
285  if (check_is_dir == false) {
286  CLOG_INFO(&LOG, 3, "using without test: '%s'", targetpath);
287  return true;
288  }
289 
290  if (BLI_is_dir(targetpath)) {
291  CLOG_INFO(&LOG, 3, "found '%s'", targetpath);
292  return true;
293  }
294 
295  CLOG_INFO(&LOG, 3, "missing '%s'", targetpath);
296 
297  /* Path not found, don't accidentally use it,
298  * otherwise call this function with `check_is_dir` set to false. */
299  targetpath[0] = '\0';
300  return false;
301 }
302 
311 static bool test_env_path(char *path, const char *envvar, const bool check_is_dir)
312 {
313  ASSERT_IS_INIT();
314 
315  const char *env_path = envvar ? BLI_getenv(envvar) : NULL;
316  if (!env_path) {
317  return false;
318  }
319 
320  BLI_strncpy(path, env_path, FILE_MAX);
321 
322  if (check_is_dir == false) {
323  CLOG_INFO(&LOG, 3, "using env '%s' without test: '%s'", envvar, env_path);
324  return true;
325  }
326 
327  if (BLI_is_dir(env_path)) {
328  CLOG_INFO(&LOG, 3, "env '%s' found: %s", envvar, env_path);
329  return true;
330  }
331 
332  CLOG_INFO(&LOG, 3, "env '%s' missing: %s", envvar, env_path);
333 
334  /* Path not found, don't accidentally use it,
335  * otherwise call this function with `check_is_dir` set to false. */
336  path[0] = '\0';
337  return false;
338 }
339 
352 static bool get_path_local_ex(char *targetpath,
353  size_t targetpath_len,
354  const char *folder_name,
355  const char *subfolder_name,
356  const int version,
357  const bool check_is_dir)
358 {
359  char relfolder[FILE_MAX];
360 
361  CLOG_INFO(&LOG,
362  3,
363  "folder='%s', subfolder='%s'",
364  STR_OR_FALLBACK(folder_name),
365  STR_OR_FALLBACK(subfolder_name));
366 
367  if (folder_name) { /* `subfolder_name` may be NULL. */
368  BLI_path_join(relfolder, sizeof(relfolder), folder_name, subfolder_name, NULL);
369  }
370  else {
371  relfolder[0] = '\0';
372  }
373 
374  /* Try `{g_app.program_dirname}/2.xx/{folder_name}` the default directory
375  * for a portable distribution. See `WITH_INSTALL_PORTABLE` build-option. */
376  const char *path_base = g_app.program_dirname;
377 #ifdef __APPLE__
378  /* Due new code-sign situation in OSX > 10.9.5
379  * we must move the blender_version dir with contents to Resources. */
380  char osx_resourses[FILE_MAX];
381  BLI_snprintf(osx_resourses, sizeof(osx_resourses), "%s../Resources", g_app.program_dirname);
382  /* Remove the '/../' added above. */
383  BLI_path_normalize(NULL, osx_resourses);
384  path_base = osx_resourses;
385 #endif
386  return test_path(targetpath,
387  targetpath_len,
388  check_is_dir,
389  path_base,
390  blender_version_decimal(version),
391  relfolder);
392 }
393 static bool get_path_local(char *targetpath,
394  size_t targetpath_len,
395  const char *folder_name,
396  const char *subfolder_name)
397 {
398  const int version = BLENDER_VERSION;
399  const bool check_is_dir = true;
400  return get_path_local_ex(
401  targetpath, targetpath_len, folder_name, subfolder_name, version, check_is_dir);
402 }
403 
405 {
406  /* Detect portable install by the existence of `config` folder. */
407  char path[FILE_MAX];
408  return get_path_local(path, sizeof(path), "config", NULL);
409 }
410 
420 static bool get_path_environment_ex(char *targetpath,
421  size_t targetpath_len,
422  const char *subfolder_name,
423  const char *envvar,
424  const bool check_is_dir)
425 {
426  char user_path[FILE_MAX];
427 
428  if (test_env_path(user_path, envvar, check_is_dir)) {
429  /* Note that `subfolder_name` may be NULL, in this case we use `user_path` as-is. */
430  return test_path(targetpath, targetpath_len, check_is_dir, user_path, subfolder_name, NULL);
431  }
432  return false;
433 }
434 static bool get_path_environment(char *targetpath,
435  size_t targetpath_len,
436  const char *subfolder_name,
437  const char *envvar)
438 {
439  const bool check_is_dir = true;
440  return get_path_environment_ex(targetpath, targetpath_len, subfolder_name, envvar, check_is_dir);
441 }
442 
453 static bool get_path_user_ex(char *targetpath,
454  size_t targetpath_len,
455  const char *folder_name,
456  const char *subfolder_name,
457  const int version,
458  const bool check_is_dir)
459 {
460  char user_path[FILE_MAX];
461  const char *user_base_path;
462 
463  /* for portable install, user path is always local */
465  return get_path_local_ex(
466  targetpath, targetpath_len, folder_name, subfolder_name, version, check_is_dir);
467  }
468  user_path[0] = '\0';
469 
470  user_base_path = GHOST_getUserDir(version, blender_version_decimal(version));
471  if (user_base_path) {
472  BLI_strncpy(user_path, user_base_path, FILE_MAX);
473  }
474 
475  if (!user_path[0]) {
476  return false;
477  }
478 
479  CLOG_INFO(&LOG,
480  3,
481  "'%s', folder='%s', subfolder='%s'",
482  user_path,
483  STR_OR_FALLBACK(folder_name),
484  STR_OR_FALLBACK(subfolder_name));
485 
486  /* `subfolder_name` may be NULL. */
487  return test_path(
488  targetpath, targetpath_len, check_is_dir, user_path, folder_name, subfolder_name);
489 }
490 static bool get_path_user(char *targetpath,
491  size_t targetpath_len,
492  const char *folder_name,
493  const char *subfolder_name)
494 {
495  const int version = BLENDER_VERSION;
496  const bool check_is_dir = true;
497  return get_path_user_ex(
498  targetpath, targetpath_len, folder_name, subfolder_name, version, check_is_dir);
499 }
500 
511 static bool get_path_system_ex(char *targetpath,
512  size_t targetpath_len,
513  const char *folder_name,
514  const char *subfolder_name,
515  const int version,
516  const bool check_is_dir)
517 {
518  char system_path[FILE_MAX];
519  const char *system_base_path;
520  char relfolder[FILE_MAX];
521 
522  if (folder_name) { /* `subfolder_name` may be NULL. */
523  BLI_path_join(relfolder, sizeof(relfolder), folder_name, subfolder_name, NULL);
524  }
525  else {
526  relfolder[0] = '\0';
527  }
528 
529  system_path[0] = '\0';
530  system_base_path = GHOST_getSystemDir(version, blender_version_decimal(version));
531  if (system_base_path) {
532  BLI_strncpy(system_path, system_base_path, FILE_MAX);
533  }
534 
535  if (!system_path[0]) {
536  return false;
537  }
538 
539  CLOG_INFO(&LOG,
540  3,
541  "'%s', folder='%s', subfolder='%s'",
542  system_path,
543  STR_OR_FALLBACK(folder_name),
544  STR_OR_FALLBACK(subfolder_name));
545 
546  /* Try `$BLENDERPATH/folder_name/subfolder_name`, `subfolder_name` may be NULL. */
547  return test_path(
548  targetpath, targetpath_len, check_is_dir, system_path, folder_name, subfolder_name);
549 }
550 
551 static bool get_path_system(char *targetpath,
552  size_t targetpath_len,
553  const char *folder_name,
554  const char *subfolder_name)
555 {
556  const int version = BLENDER_VERSION;
557  const bool check_is_dir = true;
558  return get_path_system_ex(
559  targetpath, targetpath_len, folder_name, subfolder_name, version, check_is_dir);
560 }
561 
564 /* -------------------------------------------------------------------- */
568 bool BKE_appdir_folder_id_ex(const int folder_id,
569  const char *subfolder,
570  char *path,
571  size_t path_len)
572 {
573  switch (folder_id) {
574  case BLENDER_DATAFILES: /* general case */
575  if (get_path_environment(path, path_len, subfolder, "BLENDER_USER_DATAFILES")) {
576  break;
577  }
578  if (get_path_user(path, path_len, "datafiles", subfolder)) {
579  break;
580  }
581  if (get_path_environment(path, path_len, subfolder, "BLENDER_SYSTEM_DATAFILES")) {
582  break;
583  }
584  if (get_path_local(path, path_len, "datafiles", subfolder)) {
585  break;
586  }
587  if (get_path_system(path, path_len, "datafiles", subfolder)) {
588  break;
589  }
590  return false;
591 
593  if (get_path_environment(path, path_len, subfolder, "BLENDER_USER_DATAFILES")) {
594  break;
595  }
596  if (get_path_user(path, path_len, "datafiles", subfolder)) {
597  break;
598  }
599  return false;
600 
602  if (get_path_environment(path, path_len, subfolder, "BLENDER_SYSTEM_DATAFILES")) {
603  break;
604  }
605  if (get_path_system(path, path_len, "datafiles", subfolder)) {
606  break;
607  }
608  if (get_path_local(path, path_len, "datafiles", subfolder)) {
609  break;
610  }
611  return false;
612 
614  if (get_path_environment(path, path_len, subfolder, "BLENDER_USER_DATAFILES")) {
615  break;
616  }
617  if (get_path_user(path, path_len, "autosave", subfolder)) {
618  break;
619  }
620  return false;
621 
622  case BLENDER_USER_CONFIG:
623  if (get_path_environment(path, path_len, subfolder, "BLENDER_USER_CONFIG")) {
624  break;
625  }
626  if (get_path_user(path, path_len, "config", subfolder)) {
627  break;
628  }
629  return false;
630 
632  if (get_path_environment(path, path_len, subfolder, "BLENDER_USER_SCRIPTS")) {
633  break;
634  }
635  if (get_path_user(path, path_len, "scripts", subfolder)) {
636  break;
637  }
638  return false;
639 
641  if (get_path_environment(path, path_len, subfolder, "BLENDER_SYSTEM_SCRIPTS")) {
642  break;
643  }
644  if (get_path_system(path, path_len, "scripts", subfolder)) {
645  break;
646  }
647  if (get_path_local(path, path_len, "scripts", subfolder)) {
648  break;
649  }
650  return false;
651 
653  if (get_path_environment(path, path_len, subfolder, "BLENDER_SYSTEM_PYTHON")) {
654  break;
655  }
656  if (get_path_system(path, path_len, "python", subfolder)) {
657  break;
658  }
659  if (get_path_local(path, path_len, "python", subfolder)) {
660  break;
661  }
662  return false;
663 
664  default:
666  break;
667  }
668 
669  return true;
670 }
671 
672 const char *BKE_appdir_folder_id(const int folder_id, const char *subfolder)
673 {
674  static char path[FILE_MAX] = "";
675  if (BKE_appdir_folder_id_ex(folder_id, subfolder, path, sizeof(path))) {
676  return path;
677  }
678  return NULL;
679 }
680 
681 const char *BKE_appdir_folder_id_user_notest(const int folder_id, const char *subfolder)
682 {
683  const int version = BLENDER_VERSION;
684  static char path[FILE_MAX] = "";
685  const bool check_is_dir = false;
686 
687  switch (folder_id) {
690  path, sizeof(path), subfolder, "BLENDER_USER_DATAFILES", check_is_dir)) {
691  break;
692  }
693  get_path_user_ex(path, sizeof(path), "datafiles", subfolder, version, check_is_dir);
694  break;
695  case BLENDER_USER_CONFIG:
697  path, sizeof(path), subfolder, "BLENDER_USER_CONFIG", check_is_dir)) {
698  break;
699  }
700  get_path_user_ex(path, sizeof(path), "config", subfolder, version, check_is_dir);
701  break;
704  path, sizeof(path), subfolder, "BLENDER_USER_AUTOSAVE", check_is_dir)) {
705  break;
706  }
707  get_path_user_ex(path, sizeof(path), "autosave", subfolder, version, check_is_dir);
708  break;
711  path, sizeof(path), subfolder, "BLENDER_USER_SCRIPTS", check_is_dir)) {
712  break;
713  }
714  get_path_user_ex(path, sizeof(path), "scripts", subfolder, version, check_is_dir);
715  break;
716  default:
718  break;
719  }
720 
721  if ('\0' == path[0]) {
722  return NULL;
723  }
724  return path;
725 }
726 
727 const char *BKE_appdir_folder_id_create(const int folder_id, const char *subfolder)
728 {
729  const char *path;
730 
731  /* Only for user folders. */
732  if (!ELEM(folder_id,
737  return NULL;
738  }
739 
740  path = BKE_appdir_folder_id(folder_id, subfolder);
741 
742  if (!path) {
743  path = BKE_appdir_folder_id_user_notest(folder_id, subfolder);
744  if (path) {
746  }
747  }
748 
749  return path;
750 }
751 
752 const char *BKE_appdir_folder_id_version(const int folder_id,
753  const int version,
754  const bool check_is_dir)
755 {
756  static char path[FILE_MAX] = "";
757  bool ok;
758  switch (folder_id) {
760  ok = get_path_user_ex(path, sizeof(path), NULL, NULL, version, check_is_dir);
761  break;
763  ok = get_path_local_ex(path, sizeof(path), NULL, NULL, version, check_is_dir);
764  break;
766  ok = get_path_system_ex(path, sizeof(path), NULL, NULL, version, check_is_dir);
767  break;
768  default:
769  path[0] = '\0'; /* in case check_is_dir is false */
770  ok = false;
771  BLI_assert_msg(0, "incorrect ID");
772  break;
773  }
774  return ok ? path : NULL;
775 }
776 
779 /* -------------------------------------------------------------------- */
797 static void where_am_i(char *fullname, const size_t maxlen, const char *name)
798 {
799 #ifdef WITH_BINRELOC
800  /* Linux uses `binreloc` since `argv[0]` is not reliable, call `br_init(NULL)` first. */
801  {
802  const char *path = NULL;
803  path = br_find_exe(NULL);
804  if (path) {
805  BLI_strncpy(fullname, path, maxlen);
806  free((void *)path);
807  return;
808  }
809  }
810 #endif
811 
812 #ifdef _WIN32
813  {
814  wchar_t *fullname_16 = MEM_mallocN(maxlen * sizeof(wchar_t), "ProgramPath");
815  if (GetModuleFileNameW(0, fullname_16, maxlen)) {
816  conv_utf_16_to_8(fullname_16, fullname, maxlen);
817  if (!BLI_exists(fullname)) {
818  CLOG_ERROR(&LOG, "path can't be found: \"%.*s\"", (int)maxlen, fullname);
819  MessageBox(
820  NULL, "path contains invalid characters or is too long (see console)", "Error", MB_OK);
821  }
822  MEM_freeN(fullname_16);
823  return;
824  }
825 
826  MEM_freeN(fullname_16);
827  }
828 #endif
829 
830  /* Unix and non Linux. */
831  if (name && name[0]) {
832 
833  BLI_strncpy(fullname, name, maxlen);
834  if (name[0] == '.') {
835  BLI_path_abs_from_cwd(fullname, maxlen);
836 #ifdef _WIN32
837  BLI_path_program_extensions_add_win32(fullname, maxlen);
838 #endif
839  }
840  else if (BLI_path_slash_rfind(name)) {
841  /* Full path. */
842  BLI_strncpy(fullname, name, maxlen);
843 #ifdef _WIN32
844  BLI_path_program_extensions_add_win32(fullname, maxlen);
845 #endif
846  }
847  else {
848  BLI_path_program_search(fullname, maxlen, name);
849  }
850  /* Remove "/./" and "/../" so string comparisons can be used on the path. */
851  BLI_path_normalize(NULL, fullname);
852 
853 #if defined(DEBUG)
854  if (!STREQ(name, fullname)) {
855  CLOG_INFO(&LOG, 2, "guessing '%s' == '%s'", name, fullname);
856  }
857 #endif
858  }
859 }
860 
861 void BKE_appdir_program_path_init(const char *argv0)
862 {
863  where_am_i(g_app.program_filepath, sizeof(g_app.program_filepath), argv0);
864  BLI_split_dir_part(g_app.program_filepath, g_app.program_dirname, sizeof(g_app.program_dirname));
865 }
866 
867 const char *BKE_appdir_program_path(void)
868 {
869  BLI_assert(g_app.program_filepath[0]);
870  return g_app.program_filepath;
871 }
872 
873 const char *BKE_appdir_program_dir(void)
874 {
875  BLI_assert(g_app.program_dirname[0]);
876  return g_app.program_dirname;
877 }
878 
880  const size_t fullpath_len,
881  const int version_major,
882  const int version_minor)
883 {
884  ASSERT_IS_INIT();
885 
886 #ifdef PYTHON_EXECUTABLE_NAME
887  /* Passed in from the build-systems 'PYTHON_EXECUTABLE'. */
888  const char *python_build_def = STRINGIFY(PYTHON_EXECUTABLE_NAME);
889 #endif
890  const char *basename = "python";
891 #if defined(WIN32) && !defined(NDEBUG)
892  const char *basename_debug = "python_d";
893 #endif
894  char python_version[16];
895  /* Check both possible names. */
896  const char *python_names[] = {
897 #ifdef PYTHON_EXECUTABLE_NAME
898  python_build_def,
899 #endif
900 #if defined(WIN32) && !defined(NDEBUG)
901  basename_debug,
902 #endif
903  python_version,
904  basename,
905  };
906  bool is_found = false;
907 
908  SNPRINTF(python_version, "%s%d.%d", basename, version_major, version_minor);
909 
910  {
911  const char *python_bin_dir = BKE_appdir_folder_id(BLENDER_SYSTEM_PYTHON, "bin");
912  if (python_bin_dir) {
913 
914  for (int i = 0; i < ARRAY_SIZE(python_names); i++) {
915  BLI_join_dirfile(fullpath, fullpath_len, python_bin_dir, python_names[i]);
916 
917  if (
918 #ifdef _WIN32
919  BLI_path_program_extensions_add_win32(fullpath, fullpath_len)
920 #else
921  BLI_exists(fullpath)
922 #endif
923  ) {
924  is_found = true;
925  break;
926  }
927  }
928  }
929  }
930 
931  if (is_found == false) {
932  for (int i = 0; i < ARRAY_SIZE(python_names); i++) {
933  if (BLI_path_program_search(fullpath, fullpath_len, python_names[i])) {
934  is_found = true;
935  break;
936  }
937  }
938  }
939 
940  if (is_found == false) {
941  *fullpath = '\0';
942  }
943 
944  return is_found;
945 }
946 
949 /* -------------------------------------------------------------------- */
954 static const char *app_template_directory_search[2] = {
955  "startup" SEP_STR "bl_app_templates_user",
956  "startup" SEP_STR "bl_app_templates_system",
957 };
958 
959 static const int app_template_directory_id[2] = {
960  /* Only 'USER' */
962  /* Covers 'LOCAL' & 'SYSTEM'. */
964 };
965 
967 {
968  char temp_dir[FILE_MAX];
969  for (int i = 0; i < ARRAY_SIZE(app_template_directory_id); i++) {
972  temp_dir,
973  sizeof(temp_dir))) {
974  return true;
975  }
976  }
977  return false;
978 }
979 
980 bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_len)
981 {
982  for (int i = 0; i < ARRAY_SIZE(app_template_directory_id); i++) {
983  char subdir[FILE_MAX];
984  BLI_join_dirfile(subdir, sizeof(subdir), app_template_directory_search[i], app_template);
985  if (BKE_appdir_folder_id_ex(app_template_directory_id[i], subdir, path, path_len)) {
986  return true;
987  }
988  }
989  return false;
990 }
991 
993 {
994  /* Test if app template provides a `userpref.blend`.
995  * If not, we will share user preferences with the rest of Blender. */
996  if (app_template[0] == '\0') {
997  return false;
998  }
999 
1000  char app_template_path[FILE_MAX];
1002  app_template, app_template_path, sizeof(app_template_path))) {
1003  return false;
1004  }
1005 
1006  char userpref_path[FILE_MAX];
1007  BLI_path_join(
1008  userpref_path, sizeof(userpref_path), app_template_path, BLENDER_USERPREF_FILE, NULL);
1009  return BLI_exists(userpref_path);
1010 }
1011 
1013 {
1014  BLI_listbase_clear(templates);
1015 
1016  for (int i = 0; i < ARRAY_SIZE(app_template_directory_id); i++) {
1017  char subdir[FILE_MAX];
1020  subdir,
1021  sizeof(subdir))) {
1022  continue;
1023  }
1024 
1025  struct direntry *dirs;
1026  const uint dir_num = BLI_filelist_dir_contents(subdir, &dirs);
1027  for (int f = 0; f < dir_num; f++) {
1028  if (!FILENAME_IS_CURRPAR(dirs[f].relname) && S_ISDIR(dirs[f].type)) {
1029  char *template = BLI_strdup(dirs[f].relname);
1030  BLI_addtail(templates, BLI_genericNodeN(template));
1031  }
1032  }
1033 
1034  BLI_filelist_free(dirs, dir_num);
1035  }
1036 }
1037 
1040 /* -------------------------------------------------------------------- */
1055 static void where_is_temp(char *tempdir, const size_t tempdir_len, const char *userdir)
1056 {
1057 
1058  tempdir[0] = '\0';
1059 
1060  if (userdir && BLI_is_dir(userdir)) {
1061  BLI_strncpy(tempdir, userdir, tempdir_len);
1062  }
1063 
1064  if (tempdir[0] == '\0') {
1065  const char *env_vars[] = {
1066 #ifdef WIN32
1067  "TEMP",
1068 #else
1069  /* Non standard (could be removed). */
1070  "TMP",
1071  /* Posix standard. */
1072  "TMPDIR",
1073 #endif
1074  };
1075  for (int i = 0; i < ARRAY_SIZE(env_vars); i++) {
1076  const char *tmp = BLI_getenv(env_vars[i]);
1077  if (tmp && (tmp[0] != '\0') && BLI_is_dir(tmp)) {
1078  BLI_strncpy(tempdir, tmp, tempdir_len);
1079  break;
1080  }
1081  }
1082  }
1083 
1084  if (tempdir[0] == '\0') {
1085  BLI_strncpy(tempdir, "/tmp/", tempdir_len);
1086  }
1087  else {
1088  /* add a trailing slash if needed */
1089  BLI_path_slash_ensure(tempdir);
1090  }
1091 }
1092 
1093 static void tempdir_session_create(char *tempdir_session,
1094  const size_t tempdir_session_len,
1095  const char *tempdir)
1096 {
1097  tempdir_session[0] = '\0';
1098 
1099  const int tempdir_len = strlen(tempdir);
1100  /* 'XXXXXX' is kind of tag to be replaced by `mktemp-family` by an UUID. */
1101  const char *session_name = "blender_XXXXXX";
1102  const int session_name_len = strlen(session_name);
1103 
1104  /* +1 as a slash is added,
1105  * #_mktemp_s also requires the last null character is included. */
1106  const int tempdir_session_len_required = tempdir_len + session_name_len + 1;
1107 
1108  if (tempdir_session_len_required <= tempdir_session_len) {
1109  /* No need to use path joining utility as we know the last character of #tempdir is a slash. */
1110  BLI_string_join(tempdir_session, tempdir_session_len, tempdir, session_name);
1111 #ifdef WIN32
1112  const bool needs_create = (_mktemp_s(tempdir_session, tempdir_session_len_required) == 0);
1113 #else
1114  const bool needs_create = (mkdtemp(tempdir_session) == NULL);
1115 #endif
1116  if (needs_create) {
1117  BLI_dir_create_recursive(tempdir_session);
1118  }
1119  if (BLI_is_dir(tempdir_session)) {
1120  BLI_path_slash_ensure(tempdir_session);
1121  /* Success. */
1122  return;
1123  }
1124  }
1125 
1126  CLOG_WARN(&LOG,
1127  "Could not generate a temp file name for '%s', falling back to '%s'",
1128  tempdir_session,
1129  tempdir);
1130  BLI_strncpy(tempdir_session, tempdir, tempdir_session_len);
1131 }
1132 
1133 void BKE_tempdir_init(const char *userdir)
1134 {
1135  /* Sets #g_app.temp_dirname_base to \a userdir if specified and is a valid directory,
1136  * otherwise chooses a suitable OS-specific temporary directory.
1137  * Sets #g_app.temp_dirname_session to a #mkdtemp
1138  * generated sub-dir of #g_app.temp_dirname_base. */
1139 
1140  where_is_temp(g_app.temp_dirname_base, sizeof(g_app.temp_dirname_base), userdir);
1141 
1142  /* Clear existing temp dir, if needed. */
1144  /* Now that we have a valid temp dir, add system-generated unique sub-dir. */
1146  g_app.temp_dirname_session, sizeof(g_app.temp_dirname_session), g_app.temp_dirname_base);
1147 }
1148 
1149 const char *BKE_tempdir_session(void)
1150 {
1151  return g_app.temp_dirname_session[0] ? g_app.temp_dirname_session : BKE_tempdir_base();
1152 }
1153 
1154 const char *BKE_tempdir_base(void)
1155 {
1156  return g_app.temp_dirname_base;
1157 }
1158 
1160 {
1161  if (g_app.temp_dirname_session[0] && BLI_is_dir(g_app.temp_dirname_session)) {
1162  BLI_delete(g_app.temp_dirname_session, true, true);
1163  }
1164 }
1165 
#define BLENDER_USERPREF_FILE
Definition: BKE_appdir.h:176
@ BLENDER_RESOURCE_PATH_SYSTEM
Definition: BKE_appdir.h:172
@ BLENDER_RESOURCE_PATH_LOCAL
Definition: BKE_appdir.h:171
@ BLENDER_RESOURCE_PATH_USER
Definition: BKE_appdir.h:170
@ BLENDER_USER_DATAFILES
Definition: BKE_appdir.h:158
@ BLENDER_SYSTEM_DATAFILES
Definition: BKE_appdir.h:163
@ BLENDER_DATAFILES
Definition: BKE_appdir.h:154
@ BLENDER_SYSTEM_PYTHON
Definition: BKE_appdir.h:165
@ BLENDER_SYSTEM_SCRIPTS
Definition: BKE_appdir.h:164
@ BLENDER_USER_AUTOSAVE
Definition: BKE_appdir.h:160
@ BLENDER_USER_CONFIG
Definition: BKE_appdir.h:157
@ BLENDER_USER_SCRIPTS
Definition: BKE_appdir.h:159
#define BLENDER_VERSION
#define BLI_assert_unreachable()
Definition: BLI_assert.h:93
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition: BLI_assert.h:53
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:314
int BLI_delete(const char *file, bool dir, bool recursive) ATTR_NONNULL()
Definition: fileops.c:934
unsigned int BLI_filelist_dir_contents(const char *dir, struct direntry **r_filelist)
Definition: BLI_filelist.c:218
bool BLI_is_dir(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:397
void BLI_filelist_free(struct direntry *filelist, unsigned int nrentries)
Definition: BLI_filelist.c:420
bool BLI_dir_create_recursive(const char *dir) ATTR_NONNULL()
Definition: fileops.c:1219
Some types for dealing with directories.
void BLI_kdtree_nd_() free(KDTree *tree)
Definition: kdtree_impl.h:102
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
Definition: BLI_listbase.h:273
struct LinkData * BLI_genericNodeN(void *data)
Definition: listbase.c:842
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
void BLI_split_dir_part(const char *string, char *dir, size_t dirlen)
Definition: path_util.c:1490
#define FILE_MAX
void BLI_path_normalize(const char *relabase, char *path) ATTR_NONNULL(2)
Definition: path_util.c:131
bool BLI_path_program_search(char *fullname, size_t maxlen, const char *name)
Definition: path_util.c:1091
#define FILENAME_IS_CURRPAR(_n)
const char * BLI_getenv(const char *env) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
Definition: path_util.c:1168
size_t BLI_path_join(char *__restrict dst, size_t dst_len, const char *path_first,...) ATTR_NONNULL(1
bool BLI_path_abs_from_cwd(char *path, size_t maxlen) ATTR_NONNULL()
Definition: path_util.c:1015
int BLI_path_slash_ensure(char *string) ATTR_NONNULL()
Definition: path_util.c:1780
const char * BLI_path_slash_rfind(const char *string) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT
Definition: path_util.c:1765
#define FILE_MAXDIR
void BLI_join_dirfile(char *__restrict dst, size_t maxlen, const char *__restrict dir, const char *__restrict file) ATTR_NONNULL()
Definition: path_util.c:1531
#define STRNCPY(dst, src)
Definition: BLI_string.h:483
#define SNPRINTF(dst, format,...)
Definition: BLI_string.h:485
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC
Definition: string.c:42
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
size_t BLI_strncpy_wchar_as_utf8(char *__restrict dst, const wchar_t *__restrict src, size_t maxncpy) ATTR_NONNULL(1
#define BLI_string_join(result, result_len,...)
unsigned int uint
Definition: BLI_sys_types.h:67
#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)
Definition: BLI_winstuff.h:48
#define MAXPATHLEN
Definition: BLI_winstuff.h:42
#define CLOG_ERROR(clg_ref,...)
Definition: CLG_log.h:190
#define CLOG_WARN(clg_ref,...)
Definition: CLG_log.h:189
#define CLOG_INFO(clg_ref, level,...)
Definition: CLG_log.h:187
const char * GHOST_getUserSpecialDir(GHOST_TUserSpecialDirTypes type)
const char * GHOST_getUserDir(int version, const char *versionstr)
const char * GHOST_getSystemDir(int version, const char *versionstr)
@ GHOST_kUserSpecialDirCaches
Definition: GHOST_Types.h:569
@ GHOST_kUserSpecialDirDocuments
Definition: GHOST_Types.h:564
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
Read Guarded memory(de)allocation.
bool BKE_appdir_app_template_any(void)
Definition: appdir.c:966
#define ASSERT_IS_INIT()
Definition: appdir.c:83
static const char _str_null[]
Definition: appdir.c:52
const char * BKE_appdir_folder_id_version(const int folder_id, const int version, const bool check_is_dir)
Definition: appdir.c:752
const char * BKE_appdir_folder_id_create(const int folder_id, const char *subfolder)
Definition: appdir.c:727
static const int app_template_directory_id[2]
Definition: appdir.c:959
void BKE_tempdir_init(const char *userdir)
Definition: appdir.c:1133
static bool is_appdir_init
Definition: appdir.c:82
static bool get_path_user(char *targetpath, size_t targetpath_len, const char *folder_name, const char *subfolder_name)
Definition: appdir.c:490
char program_dirname[FILE_MAX]
Definition: appdir.c:66
bool BKE_appdir_font_folder_default(char *dir)
Definition: appdir.c:227
static void where_am_i(char *fullname, const size_t maxlen, const char *name)
Definition: appdir.c:797
const char * BKE_appdir_folder_default_or_root(void)
Definition: appdir.c:153
const char * BKE_appdir_folder_default(void)
Definition: appdir.c:127
static bool get_path_system_ex(char *targetpath, size_t targetpath_len, const char *folder_name, const char *subfolder_name, const int version, const bool check_is_dir)
Definition: appdir.c:511
static char * blender_version_decimal(const int version)
Definition: appdir.c:113
bool BKE_appdir_app_is_portable_install(void)
Definition: appdir.c:404
char temp_dirname_session[FILE_MAX]
Definition: appdir.c:70
bool BKE_appdir_program_python_search(char *fullpath, const size_t fullpath_len, const int version_major, const int version_minor)
Definition: appdir.c:879
bool BKE_appdir_folder_documents(char *dir)
Definition: appdir.c:173
static bool test_env_path(char *path, const char *envvar, const bool check_is_dir)
Definition: appdir.c:311
void BKE_appdir_init(void)
Definition: appdir.c:88
bool BKE_appdir_app_template_has_userpref(const char *app_template)
Definition: appdir.c:992
void BKE_appdir_exit(void)
Definition: appdir.c:96
static bool get_path_user_ex(char *targetpath, size_t targetpath_len, const char *folder_name, const char *subfolder_name, const int version, const bool check_is_dir)
Definition: appdir.c:453
void BKE_tempdir_session_purge(void)
Definition: appdir.c:1159
static struct @79 g_app
static bool get_path_local(char *targetpath, size_t targetpath_len, const char *folder_name, const char *subfolder_name)
Definition: appdir.c:393
const char * BKE_appdir_folder_id(const int folder_id, const char *subfolder)
Definition: appdir.c:672
static const char * app_template_directory_search[2]
Definition: appdir.c:954
bool BKE_appdir_folder_id_ex(const int folder_id, const char *subfolder, char *path, size_t path_len)
Definition: appdir.c:568
void BKE_appdir_program_path_init(const char *argv0)
Definition: appdir.c:861
static void tempdir_session_create(char *tempdir_session, const size_t tempdir_session_len, const char *tempdir)
Definition: appdir.c:1093
const char * BKE_appdir_folder_home(void)
Definition: appdir.c:162
static bool get_path_local_ex(char *targetpath, size_t targetpath_len, const char *folder_name, const char *subfolder_name, const int version, const bool check_is_dir)
Definition: appdir.c:352
const char * BKE_tempdir_session(void)
Definition: appdir.c:1149
#define STR_OR_FALLBACK(a)
Definition: appdir.c:53
bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_len)
Definition: appdir.c:980
bool BKE_appdir_folder_caches(char *r_path, const size_t path_len)
Definition: appdir.c:203
static bool get_path_environment_ex(char *targetpath, size_t targetpath_len, const char *subfolder_name, const char *envvar, const bool check_is_dir)
Definition: appdir.c:420
const char * BKE_tempdir_base(void)
Definition: appdir.c:1154
const char * BKE_appdir_folder_root(void)
Definition: appdir.c:142
static CLG_LogRef LOG
Definition: appdir.c:60
static void where_is_temp(char *tempdir, const size_t tempdir_len, const char *userdir)
Definition: appdir.c:1055
static bool get_path_system(char *targetpath, size_t targetpath_len, const char *folder_name, const char *subfolder_name)
Definition: appdir.c:551
void BKE_appdir_app_templates(ListBase *templates)
Definition: appdir.c:1012
const char * BKE_appdir_program_path(void)
Definition: appdir.c:867
static bool test_path(char *targetpath, size_t targetpath_len, const bool check_is_dir, const char *path_base, const char *folder_name, const char *subfolder_name)
Definition: appdir.c:273
const char * BKE_appdir_program_dir(void)
Definition: appdir.c:873
const char * BKE_appdir_folder_id_user_notest(const int folder_id, const char *subfolder)
Definition: appdir.c:681
char temp_dirname_base[FILE_MAX]
Definition: appdir.c:68
char program_filepath[FILE_MAX]
Definition: appdir.c:64
static bool get_path_environment(char *targetpath, size_t targetpath_len, const char *subfolder_name, const char *envvar)
Definition: appdir.c:434
static char * basename(char *string)
Definition: datatoc.c:17
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
#define L
const char * BLI_expand_tilde(const char *path_with_tilde)
const char * relname
#define SEP_STR
Definition: unit.c:33
int conv_utf_16_to_8(const wchar_t *in16, char *out8, size_t size8)
Definition: utfconv.c:115
#define N_(msgid)
char app_template[64]
Definition: wm_files.c:1021