Blender  V2.93
appdir.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "BLI_fileops.h"
28 #include "BLI_fileops_types.h"
29 #include "BLI_listbase.h"
30 #include "BLI_path_util.h"
31 #include "BLI_string.h"
32 #include "BLI_string_utf8.h"
33 #include "BLI_string_utils.h"
34 #include "BLI_utildefines.h"
35 
36 #include "BKE_appdir.h" /* own include */
37 #include "BKE_blender_version.h"
38 
39 #include "BLT_translation.h"
40 
41 #include "GHOST_Path-api.h"
42 
43 #include "MEM_guardedalloc.h"
44 
45 #include "CLG_log.h"
46 
47 #ifdef WIN32
48 # include "utf_winfunc.h"
49 # include "utfconv.h"
50 # include <io.h>
51 # ifdef _WIN32_IE
52 # undef _WIN32_IE
53 # endif
54 # define _WIN32_IE 0x0501
55 # include "BLI_winstuff.h"
56 # include <shlobj.h>
57 # include <windows.h>
58 #else /* non windows */
59 # ifdef WITH_BINRELOC
60 # include "binreloc.h"
61 # endif
62 /* #mkdtemp on OSX (and probably all *BSD?), not worth making specific check for this OS. */
63 # include <unistd.h>
64 #endif /* WIN32 */
65 
66 static const char _str_null[] = "(null)";
67 #define STR_OR_FALLBACK(a) ((a) ? (a) : _str_null)
68 
69 /* -------------------------------------------------------------------- */
73 /* local */
74 static CLG_LogRef LOG = {"bke.appdir"};
75 
76 static struct {
85 } g_app = {
86  .temp_dirname_session = "",
87 };
88 
91 /* -------------------------------------------------------------------- */
95 #ifndef NDEBUG
96 static bool is_appdir_init = false;
97 # define ASSERT_IS_INIT() BLI_assert(is_appdir_init)
98 #else
99 # define ASSERT_IS_INIT() ((void)0)
100 #endif
101 
111 void BKE_appdir_init(void)
112 {
113 #ifndef NDEBUG
114  BLI_assert(is_appdir_init == false);
115  is_appdir_init = true;
116 #endif
117 }
118 
119 void BKE_appdir_exit(void)
120 {
121 #ifndef NDEBUG
122  BLI_assert(is_appdir_init == true);
123  is_appdir_init = false;
124 #endif
125 }
126 
129 /* -------------------------------------------------------------------- */
136 static char *blender_version_decimal(const int version)
137 {
138  static char version_str[5];
139  BLI_assert(version < 1000);
140  BLI_snprintf(version_str, sizeof(version_str), "%d.%02d", version / 100, version % 100);
141  return version_str;
142 }
143 
146 /* -------------------------------------------------------------------- */
157 const char *BKE_appdir_folder_default(void)
158 {
159 #ifndef WIN32
160  return BLI_getenv("HOME");
161 #else /* Windows */
162  static char documentfolder[MAXPATHLEN];
163 
164  if (BKE_appdir_folder_documents(documentfolder)) {
165  return documentfolder;
166  }
167 
168  return NULL;
169 #endif /* WIN32 */
170 }
171 
175 const char *BKE_appdir_folder_home(void)
176 {
177 #ifndef WIN32
178  return BLI_getenv("HOME");
179 #else /* Windows */
180  return BLI_getenv("userprofile");
181 #endif
182 }
183 
191 {
192  dir[0] = '\0';
193 
194  const char *documents_path = (const char *)GHOST_getUserSpecialDir(
196 
197  /* Usual case: Ghost gave us the documents path. We're done here. */
198  if (documents_path && BLI_is_dir(documents_path)) {
199  BLI_strncpy(dir, documents_path, FILE_MAXDIR);
200  return true;
201  }
202 
203  /* Ghost couldn't give us a documents path, let's try if we can find it ourselves.*/
204 
205  const char *home_path = BKE_appdir_folder_home();
206  if (!home_path || !BLI_is_dir(home_path)) {
207  return false;
208  }
209 
210  char try_documents_path[FILE_MAXDIR];
211  /* Own attempt at getting a valid Documents path. */
212  BLI_path_join(try_documents_path, sizeof(try_documents_path), home_path, N_("Documents"), NULL);
213  if (!BLI_is_dir(try_documents_path)) {
214  return false;
215  }
216 
217  BLI_strncpy(dir, try_documents_path, FILE_MAXDIR);
218  return true;
219 }
220 
225  /* This parameter can only be `const` on non-windows platforms.
226  * NOLINTNEXTLINE: readability-non-const-parameter. */
227  char *dir)
228 {
229  bool success = false;
230 #ifdef WIN32
231  wchar_t wpath[FILE_MAXDIR];
232  success = SHGetSpecialFolderPathW(0, wpath, CSIDL_FONTS, 0);
233  if (success) {
234  wcscat(wpath, L"\\");
236  }
237 #endif
238  /* TODO: Values for other platforms. */
239  UNUSED_VARS(dir);
240  return success;
241 }
242 
245 /* -------------------------------------------------------------------- */
264 static bool test_path(char *targetpath,
265  size_t targetpath_len,
266  const bool check_is_dir,
267  const char *path_base,
268  const char *folder_name,
269  const char *subfolder_name)
270 {
271  ASSERT_IS_INIT();
272 
273  /* Only the last argument should be NULL. */
274  BLI_assert(!(folder_name == NULL && (subfolder_name != NULL)));
275  BLI_path_join(targetpath, targetpath_len, path_base, folder_name, subfolder_name, NULL);
276  if (check_is_dir == false) {
277  CLOG_INFO(&LOG, 3, "using without test: '%s'", targetpath);
278  return true;
279  }
280 
281  if (BLI_is_dir(targetpath)) {
282  CLOG_INFO(&LOG, 3, "found '%s'", targetpath);
283  return true;
284  }
285 
286  CLOG_INFO(&LOG, 3, "missing '%s'", targetpath);
287 
288  /* Path not found, don't accidentally use it,
289  * otherwise call this function with `check_is_dir` set to false. */
290  targetpath[0] = '\0';
291  return false;
292 }
293 
302 static bool test_env_path(char *path, const char *envvar, const bool check_is_dir)
303 {
304  ASSERT_IS_INIT();
305 
306  const char *env_path = envvar ? BLI_getenv(envvar) : NULL;
307  if (!env_path) {
308  return false;
309  }
310 
311  BLI_strncpy(path, env_path, FILE_MAX);
312 
313  if (check_is_dir == false) {
314  CLOG_INFO(&LOG, 3, "using env '%s' without test: '%s'", envvar, env_path);
315  return true;
316  }
317 
318  if (BLI_is_dir(env_path)) {
319  CLOG_INFO(&LOG, 3, "env '%s' found: %s", envvar, env_path);
320  return true;
321  }
322 
323  CLOG_INFO(&LOG, 3, "env '%s' missing: %s", envvar, env_path);
324 
325  /* Path not found, don't accidentally use it,
326  * otherwise call this function with `check_is_dir` set to false. */
327  path[0] = '\0';
328  return false;
329 }
330 
343 static bool get_path_local_ex(char *targetpath,
344  size_t targetpath_len,
345  const char *folder_name,
346  const char *subfolder_name,
347  const int version,
348  const bool check_is_dir)
349 {
350  char relfolder[FILE_MAX];
351 
352  CLOG_INFO(&LOG,
353  3,
354  "folder='%s', subfolder='%s'",
355  STR_OR_FALLBACK(folder_name),
356  STR_OR_FALLBACK(subfolder_name));
357 
358  if (folder_name) { /* `subfolder_name` may be NULL. */
359  BLI_path_join(relfolder, sizeof(relfolder), folder_name, subfolder_name, NULL);
360  }
361  else {
362  relfolder[0] = '\0';
363  }
364 
365  /* Try `{g_app.program_dirname}/2.xx/{folder_name}` the default directory
366  * for a portable distribution. See `WITH_INSTALL_PORTABLE` build-option. */
367  const char *path_base = g_app.program_dirname;
368 #ifdef __APPLE__
369  /* Due new code-sign situation in OSX > 10.9.5
370  * we must move the blender_version dir with contents to Resources. */
371  char osx_resourses[FILE_MAX];
372  BLI_snprintf(osx_resourses, sizeof(osx_resourses), "%s../Resources", g_app.program_dirname);
373  /* Remove the '/../' added above. */
374  BLI_path_normalize(NULL, osx_resourses);
375  path_base = osx_resourses;
376 #endif
377  return test_path(targetpath,
378  targetpath_len,
379  check_is_dir,
380  path_base,
381  blender_version_decimal(version),
382  relfolder);
383 }
384 static bool get_path_local(char *targetpath,
385  size_t targetpath_len,
386  const char *folder_name,
387  const char *subfolder_name)
388 {
389  const int version = BLENDER_VERSION;
390  const bool check_is_dir = true;
391  return get_path_local_ex(
392  targetpath, targetpath_len, folder_name, subfolder_name, version, check_is_dir);
393 }
394 
400 {
401  /* Detect portable install by the existence of `config` folder. */
402  char path[FILE_MAX];
403  return get_path_local(path, sizeof(path), "config", NULL);
404 }
405 
415 static bool get_path_environment_ex(char *targetpath,
416  size_t targetpath_len,
417  const char *subfolder_name,
418  const char *envvar,
419  const bool check_is_dir)
420 {
421  char user_path[FILE_MAX];
422 
423  if (test_env_path(user_path, envvar, check_is_dir)) {
424  /* Note that `subfolder_name` may be NULL, in this case we use `user_path` as-is. */
425  return test_path(targetpath, targetpath_len, check_is_dir, user_path, subfolder_name, NULL);
426  }
427  return false;
428 }
429 static bool get_path_environment(char *targetpath,
430  size_t targetpath_len,
431  const char *subfolder_name,
432  const char *envvar)
433 {
434  const bool check_is_dir = true;
435  return get_path_environment_ex(targetpath, targetpath_len, subfolder_name, envvar, check_is_dir);
436 }
437 
448 static bool get_path_user_ex(char *targetpath,
449  size_t targetpath_len,
450  const char *folder_name,
451  const char *subfolder_name,
452  const int version,
453  const bool check_is_dir)
454 {
455  char user_path[FILE_MAX];
456  const char *user_base_path;
457 
458  /* for portable install, user path is always local */
460  return get_path_local_ex(
461  targetpath, targetpath_len, folder_name, subfolder_name, version, check_is_dir);
462  }
463  user_path[0] = '\0';
464 
465  user_base_path = (const char *)GHOST_getUserDir(version, blender_version_decimal(version));
466  if (user_base_path) {
467  BLI_strncpy(user_path, user_base_path, FILE_MAX);
468  }
469 
470  if (!user_path[0]) {
471  return false;
472  }
473 
474  CLOG_INFO(&LOG,
475  3,
476  "'%s', folder='%s', subfolder='%s'",
477  user_path,
478  STR_OR_FALLBACK(folder_name),
479  STR_OR_FALLBACK(subfolder_name));
480 
481  /* `subfolder_name` may be NULL. */
482  return test_path(
483  targetpath, targetpath_len, check_is_dir, user_path, folder_name, subfolder_name);
484 }
485 static bool get_path_user(char *targetpath,
486  size_t targetpath_len,
487  const char *folder_name,
488  const char *subfolder_name)
489 {
490  const int version = BLENDER_VERSION;
491  const bool check_is_dir = true;
492  return get_path_user_ex(
493  targetpath, targetpath_len, folder_name, subfolder_name, version, check_is_dir);
494 }
495 
506 static bool get_path_system_ex(char *targetpath,
507  size_t targetpath_len,
508  const char *folder_name,
509  const char *subfolder_name,
510  const int version,
511  const bool check_is_dir)
512 {
513  char system_path[FILE_MAX];
514  const char *system_base_path;
515  char relfolder[FILE_MAX];
516 
517  if (folder_name) { /* `subfolder_name` may be NULL. */
518  BLI_path_join(relfolder, sizeof(relfolder), folder_name, subfolder_name, NULL);
519  }
520  else {
521  relfolder[0] = '\0';
522  }
523 
524  system_path[0] = '\0';
525  system_base_path = (const char *)GHOST_getSystemDir(version, blender_version_decimal(version));
526  if (system_base_path) {
527  BLI_strncpy(system_path, system_base_path, FILE_MAX);
528  }
529 
530  if (!system_path[0]) {
531  return false;
532  }
533 
534  CLOG_INFO(&LOG,
535  3,
536  "'%s', folder='%s', subfolder='%s'",
537  system_path,
538  STR_OR_FALLBACK(folder_name),
539  STR_OR_FALLBACK(subfolder_name));
540 
541  /* Try `$BLENDERPATH/folder_name/subfolder_name`, `subfolder_name` may be NULL. */
542  return test_path(
543  targetpath, targetpath_len, check_is_dir, system_path, folder_name, subfolder_name);
544 }
545 
546 static bool get_path_system(char *targetpath,
547  size_t targetpath_len,
548  const char *folder_name,
549  const char *subfolder_name)
550 {
551  const int version = BLENDER_VERSION;
552  const bool check_is_dir = true;
553  return get_path_system_ex(
554  targetpath, targetpath_len, folder_name, subfolder_name, version, check_is_dir);
555 }
556 
559 /* -------------------------------------------------------------------- */
570 bool BKE_appdir_folder_id_ex(const int folder_id,
571  const char *subfolder,
572  char *path,
573  size_t path_len)
574 {
575  switch (folder_id) {
576  case BLENDER_DATAFILES: /* general case */
577  if (get_path_environment(path, path_len, subfolder, "BLENDER_USER_DATAFILES")) {
578  break;
579  }
580  if (get_path_user(path, path_len, "datafiles", subfolder)) {
581  break;
582  }
583  if (get_path_environment(path, path_len, subfolder, "BLENDER_SYSTEM_DATAFILES")) {
584  break;
585  }
586  if (get_path_local(path, path_len, "datafiles", subfolder)) {
587  break;
588  }
589  if (get_path_system(path, path_len, "datafiles", subfolder)) {
590  break;
591  }
592  return false;
593 
595  if (get_path_environment(path, path_len, subfolder, "BLENDER_USER_DATAFILES")) {
596  break;
597  }
598  if (get_path_user(path, path_len, "datafiles", subfolder)) {
599  break;
600  }
601  return false;
602 
604  if (get_path_environment(path, path_len, subfolder, "BLENDER_SYSTEM_DATAFILES")) {
605  break;
606  }
607  if (get_path_system(path, path_len, "datafiles", subfolder)) {
608  break;
609  }
610  if (get_path_local(path, path_len, "datafiles", subfolder)) {
611  break;
612  }
613  return false;
614 
616  if (get_path_environment(path, path_len, subfolder, "BLENDER_USER_DATAFILES")) {
617  break;
618  }
619  if (get_path_user(path, path_len, "autosave", subfolder)) {
620  break;
621  }
622  return false;
623 
624  case BLENDER_USER_CONFIG:
625  if (get_path_environment(path, path_len, subfolder, "BLENDER_USER_CONFIG")) {
626  break;
627  }
628  if (get_path_user(path, path_len, "config", subfolder)) {
629  break;
630  }
631  return false;
632 
634  if (get_path_environment(path, path_len, subfolder, "BLENDER_USER_SCRIPTS")) {
635  break;
636  }
637  if (get_path_user(path, path_len, "scripts", subfolder)) {
638  break;
639  }
640  return false;
641 
643  if (get_path_environment(path, path_len, subfolder, "BLENDER_SYSTEM_SCRIPTS")) {
644  break;
645  }
646  if (get_path_system(path, path_len, "scripts", subfolder)) {
647  break;
648  }
649  if (get_path_local(path, path_len, "scripts", subfolder)) {
650  break;
651  }
652  return false;
653 
655  if (get_path_environment(path, path_len, subfolder, "BLENDER_SYSTEM_PYTHON")) {
656  break;
657  }
658  if (get_path_system(path, path_len, "python", subfolder)) {
659  break;
660  }
661  if (get_path_local(path, path_len, "python", subfolder)) {
662  break;
663  }
664  return false;
665 
666  default:
668  break;
669  }
670 
671  return true;
672 }
673 
674 const char *BKE_appdir_folder_id(const int folder_id, const char *subfolder)
675 {
676  static char path[FILE_MAX] = "";
677  if (BKE_appdir_folder_id_ex(folder_id, subfolder, path, sizeof(path))) {
678  return path;
679  }
680  return NULL;
681 }
682 
686 const char *BKE_appdir_folder_id_user_notest(const int folder_id, const char *subfolder)
687 {
688  const int version = BLENDER_VERSION;
689  static char path[FILE_MAX] = "";
690  const bool check_is_dir = false;
691 
692  switch (folder_id) {
695  path, sizeof(path), subfolder, "BLENDER_USER_DATAFILES", check_is_dir)) {
696  break;
697  }
698  get_path_user_ex(path, sizeof(path), "datafiles", subfolder, version, check_is_dir);
699  break;
700  case BLENDER_USER_CONFIG:
702  path, sizeof(path), subfolder, "BLENDER_USER_CONFIG", check_is_dir)) {
703  break;
704  }
705  get_path_user_ex(path, sizeof(path), "config", subfolder, version, check_is_dir);
706  break;
709  path, sizeof(path), subfolder, "BLENDER_USER_AUTOSAVE", check_is_dir)) {
710  break;
711  }
712  get_path_user_ex(path, sizeof(path), "autosave", subfolder, version, check_is_dir);
713  break;
716  path, sizeof(path), subfolder, "BLENDER_USER_SCRIPTS", check_is_dir)) {
717  break;
718  }
719  get_path_user_ex(path, sizeof(path), "scripts", subfolder, version, check_is_dir);
720  break;
721  default:
723  break;
724  }
725 
726  if ('\0' == path[0]) {
727  return NULL;
728  }
729  return path;
730 }
731 
735 const char *BKE_appdir_folder_id_create(const int folder_id, const char *subfolder)
736 {
737  const char *path;
738 
739  /* Only for user folders. */
740  if (!ELEM(folder_id,
745  return NULL;
746  }
747 
748  path = BKE_appdir_folder_id(folder_id, subfolder);
749 
750  if (!path) {
751  path = BKE_appdir_folder_id_user_notest(folder_id, subfolder);
752  if (path) {
754  }
755  }
756 
757  return path;
758 }
759 
764 const char *BKE_appdir_folder_id_version(const int folder_id,
765  const int version,
766  const bool check_is_dir)
767 {
768  static char path[FILE_MAX] = "";
769  bool ok;
770  switch (folder_id) {
772  ok = get_path_user_ex(path, sizeof(path), NULL, NULL, version, check_is_dir);
773  break;
775  ok = get_path_local_ex(path, sizeof(path), NULL, NULL, version, check_is_dir);
776  break;
778  ok = get_path_system_ex(path, sizeof(path), NULL, NULL, version, check_is_dir);
779  break;
780  default:
781  path[0] = '\0'; /* in case check_is_dir is false */
782  ok = false;
783  BLI_assert(!"incorrect ID");
784  break;
785  }
786  return ok ? path : NULL;
787 }
788 
791 /* -------------------------------------------------------------------- */
809 static void where_am_i(char *fullname, const size_t maxlen, const char *name)
810 {
811 #ifdef WITH_BINRELOC
812  /* Linux uses `binreloc` since `argv[0]` is not reliable, call `br_init(NULL)` first. */
813  {
814  const char *path = NULL;
815  path = br_find_exe(NULL);
816  if (path) {
817  BLI_strncpy(fullname, path, maxlen);
818  free((void *)path);
819  return;
820  }
821  }
822 #endif
823 
824 #ifdef _WIN32
825  {
826  wchar_t *fullname_16 = MEM_mallocN(maxlen * sizeof(wchar_t), "ProgramPath");
827  if (GetModuleFileNameW(0, fullname_16, maxlen)) {
828  conv_utf_16_to_8(fullname_16, fullname, maxlen);
829  if (!BLI_exists(fullname)) {
830  CLOG_ERROR(&LOG, "path can't be found: \"%.*s\"", (int)maxlen, fullname);
831  MessageBox(
832  NULL, "path contains invalid characters or is too long (see console)", "Error", MB_OK);
833  }
834  MEM_freeN(fullname_16);
835  return;
836  }
837 
838  MEM_freeN(fullname_16);
839  }
840 #endif
841 
842  /* Unix and non Linux. */
843  if (name && name[0]) {
844 
845  BLI_strncpy(fullname, name, maxlen);
846  if (name[0] == '.') {
847  BLI_path_abs_from_cwd(fullname, maxlen);
848 #ifdef _WIN32
849  BLI_path_program_extensions_add_win32(fullname, maxlen);
850 #endif
851  }
852  else if (BLI_path_slash_rfind(name)) {
853  /* Full path. */
854  BLI_strncpy(fullname, name, maxlen);
855 #ifdef _WIN32
856  BLI_path_program_extensions_add_win32(fullname, maxlen);
857 #endif
858  }
859  else {
860  BLI_path_program_search(fullname, maxlen, name);
861  }
862  /* Remove "/./" and "/../" so string comparisons can be used on the path. */
863  BLI_path_normalize(NULL, fullname);
864 
865 #if defined(DEBUG)
866  if (!STREQ(name, fullname)) {
867  CLOG_INFO(&LOG, 2, "guessing '%s' == '%s'", name, fullname);
868  }
869 #endif
870  }
871 }
872 
873 void BKE_appdir_program_path_init(const char *argv0)
874 {
875  where_am_i(g_app.program_filename, sizeof(g_app.program_filename), argv0);
876  BLI_split_dir_part(g_app.program_filename, g_app.program_dirname, sizeof(g_app.program_dirname));
877 }
878 
882 const char *BKE_appdir_program_path(void)
883 {
884  BLI_assert(g_app.program_filename[0]);
885  return g_app.program_filename;
886 }
887 
891 const char *BKE_appdir_program_dir(void)
892 {
893  BLI_assert(g_app.program_dirname[0]);
894  return g_app.program_dirname;
895 }
896 
898  const size_t fullpath_len,
899  const int version_major,
900  const int version_minor)
901 {
902  ASSERT_IS_INIT();
903 
904 #ifdef PYTHON_EXECUTABLE_NAME
905  /* Passed in from the build-systems 'PYTHON_EXECUTABLE'. */
906  const char *python_build_def = STRINGIFY(PYTHON_EXECUTABLE_NAME);
907 #endif
908  const char *basename = "python";
909 #if defined(WIN32) && !defined(NDEBUG)
910  const char *basename_debug = "python_d";
911 #endif
912  char python_version[16];
913  /* Check both possible names. */
914  const char *python_names[] = {
915 #ifdef PYTHON_EXECUTABLE_NAME
916  python_build_def,
917 #endif
918 #if defined(WIN32) && !defined(NDEBUG)
919  basename_debug,
920 #endif
921  python_version,
922  basename,
923  };
924  bool is_found = false;
925 
926  SNPRINTF(python_version, "%s%d.%d", basename, version_major, version_minor);
927 
928  {
929  const char *python_bin_dir = BKE_appdir_folder_id(BLENDER_SYSTEM_PYTHON, "bin");
930  if (python_bin_dir) {
931 
932  for (int i = 0; i < ARRAY_SIZE(python_names); i++) {
933  BLI_join_dirfile(fullpath, fullpath_len, python_bin_dir, python_names[i]);
934 
935  if (
936 #ifdef _WIN32
937  BLI_path_program_extensions_add_win32(fullpath, fullpath_len)
938 #else
939  BLI_exists(fullpath)
940 #endif
941  ) {
942  is_found = true;
943  break;
944  }
945  }
946  }
947  }
948 
949  if (is_found == false) {
950  for (int i = 0; i < ARRAY_SIZE(python_names); i++) {
951  if (BLI_path_program_search(fullpath, fullpath_len, python_names[i])) {
952  is_found = true;
953  break;
954  }
955  }
956  }
957 
958  if (is_found == false) {
959  *fullpath = '\0';
960  }
961 
962  return is_found;
963 }
964 
967 /* -------------------------------------------------------------------- */
972 static const char *app_template_directory_search[2] = {
973  "startup" SEP_STR "bl_app_templates_user",
974  "startup" SEP_STR "bl_app_templates_system",
975 };
976 
977 static const int app_template_directory_id[2] = {
978  /* Only 'USER' */
980  /* Covers 'LOCAL' & 'SYSTEM'. */
982 };
983 
988 {
989  char temp_dir[FILE_MAX];
990  for (int i = 0; i < ARRAY_SIZE(app_template_directory_id); i++) {
993  temp_dir,
994  sizeof(temp_dir))) {
995  return true;
996  }
997  }
998  return false;
999 }
1000 
1001 bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_len)
1002 {
1003  for (int i = 0; i < ARRAY_SIZE(app_template_directory_id); i++) {
1004  char subdir[FILE_MAX];
1005  BLI_join_dirfile(subdir, sizeof(subdir), app_template_directory_search[i], app_template);
1006  if (BKE_appdir_folder_id_ex(app_template_directory_id[i], subdir, path, path_len)) {
1007  return true;
1008  }
1009  }
1010  return false;
1011 }
1012 
1014 {
1015  /* Test if app template provides a `userpref.blend`.
1016  * If not, we will share user preferences with the rest of Blender. */
1017  if (app_template[0] == '\0') {
1018  return false;
1019  }
1020 
1021  char app_template_path[FILE_MAX];
1023  app_template, app_template_path, sizeof(app_template_path))) {
1024  return false;
1025  }
1026 
1027  char userpref_path[FILE_MAX];
1028  BLI_path_join(
1029  userpref_path, sizeof(userpref_path), app_template_path, BLENDER_USERPREF_FILE, NULL);
1030  return BLI_exists(userpref_path);
1031 }
1032 
1034 {
1035  BLI_listbase_clear(templates);
1036 
1037  for (int i = 0; i < ARRAY_SIZE(app_template_directory_id); i++) {
1038  char subdir[FILE_MAX];
1041  subdir,
1042  sizeof(subdir))) {
1043  continue;
1044  }
1045 
1046  struct direntry *dir;
1047  uint totfile = BLI_filelist_dir_contents(subdir, &dir);
1048  for (int f = 0; f < totfile; f++) {
1049  if (!FILENAME_IS_CURRPAR(dir[f].relname) && S_ISDIR(dir[f].type)) {
1050  char *template = BLI_strdup(dir[f].relname);
1051  BLI_addtail(templates, BLI_genericNodeN(template));
1052  }
1053  }
1054 
1055  BLI_filelist_free(dir, totfile);
1056  }
1057 }
1058 
1061 /* -------------------------------------------------------------------- */
1076 static void where_is_temp(char *tempdir, const size_t tempdir_len, const char *userdir)
1077 {
1078 
1079  tempdir[0] = '\0';
1080 
1081  if (userdir && BLI_is_dir(userdir)) {
1082  BLI_strncpy(tempdir, userdir, tempdir_len);
1083  }
1084 
1085  if (tempdir[0] == '\0') {
1086  const char *env_vars[] = {
1087 #ifdef WIN32
1088  "TEMP",
1089 #else
1090  /* Non standard (could be removed). */
1091  "TMP",
1092  /* Posix standard. */
1093  "TMPDIR",
1094 #endif
1095  };
1096  for (int i = 0; i < ARRAY_SIZE(env_vars); i++) {
1097  const char *tmp = BLI_getenv(env_vars[i]);
1098  if (tmp && (tmp[0] != '\0') && BLI_is_dir(tmp)) {
1099  BLI_strncpy(tempdir, tmp, tempdir_len);
1100  break;
1101  }
1102  }
1103  }
1104 
1105  if (tempdir[0] == '\0') {
1106  BLI_strncpy(tempdir, "/tmp/", tempdir_len);
1107  }
1108  else {
1109  /* add a trailing slash if needed */
1110  BLI_path_slash_ensure(tempdir);
1111  }
1112 }
1113 
1114 static void tempdir_session_create(char *tempdir_session,
1115  const size_t tempdir_session_len,
1116  const char *tempdir)
1117 {
1118  tempdir_session[0] = '\0';
1119 
1120  const int tempdir_len = strlen(tempdir);
1121  /* 'XXXXXX' is kind of tag to be replaced by `mktemp-family` by an UUID. */
1122  const char *session_name = "blender_XXXXXX";
1123  const int session_name_len = strlen(session_name);
1124 
1125  /* +1 as a slash is added,
1126  * #_mktemp_s also requires the last null character is included. */
1127  const int tempdir_session_len_required = tempdir_len + session_name_len + 1;
1128 
1129  if (tempdir_session_len_required <= tempdir_session_len) {
1130  /* No need to use path joining utility as we know the last character of #tempdir is a slash. */
1131  BLI_string_join(tempdir_session, tempdir_session_len, tempdir, session_name);
1132 #ifdef WIN32
1133  const bool needs_create = (_mktemp_s(tempdir_session, tempdir_session_len_required) == 0);
1134 #else
1135  const bool needs_create = (mkdtemp(tempdir_session) == NULL);
1136 #endif
1137  if (needs_create) {
1138  BLI_dir_create_recursive(tempdir_session);
1139  }
1140  if (BLI_is_dir(tempdir_session)) {
1141  BLI_path_slash_ensure(tempdir_session);
1142  /* Success. */
1143  return;
1144  }
1145  }
1146 
1147  CLOG_WARN(&LOG,
1148  "Could not generate a temp file name for '%s', falling back to '%s'",
1149  tempdir_session,
1150  tempdir);
1151  BLI_strncpy(tempdir_session, tempdir, tempdir_session_len);
1152 }
1153 
1160 void BKE_tempdir_init(const char *userdir)
1161 {
1162  where_is_temp(g_app.temp_dirname_base, sizeof(g_app.temp_dirname_base), userdir);
1163 
1164  /* Clear existing temp dir, if needed. */
1166  /* Now that we have a valid temp dir, add system-generated unique sub-dir. */
1168  g_app.temp_dirname_session, sizeof(g_app.temp_dirname_session), g_app.temp_dirname_base);
1169 }
1170 
1174 const char *BKE_tempdir_session(void)
1175 {
1176  return g_app.temp_dirname_session[0] ? g_app.temp_dirname_session : BKE_tempdir_base();
1177 }
1178 
1182 const char *BKE_tempdir_base(void)
1183 {
1184  return g_app.temp_dirname_base;
1185 }
1186 
1191 {
1192  if (g_app.temp_dirname_session[0] && BLI_is_dir(g_app.temp_dirname_session)) {
1193  BLI_delete(g_app.temp_dirname_session, true, true);
1194  }
1195 }
1196 
@ BLENDER_USER_DATAFILES
Definition: BKE_appdir.h:82
@ BLENDER_SYSTEM_DATAFILES
Definition: BKE_appdir.h:87
@ BLENDER_DATAFILES
Definition: BKE_appdir.h:78
@ BLENDER_SYSTEM_PYTHON
Definition: BKE_appdir.h:89
@ BLENDER_SYSTEM_SCRIPTS
Definition: BKE_appdir.h:88
@ BLENDER_USER_AUTOSAVE
Definition: BKE_appdir.h:84
@ BLENDER_USER_CONFIG
Definition: BKE_appdir.h:81
@ BLENDER_USER_SCRIPTS
Definition: BKE_appdir.h:83
#define BLENDER_USERPREF_FILE
Definition: BKE_appdir.h:100
@ BLENDER_RESOURCE_PATH_SYSTEM
Definition: BKE_appdir.h:96
@ BLENDER_RESOURCE_PATH_LOCAL
Definition: BKE_appdir.h:95
@ BLENDER_RESOURCE_PATH_USER
Definition: BKE_appdir.h:94
#define BLENDER_VERSION
#define BLI_assert_unreachable()
Definition: BLI_assert.h:96
#define BLI_assert(a)
Definition: BLI_assert.h:58
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:349
void BLI_filelist_free(struct direntry *filelist, const unsigned int nrentries)
Definition: BLI_filelist.c:467
int BLI_delete(const char *file, bool dir, bool recursive) ATTR_NONNULL()
Definition: fileops.c:1037
unsigned int BLI_filelist_dir_contents(const char *dir, struct direntry **r_filelist)
Definition: BLI_filelist.c:238
bool BLI_is_dir(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:436
bool BLI_dir_create_recursive(const char *dir) ATTR_NONNULL()
Definition: fileops.c:1329
Some types for dealing with directories.
void BLI_kdtree_nd_() free(KDTree *tree)
Definition: kdtree_impl.h:116
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
Definition: BLI_listbase.h:128
struct LinkData * BLI_genericNodeN(void *data)
Definition: listbase.c:923
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:110
void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen)
Definition: path_util.c:1682
bool BLI_path_program_search(char *fullname, const size_t maxlen, const char *name)
Definition: path_util.c:1218
#define FILE_MAX
void BLI_path_normalize(const char *relabase, char *path) ATTR_NONNULL(2)
Definition: path_util.c:173
bool BLI_path_abs_from_cwd(char *path, const size_t maxlen) ATTR_NONNULL()
Definition: path_util.c:1128
void BLI_join_dirfile(char *__restrict dst, const size_t maxlen, const char *__restrict dir, const char *__restrict file) ATTR_NONNULL()
Definition: path_util.c:1737
#define FILENAME_IS_CURRPAR(_n)
size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *path_first,...) ATTR_NONNULL(1
const char * BLI_getenv(const char *env) ATTR_NONNULL(1)
Definition: path_util.c:1313
int BLI_path_slash_ensure(char *string) ATTR_NONNULL()
Definition: path_util.c:1981
const char * BLI_path_slash_rfind(const char *string) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT
Definition: path_util.c:1962
#define FILE_MAXDIR
#define SNPRINTF(dst, format,...)
Definition: BLI_string.h:165
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC
Definition: string.c:70
size_t BLI_snprintf(char *__restrict dst, size_t maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy) ATTR_NONNULL()
Definition: string.c:108
size_t BLI_strncpy_wchar_as_utf8(char *__restrict dst, const wchar_t *__restrict src, const size_t maxncpy) ATTR_NONNULL()
Definition: string_utf8.c:295
#define BLI_string_join(result, result_len,...)
unsigned int uint
Definition: BLI_sys_types.h:83
#define ARRAY_SIZE(arr)
#define UNUSED_VARS(...)
#define STRINGIFY(x)
#define ELEM(...)
#define STREQ(a, b)
Compatibility-like things for windows.
#define S_ISDIR(x)
Definition: BLI_winstuff.h:64
#define MAXPATHLEN
Definition: BLI_winstuff.h:58
#define N_(msgid)
#define CLOG_ERROR(clg_ref,...)
Definition: CLG_log.h:204
#define CLOG_WARN(clg_ref,...)
Definition: CLG_log.h:203
#define CLOG_INFO(clg_ref, level,...)
Definition: CLG_log.h:201
const GHOST_TUns8 * GHOST_getUserDir(int version, const char *versionstr)
const GHOST_TUns8 * GHOST_getUserSpecialDir(GHOST_TUserSpecialDirTypes type)
const GHOST_TUns8 * GHOST_getSystemDir(int version, const char *versionstr)
@ GHOST_kUserSpecialDirDocuments
Definition: GHOST_Types.h:570
_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:987
static struct @78 g_app
#define ASSERT_IS_INIT()
Definition: appdir.c:97
static const char _str_null[]
Definition: appdir.c:66
const char * BKE_appdir_folder_id_version(const int folder_id, const int version, const bool check_is_dir)
Definition: appdir.c:764
const char * BKE_appdir_folder_id_create(const int folder_id, const char *subfolder)
Definition: appdir.c:735
static const int app_template_directory_id[2]
Definition: appdir.c:977
void BKE_tempdir_init(const char *userdir)
Definition: appdir.c:1160
static bool is_appdir_init
Definition: appdir.c:96
static bool get_path_user(char *targetpath, size_t targetpath_len, const char *folder_name, const char *subfolder_name)
Definition: appdir.c:485
char program_dirname[FILE_MAX]
Definition: appdir.c:80
bool BKE_appdir_font_folder_default(char *dir)
Definition: appdir.c:224
static void where_am_i(char *fullname, const size_t maxlen, const char *name)
Definition: appdir.c:809
const char * BKE_appdir_folder_default(void)
Definition: appdir.c:157
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:506
static char * blender_version_decimal(const int version)
Definition: appdir.c:136
bool BKE_appdir_app_is_portable_install(void)
Definition: appdir.c:399
char temp_dirname_session[FILE_MAX]
Definition: appdir.c:84
bool BKE_appdir_program_python_search(char *fullpath, const size_t fullpath_len, const int version_major, const int version_minor)
Definition: appdir.c:897
bool BKE_appdir_folder_documents(char *dir)
Definition: appdir.c:190
static bool test_env_path(char *path, const char *envvar, const bool check_is_dir)
Definition: appdir.c:302
void BKE_appdir_init(void)
Definition: appdir.c:111
bool BKE_appdir_app_template_has_userpref(const char *app_template)
Definition: appdir.c:1013
void BKE_appdir_exit(void)
Definition: appdir.c:119
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:448
void BKE_tempdir_session_purge(void)
Definition: appdir.c:1190
static bool get_path_local(char *targetpath, size_t targetpath_len, const char *folder_name, const char *subfolder_name)
Definition: appdir.c:384
const char * BKE_appdir_folder_id(const int folder_id, const char *subfolder)
Definition: appdir.c:674
static const char * app_template_directory_search[2]
Definition: appdir.c:972
bool BKE_appdir_folder_id_ex(const int folder_id, const char *subfolder, char *path, size_t path_len)
Definition: appdir.c:570
char program_filename[FILE_MAX]
Definition: appdir.c:78
void BKE_appdir_program_path_init(const char *argv0)
Definition: appdir.c:873
static void tempdir_session_create(char *tempdir_session, const size_t tempdir_session_len, const char *tempdir)
Definition: appdir.c:1114
const char * BKE_appdir_folder_home(void)
Definition: appdir.c:175
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:343
const char * BKE_tempdir_session(void)
Definition: appdir.c:1174
#define STR_OR_FALLBACK(a)
Definition: appdir.c:67
bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_len)
Definition: appdir.c:1001
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:415
const char * BKE_tempdir_base(void)
Definition: appdir.c:1182
static CLG_LogRef LOG
Definition: appdir.c:74
static void where_is_temp(char *tempdir, const size_t tempdir_len, const char *userdir)
Definition: appdir.c:1076
static bool get_path_system(char *targetpath, size_t targetpath_len, const char *folder_name, const char *subfolder_name)
Definition: appdir.c:546
void BKE_appdir_app_templates(ListBase *templates)
Definition: appdir.c:1033
const char * BKE_appdir_program_path(void)
Definition: appdir.c:882
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:264
const char * BKE_appdir_program_dir(void)
Definition: appdir.c:891
const char * BKE_appdir_folder_id_user_notest(const int folder_id, const char *subfolder)
Definition: appdir.c:686
char temp_dirname_base[FILE_MAX]
Definition: appdir.c:82
static bool get_path_environment(char *targetpath, size_t targetpath_len, const char *subfolder_name, const char *envvar)
Definition: appdir.c:429
static char * basename(char *string)
Definition: datatoc.c:33
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
#define L
const char * relname
#define SEP_STR
Definition: unit.c:47
int conv_utf_16_to_8(const wchar_t *in16, char *out8, size_t size8)
Definition: utfconv.c:127
char app_template[64]
Definition: wm_files.c:853