use BLI_strncpy and BLI_snprintf when the size of the string is known.
[blender.git] / source / blender / blenlib / intern / path_util.c
index e03a7ba..6d63105 100644 (file)
@@ -44,6 +44,7 @@
 #include "BLI_fileops.h"
 #include "BLI_path_util.h"
 #include "BLI_string.h"
+#include "BLI_string_utf8.h"
 #include "BLI_utildefines.h"
 
 #include "BKE_utildefines.h"
@@ -52,7 +53,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
-
+#  ifdef WITH_BINRELOC
+#    include "binreloc.h"
+#  endif
 #endif /* WIN32 */
 
 /* standard paths */
 #ifdef WIN32
-#define BLENDER_USER_FORMAT            "%s\\Blender Foundation\\Blender\\%s"
-#define BLENDER_SYSTEM_FORMAT          "%s\\Blender Foundation\\Blender\\%s"
+#  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
-#define BLENDER_USER_FORMAT                    "%s/.blender/%s"
-#define BLENDER_SYSTEM_FORMAT                  "%s/blender/%s"
+#  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
 
 /* local */
@@ -221,16 +221,25 @@ int BLI_uniquename_cb(int (*unique_check)(void *, const char *), void *arg, cons
        }
 
        if(unique_check(arg, name)) {
+               char    numstr[16];
                char    tempname[UNIQUE_NAME_MAX];
                char    left[UNIQUE_NAME_MAX];
                int             number;
                int             len= BLI_split_name_num(left, &number, name, delim);
                do {
-                       int newlen= BLI_snprintf(tempname, name_len, "%s%c%03d", left, delim, ++number);
-                       if(newlen >= name_len) {
-                               len -= ((newlen + 1) - name_len);
-                               if(len < 0) len= number= 0;
-                               left[len]= '\0';
+                       int numlen= BLI_snprintf(numstr, sizeof(numstr), "%c%03d", delim, ++number);
+
+                       /* highly unlikely the string only has enough room for the number
+                        * but support anyway */
+                       if ((len == 0) || (numlen >= name_len)) {
+                               /* number is know not to be utf-8 */
+                               BLI_strncpy(tempname, numstr, name_len);
+                       }
+                       else {
+                               char *tempname_buf;
+                               tempname[0]= '\0';
+                               tempname_buf =BLI_strncat_utf8(tempname, left, name_len - numlen);
+                               memcpy(tempname_buf, numstr, numlen + 1);
                        }
                } while(unique_check(arg, tempname));
 
@@ -308,7 +317,7 @@ void BLI_uniquename(ListBase *list, void *vlink, const char defname[], char deli
 
 void BLI_cleanup_path(const char *relabase, char *dir)
 {
-       short a;
+       ptrdiff_t a;
        char *start, *eind;
        if (relabase) {
                BLI_path_abs(dir, relabase);
@@ -417,8 +426,8 @@ void BLI_cleanup_file(const char *relabase, char *dir)
 void BLI_path_rel(char *file, const char *relfile)
 {
        char * lslash;
-       char temp[FILE_MAXDIR+FILE_MAXFILE];
-       char res[FILE_MAXDIR+FILE_MAXFILE];
+       char temp[FILE_MAX];
+       char res[FILE_MAX];
        
        /* if file is already relative, bail out */
        if(file[0]=='/' && file[1]=='/') return;
@@ -436,9 +445,9 @@ void BLI_path_rel(char *file, const char *relfile)
                if (relfile[0] != '\\' && relfile[0] != '/') {
                        ptemp++;
                }
-               BLI_strncpy(ptemp, relfile, FILE_MAXDIR + FILE_MAXFILE-3);
+               BLI_strncpy(ptemp, relfile, FILE_MAX-3);
        } else {
-               BLI_strncpy(temp, relfile, FILE_MAXDIR + FILE_MAXFILE);
+               BLI_strncpy(temp, relfile, FILE_MAX);
        }
 
        if (BLI_strnlen(file, 3) > 2) {
@@ -530,7 +539,7 @@ int BLI_has_parent(char *path)
 int BLI_parent_dir(char *path)
 {
        static char parent_dir[]= {'.', '.', SEP, '\0'}; /* "../" or "..\\" */
-       char tmp[FILE_MAXDIR+FILE_MAXFILE+4];
+       char tmp[FILE_MAX+4];
        BLI_strncpy(tmp, path, sizeof(tmp)-4);
        BLI_add_slash(tmp);
        strcat(tmp, parent_dir);
@@ -618,8 +627,10 @@ int BLI_path_frame_range(char *path, int sta, int end, int digits)
 
        if (stringframe_chars(path, &ch_sta, &ch_end)) { /* warning, ch_end is the last # +1 */
                char tmp[FILE_MAX];
-               sprintf(tmp, "%.*s%.*d-%.*d%s", ch_sta, path, ch_end-ch_sta, sta, ch_end-ch_sta, end, path+ch_end);
-               strcpy(path, tmp);
+               BLI_snprintf(tmp, sizeof(tmp),
+                            "%.*s%.*d-%.*d%s",
+                            ch_sta, path, ch_end-ch_sta, sta, ch_end-ch_sta, end, path+ch_end);
+               BLI_strncpy(path, tmp, FILE_MAX);
                return 1;
        }
        return 0;
@@ -735,7 +746,7 @@ int BLI_path_cwd(char *path)
 #endif
        
        if (wasrelative==1) {
-               char cwd[FILE_MAXDIR + FILE_MAXFILE]= "";
+               char cwd[FILE_MAX]= "";
                BLI_current_working_dir(cwd, sizeof(cwd)); /* incase the full path to the blend isnt used */
                
                if (cwd[0] == '\0') {
@@ -748,8 +759,8 @@ int BLI_path_cwd(char *path)
                        * blend file which isnt a feature we want to use in this case since were dealing
                        * with a path from the command line, rather than from inside Blender */
                        
-                       char origpath[FILE_MAXDIR + FILE_MAXFILE];
-                       BLI_strncpy(origpath, path, FILE_MAXDIR + FILE_MAXFILE);
+                       char origpath[FILE_MAX];
+                       BLI_strncpy(origpath, path, FILE_MAX);
                        
                        BLI_make_file_string(NULL, path, cwd, origpath); 
                }
@@ -797,10 +808,18 @@ void BLI_getlastdir(const char* dir, char *last, const size_t maxlen)
    as default location to save documents */
 const char *BLI_getDefaultDocumentFolder(void)
 {
-       #if !defined(WIN32)
+#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;
@@ -825,7 +844,7 @@ const char *BLI_getDefaultDocumentFolder(void)
                }
                
                return NULL;
-       #endif
+#endif /* WIN32 */
 }
 
 /* NEW stuff, to be cleaned up when fully migrated */
@@ -1239,7 +1258,7 @@ void BLI_make_exist(char *dir)
 
 void BLI_make_existing_file(const char *name)
 {
-       char di[FILE_MAXDIR+FILE_MAXFILE], fi[FILE_MAXFILE];
+       char di[FILE_MAX], fi[FILE_MAXFILE];
 
        BLI_strncpy(di, name, sizeof(di));
        BLI_splitdirstring(di, fi);
@@ -1397,22 +1416,55 @@ 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 a;
+       size_t path_len= strlen(path);
+       size_t ext_len= strlen(ext);
+       ssize_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 < 0) || (path[a] != '.')) {
+               a= path_len;
+       }
+
+       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);
+       ssize_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;
 }
 
@@ -1655,8 +1707,8 @@ static int add_win32_extension(char *name)
        type = BLI_exists(name);
        if ((type == 0) || S_ISDIR(type)) {
 #ifdef _WIN32
-               char filename[FILE_MAXDIR+FILE_MAXFILE];
-               char ext[FILE_MAXDIR+FILE_MAXFILE];
+               char filename[FILE_MAX];
+               char ext[FILE_MAX];
                const char *extensions = getenv("PATHEXT");
                if (extensions) {
                        char *temp;
@@ -1702,7 +1754,7 @@ static int add_win32_extension(char *name)
 */
 static void bli_where_am_i(char *fullname, const size_t maxlen, const char *name)
 {
-       char filename[FILE_MAXDIR+FILE_MAXFILE];
+       char filename[FILE_MAX];
        const char *path = NULL, *temp;
 
 #ifdef _WIN32
@@ -1805,7 +1857,7 @@ const char *BLI_program_dir(void)
 * @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)
+static void BLI_where_is_temp(char *fullname, const size_t maxlen, char *userdir)
 {
        fullname[0] = '\0';