bugfix [#24462] UV Layouts saved as PNG results in two files (one is 0 KB, other...
[blender.git] / source / blender / blenlib / intern / path_util.c
index bf7a4f7..91558d4 100644 (file)
@@ -15,7 +15,7 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, write to the Free Software Foundation,
- * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  *
  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
  * All rights reserved.
  * various string, file, list operations.
  */
 
-#include <stdio.h>
-#include <fcntl.h>
 #include <ctype.h>
 #include <string.h>
 #include <stdlib.h>
-#include <stdarg.h>
-#include <math.h> /* for log10 */
 
 #include "MEM_guardedalloc.h"
 
-#include "DNA_listBase.h"
 #include "DNA_userdef_types.h"
 
-#include "BLI_blenlib.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_dynamiclist.h"
 
-#include "BLI_util.h"
 #include "BKE_utildefines.h"
+#include "BKE_blender.h"       // BLENDER_VERSION
 
+#include "GHOST_Path-api.h"
 
-
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#ifndef WIN32 
-#include <unistd.h>
+#if defined WIN32 && !defined _LIBC
+# include "BLI_fnmatch.h" /* use fnmatch included in blenlib */
 #else
-#include <io.h>
+# define _GNU_SOURCE
+# include <fnmatch.h>
 #endif
 
 #ifdef WIN32
+#include <io.h>
 
 #ifdef _WIN32_IE
 #undef _WIN32_IE
 
 #include "BLI_winstuff.h"
 
-#endif
-
-
-#ifndef WIN32
-#include <sys/time.h>
-#endif
-
-#ifdef __APPLE__
-#include <sys/param.h>
-#include <CoreFoundation/CoreFoundation.h>
-#endif
+#else /* non windows */
 
 #ifdef __linux__
 #include "binreloc.h"
 #endif
 
+#endif /* WIN32 */
+
 /* local */
 
 static int add_win32_extension(char *name);
+static char *blender_version_decimal(void);
 
 /* implementation */
 
-int BLI_stringdec(char *string, char *kop, char *start, unsigned short *numlen)
+int BLI_stringdec(const char *string, char *head, char *tail, unsigned short *numlen)
 {
-       unsigned short len, len2, nums = 0, nume = 0;
+       unsigned short len, len2, lenlslash = 0, nums = 0, nume = 0;
        short i, found = 0;
-
+       char *lslash = BLI_last_slash(string);
        len2 = len = strlen(string);
-       
-       if (len > 6) {
-               if (BLI_strncasecmp(string + len - 6, ".blend", 6) == 0) len -= 6;
-               else if (BLI_strncasecmp(string + len - 6, ".trace", 6) == 0) len -= 6;
-       }
-       
-       if (len > 9) {
-               if (BLI_strncasecmp(string + len - 9, ".blend.gz", 9) == 0) len -= 9;
-       }
-               
-       if (len == len2) {
-               if (len > 4) {
-                       /* handle .jf0 en .jf1 for jstreams */
-                       if (BLI_strncasecmp(string + len - 4, ".jf", 3) == 0) len -= 4;
-                       else if (BLI_strncasecmp(string + len - 4, ".tga", 4) == 0) len -= 4;
-                       else if (BLI_strncasecmp(string + len - 4, ".jpg", 4) == 0) len -= 4;
-                       else if (BLI_strncasecmp(string + len - 4, ".png", 4) == 0) len -= 4;
-                       else if (BLI_strncasecmp(string + len - 4, ".txt", 4) == 0) len -= 4;
-                       else if (BLI_strncasecmp(string + len - 4, ".cyc", 4) == 0) len -= 4;
-                       else if (BLI_strncasecmp(string + len - 4, ".enh", 4) == 0) len -= 4;
-                       else if (BLI_strncasecmp(string + len - 4, ".rgb", 4) == 0) len -= 4;
-                       else if (BLI_strncasecmp(string + len - 4, ".psx", 4) == 0) len -= 4;
-                       else if (BLI_strncasecmp(string + len - 4, ".ble", 4) == 0) len -= 4;
-                       else if (BLI_strncasecmp(string + len - 4, ".exr", 4) == 0) len -= 4;
-               }
-       }
-       
-       for (i = len - 1; i >= 0; i--) {
-               if (string[i] == '/') break;
+       if(lslash)
+               lenlslash= (int)(lslash - string);
+
+       while(len > lenlslash && string[--len] != '.') {};
+       if(len == lenlslash && string[len] != '.') len = len2;
+
+       for (i = len - 1; i >= lenlslash; i--) {
                if (isdigit(string[i])) {
                        if (found){
                                nums = i;
@@ -145,41 +109,31 @@ int BLI_stringdec(char *string, char *kop, char *start, unsigned short *numlen)
                        if (found) break;
                }
        }
-       if (found){
-               if (start) strcpy(start,&string[nume+1]);
-               if (kop) {
-                       strcpy(kop,string);
-                       kop[nums]=0;
+       if (found) {
+               if (tail) strcpy(tail, &string[nume+1]);
+               if (head) {
+                       strcpy(head,string);
+                       head[nums]=0;
                }
                if (numlen) *numlen = nume-nums+1;
                return ((int)atoi(&(string[nums])));
        }
-       if (start) strcpy(start, string + len);
-       if (kop) {
-               strncpy(kop, string, len);
-               kop[len] = 0;
+       if (tail) strcpy(tail, string + len);
+       if (head) {
+               strncpy(head, string, len);
+               head[len] = '\0';
        }
        if (numlen) *numlen=0;
        return 0;
 }
 
 
-void BLI_stringenc(char *string, char *kop, char *start, unsigned short numlen, int pic)
+void BLI_stringenc(char *string, const char *head, const char *tail, unsigned short numlen, int pic)
 {
-       char numstr[10]="";
-       unsigned short len,i;
-
-       strcpy(string,kop);
-       
-       if (pic>0 || numlen==4) {
-               len= sprintf(numstr,"%d",pic);
-               
-               for(i=len;i<numlen;i++){
-                       strcat(string,"0");
-               }
-               strcat(string,numstr);
-       }
-       strcat(string, start);
+       char fmtstr[16]="";
+       if(pic < 0) pic= 0;
+       sprintf(fmtstr, "%%s%%.%dd%%s", numlen);
+       sprintf(string, fmtstr, head, pic, tail);
 }
 
 
@@ -256,12 +210,12 @@ void BLI_uniquename(ListBase *list, void *vlink, const char defname[], char deli
                return;
 
        /* Strip off the suffix */
-       dot = strchr(GIVE_STRADDR(vlink, name_offs), delim);
+       dot = strrchr(GIVE_STRADDR(vlink, name_offs), delim);
        if (dot)
                *dot=0;
        
        for (number = 1; number <= 999; number++) {
-               BLI_snprintf(tempname, 128, "%s%c%03d", GIVE_STRADDR(vlink, name_offs), delim, number);
+               BLI_snprintf(tempname, sizeof(tempname), "%s%c%03d", GIVE_STRADDR(vlink, name_offs), delim, number);
                
                exists = 0;
                for (link= list->first; link; link= link->next) {
@@ -301,7 +255,7 @@ void BLI_cleanup_file(const char *relabase, char *dir)
        short a;
        char *start, *eind;
        if (relabase) {
-               BLI_convertstringcode(dir, relabase);
+               BLI_path_abs(dir, relabase);
        } else {
                if (dir[0]=='/' && dir[1]=='/') {
                        if (dir[2]== '\0') {
@@ -363,10 +317,17 @@ void BLI_cleanup_file(const char *relabase, char *dir)
           dir[0]= '/';
           dir[1]= 0;
           return;
-       }       
+       }
+
+       /* support for odd paths: eg /../home/me --> /home/me
+        * this is a valid path in blender but we cant handle this the useual way below
+        * simply strip this prefix then evaluate the path as useual. pythons os.path.normpath() does this */
+       while((strncmp(dir, "/../", 4)==0)) {
+               memmove( dir, dir + 4, strlen(dir + 4) + 1 );
+       }
 
        while ( (start = strstr(dir, "/../")) ) {
-               eind = start + strlen("/../") - 1;
+               eind = start + (4 - 1) /* strlen("/../") - 1 */;
                a = start-dir-1;
                while (a>0) {
                        if (dir[a] == '/') break;
@@ -380,12 +341,12 @@ void BLI_cleanup_file(const char *relabase, char *dir)
        }
 
        while ( (start = strstr(dir,"/./")) ){
-               eind = start + strlen("/./") - 1;
+               eind = start + (3 - 1) /* strlen("/./") - 1 */;
                memmove( start, eind, strlen(eind)+1 );
        }
 
        while ( (start = strstr(dir,"//" )) ){
-               eind = start + strlen("//") - 1;
+               eind = start + (2 - 1) /* strlen("//") - 1 */;
                memmove( start, eind, strlen(eind)+1 );
        }
 
@@ -400,10 +361,8 @@ void BLI_cleanup_file(const char *relabase, char *dir)
 }
 
 
-void BLI_makestringcode(const char *relfile, char *file)
+void BLI_path_rel(char *file, const char *relfile)
 {
-       char * p;
-       char * q;
        char * lslash;
        char temp[FILE_MAXDIR+FILE_MAXFILE];
        char res[FILE_MAXDIR+FILE_MAXFILE];
@@ -418,7 +377,7 @@ void BLI_makestringcode(const char *relfile, char *file)
        if (strlen(relfile) > 2 && relfile[1] != ':') {
                char* ptemp;
                /* fix missing volume name in relative base,
-                  can happen with old .Blog files */
+                  can happen with old recent-files.txt files */
                get_default_root(temp);
                ptemp = &temp[2];
                if (relfile[0] != '\\' && relfile[0] != '/') {
@@ -451,11 +410,18 @@ void BLI_makestringcode(const char *relfile, char *file)
        {       
                /* find the prefix of the filename that is equal for both filenames.
                   This is replaced by the two slashes at the beginning */
-               p = temp;
-               q = file;
-               while (*p == *q) {
+               char *p= temp;
+               char *q= file;
+
+               while ((*p == *q)) {
                        ++p; ++q;
+                       /* dont search beyond the end of the string
+                        * in the rare case they match */
+                       if ((*p=='\0') || (*q=='\0')) {
+                               break;
+                       }
                }
+
                /* we might have passed the slash when the beginning of a dir matches 
                   so we rewind. Only check on the actual filename
                */
@@ -505,13 +471,9 @@ int BLI_has_parent(char *path)
 
 int BLI_parent_dir(char *path)
 {
-#ifdef WIN32
-       static char *parent_dir="..\\";
-#else
-       static char *parent_dir="../";
-#endif
+       static char parent_dir[]= {'.', '.', SEP, '\0'}; /* "../" or "..\\" */
        char tmp[FILE_MAXDIR+FILE_MAXFILE+4];
-       BLI_strncpy(tmp, path, sizeof(tmp));
+       BLI_strncpy(tmp, path, sizeof(tmp)-4);
        BLI_add_slash(tmp);
        strcat(tmp, parent_dir);
        BLI_cleanup_dir(NULL, tmp);
@@ -524,7 +486,7 @@ int BLI_parent_dir(char *path)
        }
 }
 
-int BLI_convertstringframe(char *path, int frame)
+static int stringframe_chars(char *path, int *char_start, int *char_end)
 {
        int ch_sta, ch_end, i;
        /* Insert current frame: file### -> file001 */
@@ -543,56 +505,82 @@ int BLI_convertstringframe(char *path, int frame)
                        /* dont break, there may be a slash after this that invalidates the previous #'s */
                }
        }
-       if (ch_end) { /* warning, ch_end is the last # +1 */
-               /* Add the frame number? */
-               short numlen, hashlen;
-               char tmp[FILE_MAX];
-               
-               char format[16]; /* 6 is realistically the maxframe (300000), so 8 should be enough, but 16 to be safe. */
-               if (((ch_end-1)-ch_sta) >= 16) {
-                       ch_end = ch_sta+15; /* disallow values longer then 'format' can hold */
+
+       if(ch_end) {
+               *char_start= ch_sta;
+               *char_end= ch_end;
+               return 1;
+       }
+       else {
+               *char_start= -1;
+               *char_end= -1;
+               return 0;
+       }
+}
+
+static void ensure_digits(char *path, int digits)
+{
+       char *file= BLI_last_slash(path);
+
+       if(file==NULL)
+               file= path;
+
+       if(strrchr(file, '#') == NULL) {
+               int len= strlen(file);
+
+               while(digits--) {
+                       file[len++]= '#';
                }
-               
-               strcpy(tmp, path);
-               
-               numlen = 1 + (int)log10((double)frame); /* this is the number of chars in the number */
-               hashlen = ch_end - ch_sta;
-               
-               sprintf(format, "%d", frame);
-               
-               if (numlen==hashlen) { /* simple case */
-                       memcpy(tmp+ch_sta, format, numlen);
-               } else if (numlen < hashlen) {
-                       memcpy(tmp+ch_sta + (hashlen-numlen), format, numlen); /*dont copy the string terminator */
-                       memset(tmp+ch_sta, '0', hashlen-numlen);
-               } else {
-                       /* number is longer then number of #'s */
-                       if (tmp[ch_end] == '\0') { /* hashes are last, no need to move any string*/
-                               /* bad juju - not testing string length here :/ */
-                               memcpy(tmp+ch_sta, format, numlen+1); /* add 1 to get the string terminator \0 */
-                       } else {
-                               /* we need to move the end characters, reuse i */
-                               int j;
-                               
-                               i = strlen(tmp); /* +1 to copy the string terminator */
-                               j = i + (numlen-hashlen); /* from/to */
-                               
-                               while (i >= ch_end) {
-                                       tmp[j] = tmp[i]; 
-                                       i--;
-                                       j--;
-                               }
-                               memcpy(tmp + ch_sta, format, numlen);
-                       }
-               }       
+               file[len]= '\0';
+       }
+}
+
+int BLI_path_frame(char *path, int frame, int digits)
+{
+       int ch_sta, ch_end;
+
+       if(digits)
+               ensure_digits(path, digits);
+
+       if (stringframe_chars(path, &ch_sta, &ch_end)) { /* warning, ch_end is the last # +1 */
+               char tmp[FILE_MAX];
+#if 0  // neat but breaks on non ascii strings.
+               char format[64];
+               sprintf(format, "%%.%ds%%.%dd%%s", ch_sta, ch_end-ch_sta); /* example result: "%.12s%.5d%s" */
+               sprintf(tmp, format, path, frame, path+ch_end);
+#else
+               char format[8];
+               char *p;
+               sprintf(format, "%%.%dd", ch_end-ch_sta); /* example result: "%.5d" */
+               memcpy(tmp, path, sizeof(char) * ch_sta);
+               p= tmp + ch_sta;
+               p += sprintf(p, format, frame);
+               memcpy(p, path + ch_end, strlen(path + ch_end));
+#endif
                strcpy(path, tmp);
                return 1;
        }
        return 0;
 }
 
+int BLI_path_frame_range(char *path, int sta, int end, int digits)
+{
+       int ch_sta, ch_end;
+
+       if(digits)
+               ensure_digits(path, digits);
+
+       if (stringframe_chars(path, &ch_sta, &ch_end)) { /* warning, ch_end is the last # +1 */
+               char tmp[FILE_MAX], format[64];
+               sprintf(format, "%%.%ds%%.%dd_%%.%dd%%s", ch_sta, ch_end-ch_sta, ch_end-ch_sta); /* example result: "%.12s%.5d-%.5d%s" */
+               sprintf(tmp, format, path, sta, end, path+ch_end);
+               strcpy(path, tmp);
+               return 1;
+       }
+       return 0;
+}
 
-int BLI_convertstringcode(char *path, const char *basepath)
+int BLI_path_abs(char *path, const char *basepath)
 {
        int wasrelative = (strncmp(path, "//", 2)==0);
        char tmp[FILE_MAX];
@@ -618,7 +606,7 @@ int BLI_convertstringcode(char *path, const char *basepath)
                BLI_strncpy(tmp, path, FILE_MAX);
        }
 #else
-       BLI_strncpy(tmp, path, FILE_MAX);
+       BLI_strncpy(tmp, path, sizeof(tmp));
        
        /* Check for loading a windows path on a posix system
         * in this case, there is no use in trying C:/ since it 
@@ -635,7 +623,7 @@ int BLI_convertstringcode(char *path, const char *basepath)
        
 #endif
 
-       BLI_strncpy(base, basepath, FILE_MAX);
+       BLI_strncpy(base, basepath, sizeof(base));
        
        BLI_cleanup_file(NULL, base);
        
@@ -654,21 +642,23 @@ int BLI_convertstringcode(char *path, const char *basepath)
                char *lslash= BLI_last_slash(base);
                if (lslash) {
                        int baselen= (int) (lslash-base) + 1;
-                       /* use path for for temp storage here, we copy back over it right away */
+                       /* use path for temp storage here, we copy back over it right away */
                        BLI_strncpy(path, tmp+2, FILE_MAX);
                        
                        memcpy(tmp, base, baselen);
-                       strcpy(tmp+baselen, path);
-                       strcpy(path, tmp);
+                       BLI_strncpy(tmp+baselen, path, sizeof(tmp)-baselen);
+                       BLI_strncpy(path, tmp, FILE_MAX);
                } else {
-                       strcpy(path, tmp+2);
+                       BLI_strncpy(path, tmp+2, FILE_MAX);
                }
        } else {
-               strcpy(path, tmp);
+               BLI_strncpy(path, tmp, FILE_MAX);
        }
        
        if (path[0]!='\0') {
                if ( path[strlen(path)-1]=='/') {
+                       /* remove the '/' so we avoid BLI_cleanup_dir adding an extra \ in WIN32 */
+                       path[strlen(path)-1] = '\0';
                        BLI_cleanup_dir(NULL, path);
                } else {
                        BLI_cleanup_file(NULL, path);
@@ -693,7 +683,7 @@ int BLI_convertstringcode(char *path, const char *basepath)
  * Should only be done with command line paths.
  * this is NOT somthing blenders internal paths support like the // prefix
  */
-int BLI_convertstringcwd(char *path)
+int BLI_path_cwd(char *path)
 {
        int wasrelative = 1;
        int filelen = strlen(path);
@@ -731,7 +721,7 @@ int BLI_convertstringcwd(char *path)
 }
 
 
-/* copy di to fi, filename only */
+/* 'di's filename component is moved into 'fi', di is made a dir path */
 void BLI_splitdirstring(char *di, char *fi)
 {
        char *lslash= BLI_last_slash(di);
@@ -764,208 +754,343 @@ void BLI_getlastdir(const char* dir, char *last, int maxlen)
        }
 }
 
-char *BLI_gethome(void) {
+/* 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 */
+char *BLI_getDefaultDocumentFolder(void) {
        #if !defined(WIN32)
                return getenv("HOME");
 
        #else /* Windows */
                char * ret;
-               static char dir[512];
-               static char appdatapath[MAXPATHLEN];
+               static char documentfolder[MAXPATHLEN];
                HRESULT hResult;
 
                /* Check for %HOME% env var */
 
                ret = getenv("HOME");
                if(ret) {
-                       sprintf(dir, "%s\\.blender", ret);
-                       if (BLI_exists(dir)) return dir;
-               }
-
-               /* else, check install dir (path containing blender.exe) */
-
-               BLI_getInstallationDir(dir);
-
-               if (BLI_exists(dir))
-               {
-                       strcat(dir,"\\.blender");
-                       if (BLI_exists(dir)) return(dir);
+                       if (BLI_is_dir(ret)) return ret;
                }
-
                                
-               /* add user profile support for WIN 2K / NT */
-               hResult = SHGetFolderPath(NULL, CSIDL_APPDATA, NULL, SHGFP_TYPE_CURRENT, appdatapath);
+               /* add user profile support for WIN 2K / NT.
+                * This is %APPDATA%, which translates to either
+                * %USERPROFILE%\Application Data or since Vista
+                * to %USERPROFILE%\AppData\Roaming
+                */
+               hResult = SHGetFolderPath(NULL, CSIDL_PERSONAL, NULL, SHGFP_TYPE_CURRENT, documentfolder);
                
                if (hResult == S_OK)
                {
-                       if (BLI_exists(appdatapath)) { /* from fop, also below... */
-                               sprintf(dir, "%s\\Blender Foundation\\Blender", appdatapath);
-                               BLI_recurdir_fileops(dir);
-                               if (BLI_exists(dir)) {
-                                       strcat(dir,"\\.blender");
-                                       if(BLI_exists(dir)) return(dir);
-                               }
-                       }
-                       hResult = SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, SHGFP_TYPE_CURRENT, appdatapath);
-                       if (hResult == S_OK)
-                       {
-                               if (BLI_exists(appdatapath)) 
-                               { /* from fop, also below... */
-                                       sprintf(dir, "%s\\Blender Foundation\\Blender", appdatapath);
-                                       BLI_recurdir_fileops(dir);
-                                       if (BLI_exists(dir)) {
-                                               strcat(dir,"\\.blender");
-                                               if(BLI_exists(dir)) return(dir);
-                                       }
-                               }
-                       }
+                       if (BLI_is_dir(documentfolder)) return documentfolder;
                }
-#if 0
-               ret = getenv("USERPROFILE");
-               if (ret) {
-                       if (BLI_exists(ret)) { /* from fop, also below...  */
-                               sprintf(dir, "%s\\Application Data\\Blender Foundation\\Blender", ret);
-                               BLI_recurdir_fileops(dir);
-                               if (BLI_exists(dir)) {
-                                       strcat(dir,"\\.blender");
-                                       if(BLI_exists(dir)) return(dir);
-                               }
-                       }
-               }
-#endif
-
-               /* 
-                  Saving in the Windows dir is less than desirable. 
-                  Use as a last resort ONLY! (aphex)
-               */
                
-               ret = getenv("WINDOWS");                
-               if (ret) {
-                       if(BLI_exists(ret)) return ret;
-               }
-
-               ret = getenv("WINDIR"); 
-               if (ret) {
-                       if(BLI_exists(ret)) return ret;
-               }
-               
-               return "C:\\Temp";      /* sheesh! bad, bad, bad! (aphex) */
+               return NULL;
        #endif
 }
 
-/* this function returns the path to a blender folder, if it exists
- * utility functions for BLI_gethome_folder */
+/* NEW stuff, to be cleaned up when fully migrated */
+/* ************************************************************* */
+/* ************************************************************* */
 
-/* #define PATH_DEBUG */ /* for testing paths that are checked */
+// #define PATH_DEBUG2
 
-static int test_data_path(char *targetpath, char *path_base, char *path_sep, char *folder_name)
+static char *blender_version_decimal(void)
 {
-       char tmppath[FILE_MAXDIR];
+       static char version_str[5];
+       sprintf(version_str, "%d.%02d", BLENDER_VERSION/100, BLENDER_VERSION%100);
+       return version_str;
+}
+
+static int test_path(char *targetpath, char *path_base, char *path_sep, char *folder_name)
+{
+       char tmppath[FILE_MAX];
        
        if(path_sep)    BLI_join_dirfile(tmppath, path_base, path_sep);
        else                    BLI_strncpy(tmppath, path_base, sizeof(tmppath));
        
        BLI_make_file_string("/", targetpath, tmppath, folder_name);
        
-       if (BLI_exists(targetpath)) {
-#ifdef PATH_DEBUG
+       if (BLI_is_dir(targetpath)) {
+#ifdef PATH_DEBUG2
                printf("\tpath found: %s\n", targetpath);
 #endif
                return 1;
        }
        else {
-#ifdef PATH_DEBUG
+#ifdef PATH_DEBUG2
                printf("\tpath missing: %s\n", targetpath);
 #endif
-               targetpath[0] = '\0';
+               //targetpath[0] = '\0';
                return 0;
        }
 }
 
-static int gethome_path_local(char *targetpath, char *folder_name)
+static int test_env_path(char *path, char *envvar)
+{
+       char *env = envvar?getenv(envvar):NULL;
+       if (!env) return 0;
+       
+       if (BLI_is_dir(env)) {
+               BLI_strncpy(path, env, FILE_MAX);
+               return 1;
+       } else {
+               path[0] = '\0';
+               return 0;
+       }
+}
+
+static int get_path_local(char *targetpath, char *folder_name, char *subfolder_name)
 {
        extern char bprogname[]; /* argv[0] from creator.c */
-       char bprogdir[FILE_MAXDIR];
-       char cwd[FILE_MAXDIR];
-       char *s;
-       int i;
+       char bprogdir[FILE_MAX];
+       char relfolder[FILE_MAX];
        
-#ifdef PATH_DEBUG
-       printf("gethome_path_local...\n");
+#ifdef PATH_DEBUG2
+       printf("get_path_local...\n");
 #endif
        
-       /* try release/folder_name (binary relative) */
+       if (subfolder_name) {
+               BLI_join_dirfile(relfolder, folder_name, subfolder_name);
+       } else {
+               BLI_strncpy(relfolder, folder_name, FILE_MAX);
+       }
+       
        /* use argv[0] (bprogname) to get the path to the executable */
-       s = BLI_last_slash(bprogname);
-       i = s - bprogname + 1;
-       BLI_strncpy(bprogdir, bprogname, i);
-
-       /* try release/folder_name (CWD relative) */
-       if(test_data_path(targetpath, BLI_getwdN(cwd), "release", folder_name))
-               return 1;
-
-       if(test_data_path(targetpath, bprogdir, "release", folder_name))
+       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(), relfolder))
                return 1;
 
-       /* try ./.blender/folder_name */
-       if(test_data_path(targetpath, bprogdir, ".blender", folder_name))
-               return 1;
-       
        return 0;
 }
 
-static int gethome_path_user(char *targetpath, char *folder_name)
+static int get_path_user(char *targetpath, char *folder_name, char *subfolder_name, char *envvar)
 {
-       char *home_path= BLI_gethome();
+       char user_path[FILE_MAX];
+       const char *user_base_path;
+       
+       user_path[0] = '\0';
 
-#ifdef PATH_DEBUG
-       printf("gethome_path_user...\n");
+       if (test_env_path(user_path, envvar)) {
+               if (subfolder_name) {
+                       return test_path(targetpath, user_path, NULL, subfolder_name);
+               } else {
+                       BLI_strncpy(targetpath, user_path, FILE_MAX);
+                       return 1;
+               }
+       }
+
+       user_base_path = (const char *)GHOST_getUserDir();
+       if (user_base_path) {
+               BLI_snprintf(user_path, FILE_MAX, BLENDER_USER_FORMAT, user_base_path, blender_version_decimal());
+       }
+
+       if(!user_path[0])
+               return 0;
+       
+#ifdef PATH_DEBUG2
+       printf("get_path_user: %s\n", user_path);
 #endif
        
-       /* try $HOME/folder_name */
-       return test_data_path(targetpath, home_path, ".blender", folder_name);
+       if (subfolder_name) {
+               /* try $HOME/folder_name/subfolder_name */
+               return test_path(targetpath, user_path, folder_name, subfolder_name);
+       } else {
+               /* try $HOME/folder_name */
+               return test_path(targetpath, user_path, NULL, folder_name);
+       }
 }
 
-static int gethome_path_system(char *targetpath, char *folder_name)
+static int get_path_system(char *targetpath, char *folder_name, char *subfolder_name, char *envvar)
 {
-       extern char blender_path[]; /* unix prefix eg. /usr/share/blender/2.5 creator.c */
+       char system_path[FILE_MAX];
+       const char *system_base_path;
+
+
+       /* first allow developer only overrides to the system path
+        * these are only used when running blender from source */
+       extern char bprogname[]; /* argv[0] from creator.c */
+       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 (subfolder_name) {
+               BLI_join_dirfile(relfolder, folder_name, subfolder_name);
+       } else {
+               BLI_strncpy(relfolder, folder_name, FILE_MAX);
+       }
+
+       /* try CWD/release/folder_name */
+       if(test_path(targetpath, BLI_getwdN(cwd), "release", relfolder))
+               return 1;
+       
+       /* try EXECUTABLE_DIR/release/folder_name */
+       if(test_path(targetpath, bprogdir, "release", relfolder))
+               return 1;
+       /* end developer overrides */
+
+
+
+       system_path[0] = '\0';
+
+       if (test_env_path(system_path, envvar)) {
+               if (subfolder_name) {
+                       return test_path(targetpath, system_path, NULL, subfolder_name);
+               } else {
+                       BLI_strncpy(targetpath, system_path, FILE_MAX);
+                       return 1;
+               }
+       }
+
+       system_base_path = (const char *)GHOST_getSystemDir();
+       if (system_base_path) {
+               BLI_snprintf(system_path, FILE_MAX, BLENDER_SYSTEM_FORMAT, system_base_path, blender_version_decimal());
+       }
        
-       if(!blender_path[0])
+       if(!system_path[0])
                return 0;
        
-#ifdef PATH_DEBUG
-       printf("gethome_path_system...\n");
+#ifdef PATH_DEBUG2
+       printf("get_path_system: %s\n", system_path);
 #endif
        
-       /* try $BLENDERPATH/folder_name */
-       return test_data_path(targetpath, blender_path, NULL, folder_name);
+       if (subfolder_name) {
+               /* try $BLENDERPATH/folder_name/subfolder_name */
+               return test_path(targetpath, system_path, folder_name, subfolder_name);
+       } else {
+               /* try $BLENDERPATH/folder_name */
+               return test_path(targetpath, system_path, NULL, folder_name);
+       }
 }
 
-char *BLI_gethome_folder(char *folder_name, int flag)
+/* get a folder out of the 'folder_id' presets for paths */
+/* returns the path if found, NULL string if not */
+char *BLI_get_folder(int folder_id, char *subfolder)
 {
-       static char fulldir[FILE_MAXDIR] = "";
-       
-       /* first check if this is a redistributable bundle */
-       if(flag & BLI_GETHOME_LOCAL) {
-               if (gethome_path_local(fulldir, folder_name))
-                       return fulldir;
+       static char path[FILE_MAX] = "";
+       
+       switch (folder_id) {
+               case BLENDER_DATAFILES:         /* general case */
+                       if (get_path_local(path, "datafiles", subfolder)) break;
+                       if (get_path_user(path, "datafiles", subfolder, "BLENDER_USER_DATAFILES"))      break;
+                       if (get_path_system(path, "datafiles", subfolder, "BLENDER_SYSTEM_DATAFILES")) break;
+                       return NULL;
+                       
+               case BLENDER_USER_DATAFILES:
+                       if (get_path_local(path, "datafiles", subfolder)) break;
+                       if (get_path_user(path, "datafiles", subfolder, "BLENDER_USER_DATAFILES"))      break;
+                       return NULL;
+                       
+               case BLENDER_SYSTEM_DATAFILES:
+                       if (get_path_local(path, "datafiles", subfolder)) break;
+                       if (get_path_system(path, "datafiles", subfolder, "BLENDER_SYSTEM_DATAFILES"))  break;
+                       return NULL;
+                       
+               case BLENDER_USER_AUTOSAVE:
+                       if (get_path_local(path, "autosave", subfolder)) break;
+                       if (get_path_user(path, "autosave", subfolder, "BLENDER_USER_DATAFILES"))       break;
+                       return NULL;
+
+               case BLENDER_CONFIG:            /* general case */
+                       if (get_path_local(path, "config", subfolder)) break;
+                       if (get_path_user(path, "config", subfolder, "BLENDER_USER_CONFIG")) break;
+                       if (get_path_system(path, "config", subfolder, "BLENDER_SYSTEM_CONFIG")) break;
+                       return NULL;
+                       
+               case BLENDER_USER_CONFIG:
+                       if (get_path_local(path, "config", subfolder)) break;
+                       if (get_path_user(path, "config", subfolder, "BLENDER_USER_CONFIG")) break;
+                       return NULL;
+                       
+               case BLENDER_SYSTEM_CONFIG:
+                       if (get_path_local(path, "config", subfolder)) break;
+                       if (get_path_system(path, "config", subfolder, "BLENDER_SYSTEM_CONFIG")) break;
+                       return NULL;
+                       
+               case BLENDER_SCRIPTS:           /* general case */
+                       if (get_path_local(path, "scripts", subfolder)) break;
+                       if (get_path_user(path, "scripts", subfolder, "BLENDER_USER_SCRIPTS")) break;           
+                       if (get_path_system(path, "scripts", subfolder, "BLENDER_SYSTEM_SCRIPTS")) break;
+                       return NULL;
+                       
+               case BLENDER_USER_SCRIPTS:
+                       if (get_path_local(path, "scripts", subfolder)) break;
+                       if (get_path_user(path, "scripts", subfolder, "BLENDER_USER_SCRIPTS")) break;
+                       return NULL;
+                       
+               case BLENDER_SYSTEM_SCRIPTS:
+                       if (get_path_local(path, "scripts", subfolder)) break;
+                       if (get_path_system(path, "scripts", subfolder, "BLENDER_SYSTEM_SCRIPTS")) break;
+                       return NULL;
+                       
+               case BLENDER_PYTHON:            /* general case */
+                       if (get_path_local(path, "python", subfolder)) break;
+                       if (get_path_system(path, "python", subfolder, "BLENDER_SYSTEM_PYTHON")) break;
+                       return NULL;
+                       
+               case BLENDER_SYSTEM_PYTHON:
+                       if (get_path_local(path, "python", subfolder)) break;
+                       if (get_path_system(path, "python", subfolder, "BLENDER_SYSTEM_PYTHON")) break;
+                       return NULL;
        }
+       
+       return path;
+}
+
+char *BLI_get_user_folder_notest(int folder_id, char *subfolder)
+{
+       static char path[FILE_MAX] = "";
 
-       /* then check if the OS has blender data files installed in a global location */
-       if(flag & BLI_GETHOME_SYSTEM) {
-               if (gethome_path_system(fulldir, folder_name))
-                       return fulldir;
+       switch (folder_id) {
+               case BLENDER_USER_DATAFILES:
+                       get_path_user(path, "datafiles", subfolder, "BLENDER_USER_DATAFILES");
+                       break;
+               case BLENDER_USER_CONFIG:
+                       get_path_user(path, "config", subfolder, "BLENDER_USER_CONFIG");
+                       break;
+               case BLENDER_USER_AUTOSAVE:
+                       get_path_user(path, "autosave", subfolder, "BLENDER_USER_AUTOSAVE");
+                       break;
+               case BLENDER_USER_SCRIPTS:
+                       get_path_user(path, "scripts", subfolder, "BLENDER_USER_SCRIPTS");
+                       break;
+       }
+       if ('\0' == path[0]) {
+               return NULL;
        }
+       return path;
+}
+
+char *BLI_get_folder_create(int folder_id, char *subfolder)
+{
+       char *path;
+
+       /* only for user folders */
+       if (!ELEM4(folder_id, BLENDER_USER_DATAFILES, BLENDER_USER_CONFIG, BLENDER_USER_SCRIPTS, BLENDER_USER_AUTOSAVE))
+               return NULL;
        
-       /* now check the users home dir for data files */
-       if(flag & BLI_GETHOME_USER) {
-               if (gethome_path_user(fulldir, folder_name))
-                       return fulldir;
+       path = BLI_get_folder(folder_id, subfolder);
+       
+       if (!path) {
+               path = BLI_get_user_folder_notest(folder_id, subfolder);
+               if (path) BLI_recurdir_fileops(path);
        }
        
-       return NULL;
+       return path;
 }
 
+
+/* End new stuff */
+/* ************************************************************* */
+/* ************************************************************* */
+
+
+
 #ifdef PATH_DEBUG
 #undef PATH_DEBUG
 #endif
@@ -974,11 +1099,11 @@ void BLI_setenv(const char *env, const char*val)
 {
        /* SGI or free windows */
 #if (defined(__sgi) || ((defined(WIN32) || defined(WIN64)) && defined(FREE_WINDOWS)))
-       char *envstr= malloc(sizeof(char) * (strlen(env) + strlen(val) + 2)); /* one for = another for \0 */
+       char *envstr= MEM_mallocN(sizeof(char) * (strlen(env) + strlen(val) + 2), "envstr"); /* one for = another for \0 */
 
        sprintf(envstr, "%s=%s", env, val);
        putenv(envstr);
-       free(envstr);
+       MEM_freeN(envstr);
 
        /* non-free windows */
 #elif (defined(WIN32) || defined(WIN64)) /* not free windows */
@@ -1025,49 +1150,35 @@ void BLI_char_switch(char *string, char from, char to)
 void BLI_make_exist(char *dir) {
        int a;
 
-       #ifdef WIN32
-               BLI_char_switch(dir, '/', '\\');
-       #else
-               BLI_char_switch(dir, '\\', '/');
-       #endif  
-       
+       BLI_char_switch(dir, ALTSEP, SEP);
+
        a = strlen(dir);
-       
-#ifdef WIN32   
-       while(BLI_exists(dir) == 0){
+
+       while(BLI_is_dir(dir) == 0){
                a --;
-               while(dir[a] != '\\'){
+               while(dir[a] != SEP){
                        a--;
                        if (a <= 0) break;
                }
-               if (a >= 0) dir[a+1] = 0;
+               if (a >= 0) {
+                       dir[a+1] = '\0';
+               }
                else {
-                       /* defaulting to drive (usually 'C:') of Windows installation */
+#ifdef WIN32
                        get_default_root(dir);
-                       break;
-               }
-       }
 #else
-       while(BLI_exist(dir) == 0){
-               a --;
-               while(dir[a] != '/'){
-                       a--;
-                       if (a <= 0) break;
-               }
-               if (a >= 0) dir[a+1] = 0;
-               else {
                        strcpy(dir,"/");
+#endif
                        break;
                }
        }
-#endif
 }
 
 void BLI_make_existing_file(char *name)
 {
-       char di[FILE_MAXDIR], fi[FILE_MAXFILE];
-       
-       strcpy(di, name);
+       char di[FILE_MAXDIR+FILE_MAXFILE], fi[FILE_MAXFILE];
+
+       BLI_strncpy(di, name, sizeof(di));
        BLI_splitdirstring(di, fi);
        
        /* test exist */
@@ -1101,8 +1212,7 @@ void BLI_make_file_string(const char *relabase, char *string,  const char *dir,
                /* Get the file name, chop everything past the last slash (ie. the filename) */
                strcpy(string, relabase);
                
-               lslash= (strrchr(string, '/')>strrchr(string, '\\'))?strrchr(string, '/'):strrchr(string, '\\');
-               
+               lslash= BLI_last_slash(string);
                if(lslash) *(lslash+1)= 0;
 
                dir+=2; /* Skip over the relative reference */
@@ -1170,25 +1280,81 @@ int BLI_testextensie(const char *str, const char *ext)
        return (retval);
 }
 
-/*
- * This is a simple version of BLI_split_dirfile that has the following advantages...
- * 
- * Converts "/foo/bar.txt" to "/foo/" and "bar.txt"
+int BLI_testextensie_array(const char *str, const char **ext_array)
+{
+       int i=0;
+       while(ext_array[i]) {
+               if(BLI_testextensie(str, ext_array[i])) {
+                       return 1;
+               }
+
+               i++;
+       }
+       return 0;
+}
+
+/* semicolon separated wildcards, eg:
+ *  '*.zip;*.py;*.exe' */
+int BLI_testextensie_glob(const char *str, const char *ext_fnmatch)
+{
+       const char *ext_step= ext_fnmatch;
+       char pattern[16];
+
+       while(ext_step[0]) {
+               char *ext_next;
+               int len_ext;
+
+               if((ext_next=strchr(ext_step, ';'))) {
+                       len_ext= (int)(ext_next - ext_step) + 1;
+               }
+               else {
+                       len_ext= sizeof(pattern);
+               }
+
+               BLI_strncpy(pattern, ext_step, len_ext);
+
+               if(fnmatch(pattern, str, FNM_CASEFOLD)==0) {
+                       return 1;
+               }
+               ext_step += len_ext;
+       }
+
+       return 0;
+}
+
+
+int BLI_replace_extension(char *path, int maxlen, const char *ext)
+{
+       int a;
+
+       for(a=strlen(path)-1; a>=0; a--)
+               if(path[a] == '.' || path[a] == '/' || path[a] == '\\')
+                       break;
+       
+       if(path[a] != '.')
+               a= strlen(path);
+
+       if(a + strlen(ext) >= maxlen)
+               return 0;
+
+       strcpy(path+a, ext);
+       return 1;
+}
+
+/* Converts "/foo/bar.txt" to "/foo/" and "bar.txt"
  * - wont change 'string'
  * - wont create any directories
  * - dosnt use CWD, or deal with relative paths.
  * - Only fill's in *dir and *file when they are non NULL
  * */
-void BLI_split_dirfile_basic(const char *string, char *dir, char *file)
+void BLI_split_dirfile(const char *string, char *dir, char *file)
 {
-       int lslash=0, i = 0;
-       for (i=0; string[i]!='\0'; i++) {
-               if (string[i]=='\\' || string[i]=='/')
-                       lslash = i+1;
-       }
+       char *lslash_str = BLI_last_slash(string);
+       int lslash= lslash_str ? (int)(lslash_str - string) + 1 : 0;
+
        if (dir) {
                if (lslash) {
-                       BLI_strncpy( dir, string, lslash+1); /* +1 to include the slash and the last char */
+                       BLI_strncpy( dir, string, lslash + 1); /* +1 to include the slash and the last char */
                } else {
                        dir[0] = '\0';
                }
@@ -1199,140 +1365,188 @@ void BLI_split_dirfile_basic(const char *string, char *dir, char *file)
        }
 }
 
+/* simple appending of filename to dir, does not check for valid path! */
+void BLI_join_dirfile(char *string, const char *dir, const char *file)
+{
+       int sl_dir;
+       
+       if(string != dir) /* compare pointers */
+               BLI_strncpy(string, dir, FILE_MAX);
+
+       if (!file)
+               return;
+       
+       sl_dir= BLI_add_slash(string);
+       
+       if (sl_dir <FILE_MAX) {
+               BLI_strncpy(string + sl_dir, file, FILE_MAX-sl_dir);
+       }
+}
+
+/* like pythons os.path.basename( ) */
+char *BLI_path_basename(char *path)
+{
+       char *filename= BLI_last_slash(path);
+       return filename ? filename + 1 : path;
+}
+
+/*
+  Produce image export path.
+
+  Fails returning 0 if image filename is empty or if destination path
+  matches image path (i.e. both are the same file).
+
+  Trailing slash in dest_dir is optional.
+
+  Logic:
+
+  - if an image is "below" current .blend file directory, rebuild the
+       same dir structure in dest_dir
 
-/* Warning,
- * - May modify 'string' variable
- * - May create the directory if it dosnt exist
- * if this is not needed use BLI_split_dirfile_basic(...)
+  For example //textures/foo/bar.png becomes
+  [dest_dir]/textures/foo/bar.png.
+
+  - if an image is not "below" current .blend file directory,
+  disregard it's path and copy it in the same directory where 3D file
+  goes.
+
+  For example //../foo/bar.png becomes [dest_dir]/bar.png.
+
+  This logic will help ensure that all image paths are relative and
+  that a user gets his images in one place. It'll also provide
+  consistent behaviour across exporters.
  */
-void BLI_split_dirfile(char *string, char *dir, char *file)
+int BKE_rebase_path(char *abs, int abs_size, char *rel, int rel_size, const char *base_dir, const char *src_dir, const char *dest_dir)
 {
-       int a;
-#ifdef WIN32
-       int sl;
-       short is_relative = 0;
        char path[FILE_MAX];
-#endif
+       char dir[FILE_MAX];
+       char base[FILE_MAX];
+       char blend_dir[FILE_MAX];       /* directory, where current .blend file resides */
+       char dest_path[FILE_MAX];
+       char rel_dir[FILE_MAX];
+       int len;
 
-       dir[0]= 0;
-       file[0]= 0;
+       if (abs)
+               abs[0]= 0;
 
-#ifdef WIN32
-       BLI_strncpy(path, string, FILE_MAX);
-       BLI_char_switch(path, '/', '\\'); /* make sure we have a valid path format */
-       sl = strlen(path);
-       if (sl) {
-               int len;
-               if (path[0] == '/' || path[0] == '\\') { 
-                       BLI_strncpy(dir, path, FILE_MAXDIR);
-                       if (sl > 1 && path[0] == '\\' && path[1] == '\\') is_relative = 1;
-               } else if (sl > 2 && path[1] == ':' && path[2] == '\\') {
-                       BLI_strncpy(dir, path, FILE_MAXDIR);
-               } else {
-                       BLI_getwdN(dir);
-                       strcat(dir,"\\");
-                       strcat(dir,path);
-                       BLI_strncpy(path,dir,FILE_MAXDIR+FILE_MAXFILE);
-               }
-               
-               // BLI_exist doesn't recognize a slashed dirname as a dir
-               //  check if a trailing slash exists, and remove it. Do not do this
-               //  when we are already at root. -jesterKing
-               a = strlen(dir);
-               if(a>=4 && dir[a-1]=='\\') dir[a-1] = 0;
-
-               if (is_relative) {
-                       printf("WARNING: BLI_split_dirfile needs absolute dir\n");
-               }
-               else {
-                       BLI_make_exist(dir);
-               }
+       if (rel)
+               rel[0]= 0;
 
-               if (S_ISDIR(BLI_exist(dir))) {
+       BLI_split_dirfile(base_dir, blend_dir, NULL);
 
-                       /* copy from end of string into file, to ensure filename itself isn't truncated 
-                       if string is too long. (aphex) */
+       if (src_dir[0]=='\0')
+               return 0;
 
-                       len = FILE_MAXFILE - strlen(path);
+       BLI_strncpy(path, src_dir, sizeof(path));
 
-                       if (len < 0)
-                               BLI_strncpy(file,path + abs(len),FILE_MAXFILE);
-                       else
-                               BLI_strncpy(file,path,FILE_MAXFILE);
-                   
-                       if (strrchr(path,'\\')) {
-                               BLI_strncpy(file,strrchr(path,'\\')+1,FILE_MAXFILE);
-                       }
-                       
-                       if ( (a = strlen(dir)) ) {
-                               if (dir[a-1] != '\\') strcat(dir,"\\");
-                       }
+       /* expand "//" in filename and get absolute path */
+       BLI_path_abs(path, base_dir);
+
+       /* get the directory part */
+       BLI_split_dirfile(path, dir, base);
+
+       len= strlen(blend_dir);
+
+       rel_dir[0] = 0;
+
+       /* if image is "below" current .blend file directory */
+       if (!strncmp(path, blend_dir, len)) {
+
+               /* if image is _in_ current .blend file directory */
+               if (!strcmp(dir, blend_dir)) {
+                       BLI_join_dirfile(dest_path, dest_dir, base);
                }
+               /* "below" */
                else {
-                       a = strlen(dir) - 1;
-                       while(a>0 && dir[a] != '\\') a--;
-                       dir[a + 1] = 0;
-                       BLI_strncpy(file, path + strlen(dir),FILE_MAXFILE);
+                       /* rel = image_path_dir - blend_dir */
+                       BLI_strncpy(rel_dir, dir + len, sizeof(rel_dir));
+
+                       BLI_join_dirfile(dest_path, dest_dir, rel_dir);
+                       BLI_join_dirfile(dest_path, dest_path, base);
                }
 
        }
+       /* image is out of current directory */
        else {
-               /* defaulting to first valid drive hoping it's not empty CD and DVD drives */
-               get_default_root(dir);
-               file[0]=0;
+               BLI_join_dirfile(dest_path, dest_dir, base);
        }
-#else
-       if (strlen(string)) {
-               if (string[0] == '/') { 
-                       strcpy(dir, string);
-               } else if (string[1] == ':' && string[2] == '\\') {
-                       string+=2;
-                       strcpy(dir, string);
-               } else {
-                       BLI_getwdN(dir);
-                       strcat(dir,"/");
-                       strcat(dir,string);
-                       strcpy((char *)string,dir);
-               }
 
-               BLI_make_exist(dir);
-                       
-               if (S_ISDIR(BLI_exist(dir))) {
-                       strcpy(file,string + strlen(dir));
+       if (abs)
+               BLI_strncpy(abs, dest_path, abs_size);
 
-                       if (strrchr(file,'/')) strcpy(file,strrchr(file,'/')+1);
-               
-                       if ( (a = strlen(dir)) ) {
-                               if (dir[a-1] != '/') strcat(dir,"/");
-                       }
-               }
-               else {
-                       a = strlen(dir) - 1;
-                       while(dir[a] != '/') a--;
-                       dir[a + 1] = 0;
-                       strcpy(file, string + strlen(dir));
-               }
+       if (rel) {
+               strncat(rel, rel_dir, rel_size);
+               strncat(rel, base, rel_size);
        }
-       else {
-               BLI_getwdN(dir);
-               strcat(dir, "/");
-               file[0] = 0;
+
+       /* return 2 if src=dest */
+       if (!strcmp(path, dest_path)) {
+               // if (G.f & G_DEBUG) printf("%s and %s are the same file\n", path, dest_path);
+               return 2;
        }
-#endif
+
+       return 1;
 }
 
-/* simple appending of filename to dir, does not check for valid path! */
-void BLI_join_dirfile(char *string, const char *dir, const char *file)
-{
-       int sl_dir;
+char *BLI_first_slash(char *string) {
+       char *ffslash, *fbslash;
        
-       if(string != dir) /* compare pointers */
-               BLI_strncpy(string, dir, FILE_MAX);
+       ffslash= strchr(string, '/');   
+       fbslash= strchr(string, '\\');
        
-       sl_dir= BLI_add_slash(string);
+       if (!ffslash) return fbslash;
+       else if (!fbslash) return ffslash;
        
-       if (sl_dir <FILE_MAX) {
-               BLI_strncpy(string + sl_dir, file, FILE_MAX-sl_dir);
+       if ((intptr_t)ffslash < (intptr_t)fbslash) return ffslash;
+       else return fbslash;
+}
+
+char *BLI_last_slash(const char *string) {
+       char *lfslash, *lbslash;
+       
+       lfslash= strrchr(string, '/');  
+       lbslash= strrchr(string, '\\');
+
+       if (!lfslash) return lbslash; 
+       else if (!lbslash) return lfslash;
+       
+       if ((intptr_t)lfslash < (intptr_t)lbslash) return lbslash;
+       else return lfslash;
+}
+
+/* adds a slash if there isnt one there already */
+int BLI_add_slash(char *string) {
+       int len = strlen(string);
+#ifdef WIN32
+       if (len==0 || string[len-1]!='\\') {
+               string[len] = '\\';
+               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) {
+       int len = strlen(string);
+       while (len) {
+#ifdef WIN32
+               if (string[len-1]=='\\') {
+#else
+               if (string[len-1]=='/') {
+#endif
+                       string[len-1] = '\0';
+                       len--;
+               } else {
+                       break;
+               }
        }
 }
 
@@ -1395,7 +1609,7 @@ void BLI_where_am_i(char *fullname, const char *name)
        /* linux uses binreloc since argv[0] is not relyable, call br_init( NULL ) first */
        path = br_find_exe( NULL );
        if (path) {
-               strcpy(fullname, path);
+               BLI_strncpy(fullname, path, FILE_MAXDIR+FILE_MAXFILE);
                free(path);
                return;
        }
@@ -1447,7 +1661,7 @@ void BLI_where_am_i(char *fullname, const char *name)
                                } while (temp);
                        }
                }
-#ifndef NDEBUG
+#if defined(DEBUG)
                if (strcmp(name, fullname)) {
                        printf("guessing '%s' == '%s'\n", name, fullname);
                }
@@ -1460,7 +1674,7 @@ void BLI_where_am_i(char *fullname, const char *name)
                // with spawnv(P_WAIT, bprogname, argv) instead of system() but
                // that's even uglier
                GetShortPathName(fullname, fullname, FILE_MAXDIR+FILE_MAXFILE);
-#ifndef NDEBUG
+#if defined(DEBUG)
                printf("Shortname = '%s'\n", fullname);
 #endif
 #endif
@@ -1471,7 +1685,7 @@ void BLI_where_is_temp(char *fullname, int usertemp)
 {
        fullname[0] = '\0';
        
-       if (usertemp && BLI_exists(U.tempdir)) {
+       if (usertemp && BLI_is_dir(U.tempdir)) {
                strcpy(fullname, U.tempdir);
        }
        
@@ -1479,7 +1693,7 @@ void BLI_where_is_temp(char *fullname, int usertemp)
 #ifdef WIN32
        if (fullname[0] == '\0') {
                char *tmp = getenv("TEMP"); /* Windows */
-               if (tmp && BLI_exists(tmp)) {
+               if (tmp && BLI_is_dir(tmp)) {
                        strcpy(fullname, tmp);
                }
        }
@@ -1487,14 +1701,14 @@ void BLI_where_is_temp(char *fullname, int usertemp)
        /* Other OS's - Try TMP and TMPDIR */
        if (fullname[0] == '\0') {
                char *tmp = getenv("TMP");
-               if (tmp && BLI_exists(tmp)) {
+               if (tmp && BLI_is_dir(tmp)) {
                        strcpy(fullname, tmp);
                }
        }
        
        if (fullname[0] == '\0') {
                char *tmp = getenv("TMPDIR");
-               if (tmp && BLI_exists(tmp)) {
+               if (tmp && BLI_is_dir(tmp)) {
                        strcpy(fullname, tmp);
                }
        }
@@ -1505,6 +1719,9 @@ void BLI_where_is_temp(char *fullname, int usertemp)
        } else {
                /* add a trailing slash if needed */
                BLI_add_slash(fullname);
+#ifdef WIN32
+               strcpy(U.tempdir, fullname); /* also set user pref to show %TEMP%. /tmp/ is just plain confusing for Windows users. */
+#endif
        }
 }
 
@@ -1529,29 +1746,7 @@ char *get_install_dir(void) {
        }
 }
 
-/* 
- * returns absolute path to the app bundle
- * only useful on OS X 
- */
-#ifdef __APPLE__
-char* BLI_getbundle(void) {
-       CFURLRef bundleURL;
-       CFStringRef pathStr;
-       static char path[MAXPATHLEN];
-       CFBundleRef mainBundle = CFBundleGetMainBundle();
-
-       bundleURL = CFBundleCopyBundleURL(mainBundle);
-       pathStr = CFURLCopyFileSystemPath(bundleURL, kCFURLPOSIXPathStyle);
-       CFStringGetCString(pathStr, path, MAXPATHLEN, kCFStringEncodingASCII);
-       CFRelease(pathStr);
-       CFRelease(bundleURL);
-       return path;
-}
-#endif
-
 #ifdef WITH_ICONV
-#include "iconv.h"
-#include "localcharset.h"
 
 void BLI_string_to_utf8(char *original, char *utf_8, const char *code)
 {