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