patch [#28947] Patches for #28943 (Support for XDG Base Directory Specification)
[blender.git] / source / blender / blenlib / intern / path_util.c
index 0b750d5..7aa8452 100644 (file)
@@ -1,6 +1,4 @@
 /*
- *  $Id$
- *
  * ***** BEGIN GPL LICENSE BLOCK *****
  *
  * This program is free software; you can redistribute it and/or
 
 #include "MEM_guardedalloc.h"
 
-#include "DNA_userdef_types.h"
+#include "DNA_listBase.h"
 
 #include "BLI_fileops.h"
 #include "BLI_path_util.h"
 #include "BLI_string.h"
-#include "BLI_storage.h"
-#include "BLI_storage_types.h"
 #include "BLI_utildefines.h"
 
 #include "BKE_utildefines.h"
@@ -56,7 +52,7 @@
 #include "GHOST_Path-api.h"
 
 #if defined WIN32 && !defined _LIBC
-# include "BLI_fnmatch.h" /* use fnmatch included in blenlib */
+#  include "BLI_fnmatch.h" /* use fnmatch included in blenlib */
 #else
 #  ifndef _GNU_SOURCE
 #    define _GNU_SOURCE
 #endif
 
 #ifdef WIN32
-#include <io.h>
-
-#ifdef _WIN32_IE
-#undef _WIN32_IE
-#endif
-#define _WIN32_IE 0x0501
-#include <windows.h>
-#include <shlobj.h>
-
-#include "BLI_winstuff.h"
-
+#  include <io.h>
+#  ifdef _WIN32_IE
+#    undef _WIN32_IE
+#  endif
+#  define _WIN32_IE 0x0501
+#  include <windows.h>
+#  include <shlobj.h>
+#  include "BLI_winstuff.h"
 #else /* non windows */
+#  ifdef WITH_BINRELOC
+#    include "binreloc.h"
+#  endif
+#endif /* WIN32 */
 
-#ifdef WITH_BINRELOC
-#include "binreloc.h"
+/* standard paths */
+#ifdef WIN32
+#  define BLENDER_USER_FORMAT          "%s\\Blender Foundation\\Blender\\%s"
+#  define BLENDER_SYSTEM_FORMAT                "%s\\Blender Foundation\\Blender\\%s"
+#elif defined(__APPLE__)
+#  define BLENDER_USER_FORMAT                  "%s/Blender/%s"
+#  define BLENDER_SYSTEM_FORMAT                        "%s/Blender/%s"
+#else /* UNIX */
+#  ifndef WITH_XDG_USER_DIRS /* oldschool unix ~/.blender/ */
+#    define BLENDER_USER_FORMAT                        "%s/.blender/%s"
+#  else /* new XDG ~/blender/.config/ */
+#    define BLENDER_USER_FORMAT                        "%s/blender/%s"
+#  endif // WITH_XDG_USER_DIRS
+#  define BLENDER_SYSTEM_FORMAT                        "%s/blender/%s"
 #endif
 
-#endif /* WIN32 */
-
 /* local */
 #define UNIQUE_NAME_MAX 128
 
-extern char bprogname[];
+static char bprogname[FILE_MAX];       /* path to program executable */
+static char bprogdir[FILE_MAX];                /* path in which executable is located */
+static char btempdir[FILE_MAX];                /* temporary directory */
 
 static int add_win32_extension(char *name);
 static char *blender_version_decimal(const int ver);
@@ -726,7 +735,7 @@ int BLI_path_cwd(char *path)
        
        if (wasrelative==1) {
                char cwd[FILE_MAXDIR + FILE_MAXFILE]= "";
-               BLI_getwdN(cwd, sizeof(cwd)); /* incase the full path to the blend isnt used */
+               BLI_current_working_dir(cwd, sizeof(cwd)); /* incase the full path to the blend isnt used */
                
                if (cwd[0] == '\0') {
                        printf( "Could not get the current working directory - $PWD for an unknown reason.");
@@ -785,11 +794,20 @@ void BLI_getlastdir(const char* dir, char *last, const size_t maxlen)
 /* This is now only used to really get the user's default document folder */
 /* On Windows I chose the 'Users/<MyUserName>/Documents' since it's used
    as default location to save documents */
-const char *BLI_getDefaultDocumentFolder(void) {
-       #if !defined(WIN32)
+const char *BLI_getDefaultDocumentFolder(void)
+{
+#ifndef WIN32
+
+#ifdef WITH_XDG_USER_DIRS
+               const char *xdg_documents_dir= getenv("XDG_DOCUMENTS_DIR");
+               if (xdg_documents_dir) {
+                       return xdg_documents_dir;
+               }
+#endif
+
                return getenv("HOME");
 
-       #else /* Windows */
+#else /* Windows */
                const char * ret;
                static char documentfolder[MAXPATHLEN];
                HRESULT hResult;
@@ -814,7 +832,7 @@ const char *BLI_getDefaultDocumentFolder(void) {
                }
                
                return NULL;
-       #endif
+#endif /* WIN32 */
 }
 
 /* NEW stuff, to be cleaned up when fully migrated */
@@ -874,7 +892,6 @@ static int test_env_path(char *path, const char *envvar)
 
 static int get_path_local(char *targetpath, const char *folder_name, const char *subfolder_name, const int ver)
 {
-       char bprogdir[FILE_MAX];
        char relfolder[FILE_MAX];
        
 #ifdef PATH_DEBUG2
@@ -891,10 +908,7 @@ static int get_path_local(char *targetpath, const char *folder_name, const char
        else {
                relfolder[0]= '\0';
        }
-       
-       /* use argv[0] (bprogname) to get the path to the executable */
-       BLI_split_dirfile(bprogname, bprogdir, NULL);
-       
+
        /* try EXECUTABLE_DIR/2.5x/folder_name - new default directory for local blender installed files */
        if(test_path(targetpath, bprogdir, blender_version_decimal(ver), relfolder))
                return 1;
@@ -962,10 +976,6 @@ static int get_path_system(char *targetpath, const char *folder_name, const char
         * these are only used when running blender from source */
        char cwd[FILE_MAX];
        char relfolder[FILE_MAX];
-       char bprogdir[FILE_MAX];
-
-       /* use argv[0] (bprogname) to get the path to the executable */
-       BLI_split_dirfile(bprogname, bprogdir, NULL);
 
        if(folder_name) {
                if (subfolder_name) {
@@ -979,7 +989,7 @@ static int get_path_system(char *targetpath, const char *folder_name, const char
        }
 
        /* try CWD/release/folder_name */
-       if(BLI_getwdN(cwd, sizeof(cwd))) {
+       if(BLI_current_working_dir(cwd, sizeof(cwd))) {
                if(test_path(targetpath, cwd, "release", relfolder)) {
                        return 1;
                }
@@ -1110,7 +1120,7 @@ char *BLI_get_folder_create(int folder_id, const char *subfolder)
        
        if (!path) {
                path = BLI_get_user_folder_notest(folder_id, subfolder);
-               if (path) BLI_recurdir_fileops(path);
+               if (path) BLI_dir_create_recursive(path);
        }
        
        return path;
@@ -1206,7 +1216,8 @@ void BLI_char_switch(char *string, char from, char to)
        }
 }
 
-void BLI_make_exist(char *dir) {
+void BLI_make_exist(char *dir)
+{
        int a;
 
        BLI_char_switch(dir, ALTSEP, SEP);
@@ -1242,7 +1253,7 @@ void BLI_make_existing_file(const char *name)
        
        /* test exist */
        if (BLI_exists(di) == 0) {
-               BLI_recurdir_fileops(di);
+               BLI_dir_create_recursive(di);
        }
 }
 
@@ -1251,9 +1262,18 @@ void BLI_make_file_string(const char *relabase, char *string,  const char *dir,
 {
        int sl;
 
-       if (!string || !dir || !file) return; /* We don't want any NULLs */
-       
-       string[0]= 0; /* ton */
+       if (string) {
+               /* ensure this is always set even if dir/file are NULL */
+               string[0]= '\0';
+
+               if (ELEM(NULL, dir, file)) {
+                       return; /* We don't want any NULLs */
+               }
+       }
+       else {
+               return; /* string is NULL, probably shouldnt happen but return anyway */
+       }
+
 
        /* we first push all slashes into unix mode, just to make sure we don't get
           any mess with slashes later on. -jesterKing */
@@ -1384,22 +1404,51 @@ int BLI_testextensie_glob(const char *str, const char *ext_fnmatch)
 
 int BLI_replace_extension(char *path, size_t maxlen, const char *ext)
 {
+       size_t path_len= strlen(path);
+       size_t ext_len= strlen(ext);
        size_t a;
 
-       for(a=strlen(path); a>0; a--) {
-               if(path[a-1] == '.' || path[a-1] == '/' || path[a-1] == '\\') {
-                       a--;
+       for(a= path_len - 1; a >= 0; a--) {
+               if (ELEM3(path[a], '.', '/', '\\')) {
                        break;
                }
        }
-       
-       if(path[a] != '.')
-               a= strlen(path);
 
-       if(a + strlen(ext) >= maxlen)
+       if(a + ext_len >= maxlen)
+               return 0;
+
+       memcpy(path+a, ext, ext_len + 1);
+       return 1;
+}
+
+/* strip's trailing '.'s and adds the extension only when needed */
+int BLI_ensure_extension(char *path, size_t maxlen, const char *ext)
+{
+       size_t path_len= strlen(path);
+       size_t ext_len= strlen(ext);
+       size_t a;
+
+       /* first check the extension is alread there */
+       if (    (ext_len <= path_len) &&
+               (strcmp(path + (path_len - ext_len), ext) == 0))
+       {
+               return 1;
+       }
+
+       for(a= path_len - 1; a >= 0; a--) {
+               if (path[a] == '.') {
+                       path[a]= '\0';
+               }
+               else {
+                       break;
+               }
+       }
+       a++;
+
+       if(a + ext_len >= maxlen)
                return 0;
 
-       strcpy(path+a, ext);
+       memcpy(path+a, ext, ext_len + 1);
        return 1;
 }
 
@@ -1409,40 +1458,70 @@ int BLI_replace_extension(char *path, size_t maxlen, const char *ext)
  * - dosnt use CWD, or deal with relative paths.
  * - Only fill's in *dir and *file when they are non NULL
  * */
-void BLI_split_dirfile(const char *string, char *dir, char *file)
+void BLI_split_dirfile(const char *string, char *dir, char *file, const size_t dirlen, const size_t filelen)
 {
        char *lslash_str = BLI_last_slash(string);
-       int lslash= lslash_str ? (int)(lslash_str - string) + 1 : 0;
+       size_t lslash= lslash_str ? (size_t)(lslash_str - string) + 1 : 0;
 
        if (dir) {
                if (lslash) {
-                       BLI_strncpy( dir, string, lslash + 1); /* +1 to include the slash and the last char */
-               } else {
+                       BLI_strncpy( dir, string, MIN2(dirlen, lslash + 1)); /* +1 to include the slash and the last char */
+               }
+               else {
                        dir[0] = '\0';
                }
        }
        
        if (file) {
-               strcpy( file, string+lslash);
+               BLI_strncpy(file, string+lslash, filelen);
        }
 }
 
+void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen)
+{
+       BLI_split_dirfile(string, dir, NULL, dirlen, 0);
+}
+
+void BLI_split_file_part(const char *string, char *file, const size_t filelen)
+{
+       BLI_split_dirfile(string, NULL, file, 0, filelen);
+}
+
 /* simple appending of filename to dir, does not check for valid path! */
-void BLI_join_dirfile(char *string, const size_t maxlen, const char *dir, const char *file)
+void BLI_join_dirfile(char *dst, const size_t maxlen, const char *dir, const char *file)
 {
-       int sl_dir;
-       
-       if(string != dir) /* compare pointers */
-               BLI_strncpy(string, dir, maxlen);
+       size_t dirlen= BLI_strnlen(dir, maxlen);
+
+       if (dst != dir) {
+               if(dirlen  == maxlen) {
+                       memcpy(dst, dir, dirlen);
+                       dst[dirlen - 1]= '\0';
+                       return; /* dir fills the path */
+               }
+               else {
+                       memcpy(dst, dir, dirlen + 1);
+               }
+       }
 
-       if (!file)
+       if (dirlen + 1 >= maxlen) {
+               return; /* fills the path */
+       }
+
+       /* inline BLI_add_slash */
+       if (dst[dirlen - 1] != SEP) {
+               dst[dirlen++]= SEP;
+               dst[dirlen  ]= '\0';
+       }
+
+       if (dirlen >= maxlen) {
+               return; /* fills the path */
+       }
+
+       if (file == NULL) {
                return;
-       
-       sl_dir= BLI_add_slash(string);
-       
-       if (sl_dir <FILE_MAX) {
-               BLI_strncpy(string + sl_dir, file, maxlen - sl_dir);
        }
+
+       BLI_strncpy(dst + dirlen, file, maxlen - dirlen);
 }
 
 /* like pythons os.path.basename( ) */
@@ -1494,7 +1573,7 @@ int BKE_rebase_path(char *abs, size_t abs_len, char *rel, size_t rel_len, const
        if (rel)
                rel[0]= 0;
 
-       BLI_split_dirfile(base_dir, blend_dir, NULL);
+       BLI_split_dir_part(base_dir, blend_dir, sizeof(blend_dir));
 
        if (src_dir[0]=='\0')
                return 0;
@@ -1505,7 +1584,7 @@ int BKE_rebase_path(char *abs, size_t abs_len, char *rel, size_t rel_len, const
        BLI_path_abs(path, base_dir);
 
        /* get the directory part */
-       BLI_split_dirfile(path, dir, base);
+       BLI_split_dirfile(path, dir, base, sizeof(dir), sizeof(base));
 
        len= strlen(blend_dir);
 
@@ -1550,7 +1629,8 @@ int BKE_rebase_path(char *abs, size_t abs_len, char *rel, size_t rel_len, const
        return 1;
 }
 
-char *BLI_first_slash(char *string) {
+char *BLI_first_slash(char *string)
+{
        char *ffslash, *fbslash;
        
        ffslash= strchr(string, '/');   
@@ -1563,7 +1643,8 @@ char *BLI_first_slash(char *string) {
        else return fbslash;
 }
 
-char *BLI_last_slash(const char *string) {
+char *BLI_last_slash(const char *string)
+{
        char *lfslash, *lbslash;
        
        lfslash= strrchr(string, '/');  
@@ -1577,33 +1658,23 @@ char *BLI_last_slash(const char *string) {
 }
 
 /* adds a slash if there isnt one there already */
-int BLI_add_slash(char *string) {
+int BLI_add_slash(char *string)
+{
        int len = strlen(string);
-#ifdef WIN32
-       if (len==0 || string[len-1]!='\\') {
-               string[len] = '\\';
+       if (len==0 || string[len-1] != SEP) {
+               string[len] = SEP;
                string[len+1] = '\0';
                return len+1;
        }
-#else
-       if (len==0 || string[len-1]!='/') {
-               string[len] = '/';
-               string[len+1] = '\0';
-               return len+1;
-       }
-#endif
        return len;
 }
 
 /* removes a slash if there is one */
-void BLI_del_slash(char *string) {
+void BLI_del_slash(char *string)
+{
        int len = strlen(string);
        while (len) {
-#ifdef WIN32
-               if (string[len-1]=='\\') {
-#else
-               if (string[len-1]=='/') {
-#endif
+               if (string[len-1] == SEP) {
                        string[len-1] = '\0';
                        len--;
                } else {
@@ -1617,7 +1688,7 @@ static int add_win32_extension(char *name)
        int retval = 0;
        int type;
 
-       type = BLI_exist(name);
+       type = BLI_exists(name);
        if ((type == 0) || S_ISDIR(type)) {
 #ifdef _WIN32
                char filename[FILE_MAXDIR+FILE_MAXFILE];
@@ -1637,7 +1708,7 @@ static int add_win32_extension(char *name)
                                        strcat(filename, extensions);
                                }
 
-                               type = BLI_exist(filename);
+                               type = BLI_exists(filename);
                                if (type && (! S_ISDIR(type))) {
                                        retval = 1;
                                        strcpy(name, filename);
@@ -1653,8 +1724,19 @@ static int add_win32_extension(char *name)
        return (retval);
 }
 
-/* filename must be FILE_MAX length minimum */
-void BLI_where_am_i(char *fullname, const size_t maxlen, const char *name)
+/*
+* Checks if name is a fully qualified filename to an executable.
+* If not it searches $PATH for the file. On Windows it also
+* adds the correct extension (.com .exe etc) from
+* $PATHEXT if necessary. Also on Windows it translates
+* the name to its 8.3 version to prevent problems with
+* spaces and stuff. Final result is returned in fullname.
+*
+* @param fullname The full path and full name of the executable
+* (must be FILE_MAX minimum)
+* @param name The name of the executable (usually argv[0]) to be checked
+*/
+static void bli_where_am_i(char *fullname, const size_t maxlen, const char *name)
 {
        char filename[FILE_MAXDIR+FILE_MAXFILE];
        const char *path = NULL, *temp;
@@ -1667,7 +1749,7 @@ void BLI_where_am_i(char *fullname, const size_t maxlen, const char *name)
 
        
 #ifdef WITH_BINRELOC
-       /* linux uses binreloc since argv[0] is not relyable, call br_init( NULL ) first */
+       /* linux uses binreloc since argv[0] is not reliable, call br_init( NULL ) first */
        path = br_find_exe( NULL );
        if (path) {
                BLI_strncpy(fullname, path, maxlen);
@@ -1691,7 +1773,7 @@ void BLI_where_am_i(char *fullname, const size_t maxlen, const char *name)
                BLI_strncpy(fullname, name, maxlen);
                if (name[0] == '.') {
                        char wdir[FILE_MAX]= "";
-                       BLI_getwdN(wdir, sizeof(wdir));  /* backup cwd to restore after */
+                       BLI_current_working_dir(wdir, sizeof(wdir));     /* backup cwd to restore after */
 
                        // not needed but avoids annoying /./ in name
                        if(name[1]==SEP)
@@ -1734,12 +1816,37 @@ void BLI_where_am_i(char *fullname, const size_t maxlen, const char *name)
        }
 }
 
-void BLI_where_is_temp(char *fullname, const size_t maxlen, int usertemp)
+void BLI_init_program_path(const char *argv0)
+{
+       bli_where_am_i(bprogname, sizeof(bprogname), argv0);
+       BLI_split_dir_part(bprogname, bprogdir, sizeof(bprogdir));
+}
+
+const char *BLI_program_path(void)
+{
+       return bprogname;
+}
+
+const char *BLI_program_dir(void)
+{
+       return bprogdir;
+}
+
+/**
+* Gets the temp directory when blender first runs.
+* If the default path is not found, use try $TEMP
+* 
+* Also make sure the temp dir has a trailing slash
+*
+* @param fullname The full path to the temp directory
+* @param userdir Directory specified in user preferences 
+*/
+void BLI_where_is_temp(char *fullname, const size_t maxlen, char *userdir)
 {
        fullname[0] = '\0';
        
-       if (usertemp && BLI_is_dir(U.tempdir)) {
-               BLI_strncpy(fullname, U.tempdir, maxlen);
+       if (userdir && BLI_is_dir(userdir)) {
+               BLI_strncpy(fullname, userdir, maxlen);
        }
        
        
@@ -1773,13 +1880,28 @@ void BLI_where_is_temp(char *fullname, const size_t maxlen, int usertemp)
                /* add a trailing slash if needed */
                BLI_add_slash(fullname);
 #ifdef WIN32
-               if(U.tempdir != fullname) {
-                       BLI_strncpy(U.tempdir, fullname, maxlen); /* also set user pref to show %TEMP%. /tmp/ is just plain confusing for Windows users. */
+               if(userdir && userdir != fullname) {
+                       BLI_strncpy(userdir, fullname, maxlen); /* also set user pref to show %TEMP%. /tmp/ is just plain confusing for Windows users. */
                }
 #endif
        }
 }
 
+void BLI_init_temporary_dir(char *userdir)
+{
+       BLI_where_is_temp(btempdir, FILE_MAX, userdir);
+}
+
+const char *BLI_temporary_dir(void)
+{
+       return btempdir;
+}
+
+void BLI_system_temporary_dir(char *dir)
+{
+       BLI_where_is_temp(dir, FILE_MAX, NULL);
+}
+
 #ifdef WITH_ICONV
 
 void BLI_string_to_utf8(char *original, char *utf_8, const char *code)