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