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