Cleanup: style, use braces for blenkernel
[blender.git] / source / blender / blenkernel / intern / appdir.c
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
17 /** \file
18  * \ingroup bke
19  *
20  * Access to application level directories.
21  */
22
23 #include <stdlib.h>
24 #include <string.h>
25 #include <stdio.h>
26
27 #include "BLI_utildefines.h"
28 #include "BLI_fileops.h"
29 #include "BLI_fileops_types.h"
30 #include "BLI_listbase.h"
31 #include "BLI_path_util.h"
32 #include "BLI_string.h"
33
34 #include "BKE_blender_version.h"
35 #include "BKE_appdir.h" /* own include */
36
37 #include "GHOST_Path-api.h"
38
39 #include "MEM_guardedalloc.h"
40
41 #include "CLG_log.h"
42
43 #ifdef WIN32
44 #  include "utf_winfunc.h"
45 #  include "utfconv.h"
46 #  include <io.h>
47 #  ifdef _WIN32_IE
48 #    undef _WIN32_IE
49 #  endif
50 #  define _WIN32_IE 0x0501
51 #  include <windows.h>
52 #  include <shlobj.h>
53 #  include "BLI_winstuff.h"
54 #else /* non windows */
55 #  ifdef WITH_BINRELOC
56 #    include "binreloc.h"
57 #  endif
58 #  include <unistd.h> /* mkdtemp on OSX (and probably all *BSD?), not worth making specific check for this OS. */
59 #endif                /* WIN32 */
60
61 /* local */
62 static CLG_LogRef LOG = {"bke.appdir"};
63 static char bprogname[FILE_MAX];     /* full path to program executable */
64 static char bprogdir[FILE_MAX];      /* full path to directory in which executable is located */
65 static char btempdir_base[FILE_MAX]; /* persistent temporary directory */
66 static char btempdir_session[FILE_MAX] = ""; /* volatile temporary directory */
67
68 /* This is now only used to really get the user's default document folder */
69 /* On Windows I chose the 'Users/<MyUserName>/Documents' since it's used
70  * as default location to save documents */
71 const char *BKE_appdir_folder_default(void)
72 {
73 #ifndef WIN32
74   const char *const xdg_documents_dir = BLI_getenv("XDG_DOCUMENTS_DIR");
75
76   if (xdg_documents_dir) {
77     return xdg_documents_dir;
78   }
79
80   return BLI_getenv("HOME");
81 #else  /* Windows */
82   static char documentfolder[MAXPATHLEN];
83   HRESULT hResult;
84
85   /* Check for %HOME% env var */
86   if (uput_getenv("HOME", documentfolder, MAXPATHLEN)) {
87     if (BLI_is_dir(documentfolder)) {
88       return documentfolder;
89     }
90   }
91
92   /* add user profile support for WIN 2K / NT.
93    * This is %APPDATA%, which translates to either
94    * %USERPROFILE%\Application Data or since Vista
95    * to %USERPROFILE%\AppData\Roaming
96    */
97   hResult = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, documentfolder);
98
99   if (hResult == S_OK) {
100     if (BLI_is_dir(documentfolder)) {
101       return documentfolder;
102     }
103   }
104
105   return NULL;
106 #endif /* WIN32 */
107 }
108
109 // #define PATH_DEBUG
110
111 /* returns a formatted representation of the specified version number. Non-reentrant! */
112 static char *blender_version_decimal(const int ver)
113 {
114   static char version_str[5];
115   BLI_assert(ver < 1000);
116   BLI_snprintf(version_str, sizeof(version_str), "%d.%02d", ver / 100, ver % 100);
117   return version_str;
118 }
119
120 /**
121  * Concatenates path_base, (optional) path_sep and (optional) folder_name into targetpath,
122  * returning true if result points to a directory.
123  */
124 static bool test_path(char *targetpath,
125                       size_t targetpath_len,
126                       const char *path_base,
127                       const char *path_sep,
128                       const char *folder_name)
129 {
130   char tmppath[FILE_MAX];
131
132   if (path_sep) {
133     BLI_join_dirfile(tmppath, sizeof(tmppath), path_base, path_sep);
134   }
135   else {
136     BLI_strncpy(tmppath, path_base, sizeof(tmppath));
137   }
138
139   /* rare cases folder_name is omitted (when looking for ~/.config/blender/2.xx dir only) */
140   if (folder_name) {
141     BLI_join_dirfile(targetpath, targetpath_len, tmppath, folder_name);
142   }
143   else {
144     BLI_strncpy(targetpath, tmppath, targetpath_len);
145   }
146   /* FIXME: why is "//" on front of tmppath expanded to "/" (by BLI_join_dirfile)
147    * if folder_name is specified but not otherwise? */
148
149   if (BLI_is_dir(targetpath)) {
150 #ifdef PATH_DEBUG
151     printf("\t%s found: %s\n", __func__, targetpath);
152 #endif
153     return true;
154   }
155   else {
156 #ifdef PATH_DEBUG
157     printf("\t%s missing: %s\n", __func__, targetpath);
158 #endif
159     //targetpath[0] = '\0';
160     return false;
161   }
162 }
163
164 /**
165  * Puts the value of the specified environment variable into *path if it exists
166  * and points at a directory. Returns true if this was done.
167  */
168 static bool test_env_path(char *path, const char *envvar)
169 {
170   const char *env = envvar ? BLI_getenv(envvar) : NULL;
171   if (!env) {
172     return false;
173   }
174
175   if (BLI_is_dir(env)) {
176     BLI_strncpy(path, env, FILE_MAX);
177 #ifdef PATH_DEBUG
178     printf("\t%s env %s found: %s\n", __func__, envvar, env);
179 #endif
180     return true;
181   }
182   else {
183     path[0] = '\0';
184 #ifdef PATH_DEBUG
185     printf("\t%s env %s missing: %s\n", __func__, envvar, env);
186 #endif
187     return false;
188   }
189 }
190
191 /**
192  * Constructs in \a targetpath the name of a directory relative to a version-specific
193  * subdirectory in the parent directory of the Blender executable.
194  *
195  * \param targetpath: String to return path
196  * \param folder_name: Optional folder name within version-specific directory
197  * \param subfolder_name: Optional subfolder name within folder_name
198  * \param ver: To construct name of version-specific directory within bprogdir
199  * \return true if such a directory exists.
200  */
201 static bool get_path_local(char *targetpath,
202                            size_t targetpath_len,
203                            const char *folder_name,
204                            const char *subfolder_name,
205                            const int ver)
206 {
207   char relfolder[FILE_MAX];
208
209 #ifdef PATH_DEBUG
210   printf("%s...\n", __func__);
211 #endif
212
213   if (folder_name) {
214     if (subfolder_name) {
215       BLI_join_dirfile(relfolder, sizeof(relfolder), folder_name, subfolder_name);
216     }
217     else {
218       BLI_strncpy(relfolder, folder_name, sizeof(relfolder));
219     }
220   }
221   else {
222     relfolder[0] = '\0';
223   }
224
225   /* try EXECUTABLE_DIR/2.5x/folder_name - new default directory for local blender installed files */
226 #ifdef __APPLE__
227   /* due new codesign situation in OSX > 10.9.5 we must move the blender_version dir with contents to Resources */
228   char osx_resourses[FILE_MAX];
229   BLI_snprintf(osx_resourses, sizeof(osx_resourses), "%s../Resources", bprogdir);
230   /* Remove the '/../' added above. */
231   BLI_cleanup_path(NULL, osx_resourses);
232   return test_path(
233       targetpath, targetpath_len, osx_resourses, blender_version_decimal(ver), relfolder);
234 #else
235   return test_path(targetpath, targetpath_len, bprogdir, blender_version_decimal(ver), relfolder);
236 #endif
237 }
238
239 /**
240  * Is this an install with user files kept together with the Blender executable and its
241  * installation files.
242  */
243 bool BKE_appdir_app_is_portable_install(void)
244 {
245   /* detect portable install by the existence of config folder */
246   const int ver = BLENDER_VERSION;
247   char path[FILE_MAX];
248
249   return get_path_local(path, sizeof(path), "config", NULL, ver);
250 }
251
252 /**
253  * Returns the path of a folder from environment variables
254  *
255  * \param targetpath: String to return path.
256  * \param subfolder_name: optional name of subfolder within folder.
257  * \param envvar: name of environment variable to check folder_name.
258  * \return true if it was able to construct such a path and the path exists.
259  */
260 static bool get_path_environment(char *targetpath,
261                                  size_t targetpath_len,
262                                  const char *subfolder_name,
263                                  const char *envvar)
264 {
265   char user_path[FILE_MAX];
266
267   if (test_env_path(user_path, envvar)) {
268     if (subfolder_name) {
269       return test_path(targetpath, targetpath_len, user_path, NULL, subfolder_name);
270     }
271     else {
272       BLI_strncpy(targetpath, user_path, FILE_MAX);
273       return true;
274     }
275   }
276   return false;
277 }
278
279 /**
280  * Returns the path of a folder from environment variables
281  *
282  * \param targetpath: String to return path.
283  * \param subfolder_name: optional name of subfolder within folder.
284  * \param envvar: name of environment variable to check folder_name.
285  * \return true if it was able to construct such a path.
286  */
287 static bool get_path_environment_notest(char *targetpath,
288                                         size_t targetpath_len,
289                                         const char *subfolder_name,
290                                         const char *envvar)
291 {
292   char user_path[FILE_MAX];
293
294   if (test_env_path(user_path, envvar)) {
295     if (subfolder_name) {
296       BLI_join_dirfile(targetpath, targetpath_len, user_path, subfolder_name);
297       return true;
298     }
299     else {
300       BLI_strncpy(targetpath, user_path, FILE_MAX);
301       return true;
302     }
303   }
304   return false;
305 }
306
307 /**
308  * Returns the path of a folder within the user-files area.
309  * \param targetpath: String to return path
310  * \param folder_name: default name of folder within user area
311  * \param subfolder_name: optional name of subfolder within folder
312  * \param ver: Blender version, used to construct a subdirectory name
313  * \return true if it was able to construct such a path.
314  */
315 static bool get_path_user(char *targetpath,
316                           size_t targetpath_len,
317                           const char *folder_name,
318                           const char *subfolder_name,
319                           const int ver)
320 {
321   char user_path[FILE_MAX];
322   const char *user_base_path;
323
324   /* for portable install, user path is always local */
325   if (BKE_appdir_app_is_portable_install()) {
326     return get_path_local(targetpath, targetpath_len, folder_name, subfolder_name, ver);
327   }
328   user_path[0] = '\0';
329
330   user_base_path = (const char *)GHOST_getUserDir(ver, blender_version_decimal(ver));
331   if (user_base_path) {
332     BLI_strncpy(user_path, user_base_path, FILE_MAX);
333   }
334
335   if (!user_path[0]) {
336     return false;
337   }
338
339 #ifdef PATH_DEBUG
340   printf("%s: %s\n", __func__, user_path);
341 #endif
342
343   if (subfolder_name) {
344     return test_path(targetpath, targetpath_len, user_path, folder_name, subfolder_name);
345   }
346   else {
347     return test_path(targetpath, targetpath_len, user_path, NULL, folder_name);
348   }
349 }
350
351 /**
352  * Returns the path of a folder within the Blender installation directory.
353  *
354  * \param targetpath: String to return path
355  * \param folder_name: default name of folder within installation area
356  * \param subfolder_name: optional name of subfolder within folder
357  * \param ver: Blender version, used to construct a subdirectory name
358  * \return  true if it was able to construct such a path.
359  */
360 static bool get_path_system(char *targetpath,
361                             size_t targetpath_len,
362                             const char *folder_name,
363                             const char *subfolder_name,
364                             const int ver)
365 {
366   char system_path[FILE_MAX];
367   const char *system_base_path;
368   char relfolder[FILE_MAX];
369
370   if (folder_name) {
371     if (subfolder_name) {
372       BLI_join_dirfile(relfolder, sizeof(relfolder), folder_name, subfolder_name);
373     }
374     else {
375       BLI_strncpy(relfolder, folder_name, sizeof(relfolder));
376     }
377   }
378   else {
379     relfolder[0] = '\0';
380   }
381
382   system_path[0] = '\0';
383   system_base_path = (const char *)GHOST_getSystemDir(ver, blender_version_decimal(ver));
384   if (system_base_path) {
385     BLI_strncpy(system_path, system_base_path, FILE_MAX);
386   }
387
388   if (!system_path[0]) {
389     return false;
390   }
391
392 #ifdef PATH_DEBUG
393   printf("%s: %s\n", __func__, system_path);
394 #endif
395
396   if (subfolder_name) {
397     /* try $BLENDERPATH/folder_name/subfolder_name */
398     return test_path(targetpath, targetpath_len, system_path, folder_name, subfolder_name);
399   }
400   else {
401     /* try $BLENDERPATH/folder_name */
402     return test_path(targetpath, targetpath_len, system_path, NULL, folder_name);
403   }
404 }
405
406 /**
407  * Get a folder out of the 'folder_id' presets for paths.
408  * returns the path if found, NULL string if not
409  *
410  * \param subfolder: The name of a directory to check for,
411  * this may contain path separators but must resolve to a directory, checked with #BLI_is_dir.
412  */
413 const char *BKE_appdir_folder_id_ex(const int folder_id,
414                                     const char *subfolder,
415                                     char *path,
416                                     size_t path_len)
417 {
418   const int ver = BLENDER_VERSION;
419
420   switch (folder_id) {
421     case BLENDER_DATAFILES: /* general case */
422       if (get_path_environment(path, path_len, subfolder, "BLENDER_USER_DATAFILES")) {
423         break;
424       }
425       if (get_path_user(path, path_len, "datafiles", subfolder, ver)) {
426         break;
427       }
428       if (get_path_environment(path, path_len, subfolder, "BLENDER_SYSTEM_DATAFILES")) {
429         break;
430       }
431       if (get_path_local(path, path_len, "datafiles", subfolder, ver)) {
432         break;
433       }
434       if (get_path_system(path, path_len, "datafiles", subfolder, ver)) {
435         break;
436       }
437       return NULL;
438
439     case BLENDER_USER_DATAFILES:
440       if (get_path_environment(path, path_len, subfolder, "BLENDER_USER_DATAFILES")) {
441         break;
442       }
443       if (get_path_user(path, path_len, "datafiles", subfolder, ver)) {
444         break;
445       }
446       return NULL;
447
448     case BLENDER_SYSTEM_DATAFILES:
449       if (get_path_environment(path, path_len, subfolder, "BLENDER_SYSTEM_DATAFILES")) {
450         break;
451       }
452       if (get_path_system(path, path_len, "datafiles", subfolder, ver)) {
453         break;
454       }
455       if (get_path_local(path, path_len, "datafiles", subfolder, ver)) {
456         break;
457       }
458       return NULL;
459
460     case BLENDER_USER_AUTOSAVE:
461       if (get_path_environment(path, path_len, subfolder, "BLENDER_USER_DATAFILES")) {
462         break;
463       }
464       if (get_path_user(path, path_len, "autosave", subfolder, ver)) {
465         break;
466       }
467       return NULL;
468
469     case BLENDER_USER_CONFIG:
470       if (get_path_environment(path, path_len, subfolder, "BLENDER_USER_CONFIG")) {
471         break;
472       }
473       if (get_path_user(path, path_len, "config", subfolder, ver)) {
474         break;
475       }
476       return NULL;
477
478     case BLENDER_USER_SCRIPTS:
479       if (get_path_environment(path, path_len, subfolder, "BLENDER_USER_SCRIPTS")) {
480         break;
481       }
482       if (get_path_user(path, path_len, "scripts", subfolder, ver)) {
483         break;
484       }
485       return NULL;
486
487     case BLENDER_SYSTEM_SCRIPTS:
488       if (get_path_environment(path, path_len, subfolder, "BLENDER_SYSTEM_SCRIPTS")) {
489         break;
490       }
491       if (get_path_system(path, path_len, "scripts", subfolder, ver)) {
492         break;
493       }
494       if (get_path_local(path, path_len, "scripts", subfolder, ver)) {
495         break;
496       }
497       return NULL;
498
499     case BLENDER_SYSTEM_PYTHON:
500       if (get_path_environment(path, path_len, subfolder, "BLENDER_SYSTEM_PYTHON")) {
501         break;
502       }
503       if (get_path_system(path, path_len, "python", subfolder, ver)) {
504         break;
505       }
506       if (get_path_local(path, path_len, "python", subfolder, ver)) {
507         break;
508       }
509       return NULL;
510
511     default:
512       BLI_assert(0);
513       break;
514   }
515
516   return path;
517 }
518
519 const char *BKE_appdir_folder_id(const int folder_id, const char *subfolder)
520 {
521   static char path[FILE_MAX] = "";
522   return BKE_appdir_folder_id_ex(folder_id, subfolder, path, sizeof(path));
523 }
524
525 /**
526  * Returns the path to a folder in the user area without checking that it actually exists first.
527  */
528 const char *BKE_appdir_folder_id_user_notest(const int folder_id, const char *subfolder)
529 {
530   const int ver = BLENDER_VERSION;
531   static char path[FILE_MAX] = "";
532
533   switch (folder_id) {
534     case BLENDER_USER_DATAFILES:
535       if (get_path_environment_notest(path, sizeof(path), subfolder, "BLENDER_USER_DATAFILES")) {
536         break;
537       }
538       get_path_user(path, sizeof(path), "datafiles", subfolder, ver);
539       break;
540     case BLENDER_USER_CONFIG:
541       if (get_path_environment_notest(path, sizeof(path), subfolder, "BLENDER_USER_CONFIG")) {
542         break;
543       }
544       get_path_user(path, sizeof(path), "config", subfolder, ver);
545       break;
546     case BLENDER_USER_AUTOSAVE:
547       if (get_path_environment_notest(path, sizeof(path), subfolder, "BLENDER_USER_AUTOSAVE")) {
548         break;
549       }
550       get_path_user(path, sizeof(path), "autosave", subfolder, ver);
551       break;
552     case BLENDER_USER_SCRIPTS:
553       if (get_path_environment_notest(path, sizeof(path), subfolder, "BLENDER_USER_SCRIPTS")) {
554         break;
555       }
556       get_path_user(path, sizeof(path), "scripts", subfolder, ver);
557       break;
558     default:
559       BLI_assert(0);
560       break;
561   }
562
563   if ('\0' == path[0]) {
564     return NULL;
565   }
566   return path;
567 }
568
569 /**
570  * Returns the path to a folder in the user area, creating it if it doesn't exist.
571  */
572 const char *BKE_appdir_folder_id_create(const int folder_id, const char *subfolder)
573 {
574   const char *path;
575
576   /* only for user folders */
577   if (!ELEM(folder_id,
578             BLENDER_USER_DATAFILES,
579             BLENDER_USER_CONFIG,
580             BLENDER_USER_SCRIPTS,
581             BLENDER_USER_AUTOSAVE)) {
582     return NULL;
583   }
584
585   path = BKE_appdir_folder_id(folder_id, subfolder);
586
587   if (!path) {
588     path = BKE_appdir_folder_id_user_notest(folder_id, subfolder);
589     if (path) {
590       BLI_dir_create_recursive(path);
591     }
592   }
593
594   return path;
595 }
596
597 /**
598  * Returns the path of the top-level version-specific local, user or system directory.
599  * If do_check, then the result will be NULL if the directory doesn't exist.
600  */
601 const char *BKE_appdir_folder_id_version(const int folder_id, const int ver, const bool do_check)
602 {
603   static char path[FILE_MAX] = "";
604   bool ok;
605   switch (folder_id) {
606     case BLENDER_RESOURCE_PATH_USER:
607       ok = get_path_user(path, sizeof(path), NULL, NULL, ver);
608       break;
609     case BLENDER_RESOURCE_PATH_LOCAL:
610       ok = get_path_local(path, sizeof(path), NULL, NULL, ver);
611       break;
612     case BLENDER_RESOURCE_PATH_SYSTEM:
613       ok = get_path_system(path, sizeof(path), NULL, NULL, ver);
614       break;
615     default:
616       path[0] = '\0'; /* in case do_check is false */
617       ok = false;
618       BLI_assert(!"incorrect ID");
619       break;
620   }
621
622   if (!ok && do_check) {
623     return NULL;
624   }
625
626   return path;
627 }
628
629 #ifdef PATH_DEBUG
630 #  undef PATH_DEBUG
631 #endif
632
633 /* -------------------------------------------------------------------- */
634 /* Preset paths */
635
636 /**
637  * Checks if name is a fully qualified filename to an executable.
638  * If not it searches $PATH for the file. On Windows it also
639  * adds the correct extension (.com .exe etc) from
640  * $PATHEXT if necessary. Also on Windows it translates
641  * the name to its 8.3 version to prevent problems with
642  * spaces and stuff. Final result is returned in fullname.
643  *
644  * \param fullname: The full path and full name of the executable
645  * (must be FILE_MAX minimum)
646  * \param name: The name of the executable (usually argv[0]) to be checked
647  */
648 static void where_am_i(char *fullname, const size_t maxlen, const char *name)
649 {
650 #ifdef WITH_BINRELOC
651   /* linux uses binreloc since argv[0] is not reliable, call br_init( NULL ) first */
652   {
653     const char *path = NULL;
654     path = br_find_exe(NULL);
655     if (path) {
656       BLI_strncpy(fullname, path, maxlen);
657       free((void *)path);
658       return;
659     }
660   }
661 #endif
662
663 #ifdef _WIN32
664   {
665     wchar_t *fullname_16 = MEM_mallocN(maxlen * sizeof(wchar_t), "ProgramPath");
666     if (GetModuleFileNameW(0, fullname_16, maxlen)) {
667       conv_utf_16_to_8(fullname_16, fullname, maxlen);
668       if (!BLI_exists(fullname)) {
669         CLOG_ERROR(&LOG, "path can't be found: \"%.*s\"", (int)maxlen, fullname);
670         MessageBox(
671             NULL, "path contains invalid characters or is too long (see console)", "Error", MB_OK);
672       }
673       MEM_freeN(fullname_16);
674       return;
675     }
676
677     MEM_freeN(fullname_16);
678   }
679 #endif
680
681   /* unix and non linux */
682   if (name && name[0]) {
683
684     BLI_strncpy(fullname, name, maxlen);
685     if (name[0] == '.') {
686       BLI_path_cwd(fullname, maxlen);
687 #ifdef _WIN32
688       BLI_path_program_extensions_add_win32(fullname, maxlen);
689 #endif
690     }
691     else if (BLI_last_slash(name)) {
692       // full path
693       BLI_strncpy(fullname, name, maxlen);
694 #ifdef _WIN32
695       BLI_path_program_extensions_add_win32(fullname, maxlen);
696 #endif
697     }
698     else {
699       BLI_path_program_search(fullname, maxlen, name);
700     }
701     /* Remove "/./" and "/../" so string comparisons can be used on the path. */
702     BLI_cleanup_path(NULL, fullname);
703
704 #if defined(DEBUG)
705     if (!STREQ(name, fullname)) {
706       CLOG_INFO(&LOG, 2, "guessing '%s' == '%s'", name, fullname);
707     }
708 #endif
709   }
710 }
711
712 void BKE_appdir_program_path_init(const char *argv0)
713 {
714   where_am_i(bprogname, sizeof(bprogname), argv0);
715   BLI_split_dir_part(bprogname, bprogdir, sizeof(bprogdir));
716 }
717
718 /**
719  * Path to executable
720  */
721 const char *BKE_appdir_program_path(void)
722 {
723   return bprogname;
724 }
725
726 /**
727  * Path to directory of executable
728  */
729 const char *BKE_appdir_program_dir(void)
730 {
731   return bprogdir;
732 }
733
734 bool BKE_appdir_program_python_search(char *fullpath,
735                                       const size_t fullpath_len,
736                                       const int version_major,
737                                       const int version_minor)
738 {
739 #ifdef PYTHON_EXECUTABLE_NAME
740   /* passed in from the build-systems 'PYTHON_EXECUTABLE' */
741   const char *python_build_def = STRINGIFY(PYTHON_EXECUTABLE_NAME);
742 #endif
743   const char *basename = "python";
744   char python_ver[16];
745   /* check both possible names */
746   const char *python_names[] = {
747 #ifdef PYTHON_EXECUTABLE_NAME
748       python_build_def,
749 #endif
750       python_ver,
751       basename,
752   };
753   int i;
754
755   bool is_found = false;
756
757   BLI_snprintf(python_ver, sizeof(python_ver), "%s%d.%d", basename, version_major, version_minor);
758
759   {
760     const char *python_bin_dir = BKE_appdir_folder_id(BLENDER_SYSTEM_PYTHON, "bin");
761     if (python_bin_dir) {
762
763       for (i = 0; i < ARRAY_SIZE(python_names); i++) {
764         BLI_join_dirfile(fullpath, fullpath_len, python_bin_dir, python_names[i]);
765
766         if (
767 #ifdef _WIN32
768             BLI_path_program_extensions_add_win32(fullpath, fullpath_len)
769 #else
770             BLI_exists(fullpath)
771 #endif
772         ) {
773           is_found = true;
774           break;
775         }
776       }
777     }
778   }
779
780   if (is_found == false) {
781     for (i = 0; i < ARRAY_SIZE(python_names); i++) {
782       if (BLI_path_program_search(fullpath, fullpath_len, python_names[i])) {
783         is_found = true;
784         break;
785       }
786     }
787   }
788
789   if (is_found == false) {
790     *fullpath = '\0';
791   }
792
793   return is_found;
794 }
795
796 /** Keep in sync with `bpy.utils.app_template_paths()` */
797 static const char *app_template_directory_search[2] = {
798     "startup" SEP_STR "bl_app_templates_user",
799     "startup" SEP_STR "bl_app_templates_system",
800 };
801
802 static const int app_template_directory_id[2] = {
803     /* Only 'USER' */
804     BLENDER_USER_SCRIPTS,
805     /* Covers 'LOCAL' & 'SYSTEM'. */
806     BLENDER_SYSTEM_SCRIPTS,
807 };
808
809 /**
810  * Return true if templates exist
811  */
812 bool BKE_appdir_app_template_any(void)
813 {
814   char temp_dir[FILE_MAX];
815   for (int i = 0; i < 2; i++) {
816     if (BKE_appdir_folder_id_ex(app_template_directory_id[i],
817                                 app_template_directory_search[i],
818                                 temp_dir,
819                                 sizeof(temp_dir))) {
820       return true;
821     }
822   }
823   return false;
824 }
825
826 bool BKE_appdir_app_template_id_search(const char *app_template, char *path, size_t path_len)
827 {
828   for (int i = 0; i < 2; i++) {
829     char subdir[FILE_MAX];
830     BLI_join_dirfile(subdir, sizeof(subdir), app_template_directory_search[i], app_template);
831     if (BKE_appdir_folder_id_ex(app_template_directory_id[i], subdir, path, path_len)) {
832       return true;
833     }
834   }
835   return false;
836 }
837
838 void BKE_appdir_app_templates(ListBase *templates)
839 {
840   BLI_listbase_clear(templates);
841
842   for (int i = 0; i < 2; i++) {
843     char subdir[FILE_MAX];
844     if (!BKE_appdir_folder_id_ex(app_template_directory_id[i],
845                                  app_template_directory_search[i],
846                                  subdir,
847                                  sizeof(subdir))) {
848       continue;
849     }
850
851     struct direntry *dir;
852     uint totfile = BLI_filelist_dir_contents(subdir, &dir);
853     for (int f = 0; f < totfile; f++) {
854       if (!FILENAME_IS_CURRPAR(dir[f].relname) && S_ISDIR(dir[f].type)) {
855         char *template = BLI_strdup(dir[f].relname);
856         BLI_addtail(templates, BLI_genericNodeN(template));
857       }
858     }
859
860     BLI_filelist_free(dir, totfile);
861   }
862 }
863
864 /**
865  * Gets the temp directory when blender first runs.
866  * If the default path is not found, use try $TEMP
867  *
868  * Also make sure the temp dir has a trailing slash
869  *
870  * \param fullname: The full path to the temporary temp directory
871  * \param basename: The full path to the persistent temp directory (may be NULL)
872  * \param maxlen: The size of the fullname buffer
873  * \param userdir: Directory specified in user preferences
874  */
875 static void where_is_temp(char *fullname, char *basename, const size_t maxlen, char *userdir)
876 {
877   /* Clear existing temp dir, if needed. */
878   BKE_tempdir_session_purge();
879
880   fullname[0] = '\0';
881   if (basename) {
882     basename[0] = '\0';
883   }
884
885   if (userdir && BLI_is_dir(userdir)) {
886     BLI_strncpy(fullname, userdir, maxlen);
887   }
888
889 #ifdef WIN32
890   if (fullname[0] == '\0') {
891     const char *tmp = BLI_getenv("TEMP"); /* Windows */
892     if (tmp && BLI_is_dir(tmp)) {
893       BLI_strncpy(fullname, tmp, maxlen);
894     }
895   }
896 #else
897   /* Other OS's - Try TMP and TMPDIR */
898   if (fullname[0] == '\0') {
899     const char *tmp = BLI_getenv("TMP");
900     if (tmp && BLI_is_dir(tmp)) {
901       BLI_strncpy(fullname, tmp, maxlen);
902     }
903   }
904
905   if (fullname[0] == '\0') {
906     const char *tmp = BLI_getenv("TMPDIR");
907     if (tmp && BLI_is_dir(tmp)) {
908       BLI_strncpy(fullname, tmp, maxlen);
909     }
910   }
911 #endif
912
913   if (fullname[0] == '\0') {
914     BLI_strncpy(fullname, "/tmp/", maxlen);
915   }
916   else {
917     /* add a trailing slash if needed */
918     BLI_add_slash(fullname);
919 #ifdef WIN32
920     if (userdir && userdir != fullname) {
921       /* also set user pref to show %TEMP%. /tmp/ is just plain confusing for Windows users. */
922       BLI_strncpy(userdir, fullname, maxlen);
923     }
924 #endif
925   }
926
927   /* Now that we have a valid temp dir, add system-generated unique sub-dir. */
928   if (basename) {
929     /* 'XXXXXX' is kind of tag to be replaced by mktemp-familly by an uuid. */
930     char *tmp_name = BLI_strdupcat(fullname, "blender_XXXXXX");
931     const size_t ln = strlen(tmp_name) + 1;
932     if (ln <= maxlen) {
933 #ifdef WIN32
934       if (_mktemp_s(tmp_name, ln) == 0) {
935         BLI_dir_create_recursive(tmp_name);
936       }
937 #else
938       if (mkdtemp(tmp_name) == NULL) {
939         BLI_dir_create_recursive(tmp_name);
940       }
941 #endif
942     }
943     if (BLI_is_dir(tmp_name)) {
944       BLI_strncpy(basename, fullname, maxlen);
945       BLI_strncpy(fullname, tmp_name, maxlen);
946       BLI_add_slash(fullname);
947     }
948     else {
949       CLOG_WARN(&LOG,
950                 "Could not generate a temp file name for '%s', falling back to '%s'",
951                 tmp_name,
952                 fullname);
953     }
954
955     MEM_freeN(tmp_name);
956   }
957 }
958
959 /**
960  * Sets btempdir_base to userdir if specified and is a valid directory, otherwise
961  * chooses a suitable OS-specific temporary directory.
962  * Sets btempdir_session to a mkdtemp-generated sub-dir of btempdir_base.
963  *
964  * \note On Window userdir will be set to the temporary directory!
965  */
966 void BKE_tempdir_init(char *userdir)
967 {
968   where_is_temp(btempdir_session, btempdir_base, FILE_MAX, userdir);
969 }
970
971 /**
972  * Path to temporary directory (with trailing slash)
973  */
974 const char *BKE_tempdir_session(void)
975 {
976   return btempdir_session[0] ? btempdir_session : BKE_tempdir_base();
977 }
978
979 /**
980  * Path to persistent temporary directory (with trailing slash)
981  */
982 const char *BKE_tempdir_base(void)
983 {
984   return btempdir_base;
985 }
986
987 /**
988  * Path to the system temporary directory (with trailing slash)
989  */
990 void BKE_tempdir_system_init(char *dir)
991 {
992   where_is_temp(dir, NULL, FILE_MAX, NULL);
993 }
994
995 /**
996  * Delete content of this instance's temp dir.
997  */
998 void BKE_tempdir_session_purge(void)
999 {
1000   if (btempdir_session[0] && BLI_is_dir(btempdir_session)) {
1001     BLI_delete(btempdir_session, true, true);
1002   }
1003 }