Files
blender/source/blender/blenlib/intern/path_util.c

1935 lines
45 KiB
C
Raw Normal View History

/*
* ***** BEGIN GPL LICENSE BLOCK *****
2002-10-12 11:37:38 +00:00
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
2002-10-12 11:37:38 +00:00
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
2010-02-12 13:34:04 +00:00
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2002-10-12 11:37:38 +00:00
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
*
* The Original Code is: all of this file.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*
* various string, file, list operations.
2002-10-12 11:37:38 +00:00
*/
2011-02-27 20:37:56 +00:00
/** \file blender/blenlib/intern/path_util.c
* \ingroup bli
*/
2002-10-12 11:37:38 +00:00
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
2002-10-12 11:37:38 +00:00
#include "MEM_guardedalloc.h"
#include "DNA_listBase.h"
#include "BLI_fileops.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
2002-10-12 11:37:38 +00:00
#include "BKE_utildefines.h"
#include "BKE_blender.h" // BLENDER_VERSION
#include "GHOST_Path-api.h"
#if defined WIN32 && !defined _LIBC
# include "BLI_fnmatch.h" /* use fnmatch included in blenlib */
#else
# ifndef _GNU_SOURCE
# define _GNU_SOURCE
# endif
# include <fnmatch.h>
#endif
2002-10-12 11:37:38 +00:00
#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"
#else /* non windows */
# 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"
#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
2002-10-12 11:37:38 +00:00
/* local */
#define UNIQUE_NAME_MAX 128
2002-10-12 11:37:38 +00:00
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 */
2002-10-12 11:37:38 +00:00
static int add_win32_extension(char *name);
static char *blender_version_decimal(const int ver);
2002-10-12 11:37:38 +00:00
/* implementation */
int BLI_stringdec(const char *string, char *head, char *tail, unsigned short *numlen)
2002-10-12 11:37:38 +00:00
{
unsigned short len, len2, lenlslash = 0, nums = 0, nume = 0;
2002-10-12 11:37:38 +00:00
short i, found = 0;
char *lslash = BLI_last_slash(string);
len2 = len = strlen(string);
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--) {
2002-10-12 11:37:38 +00:00
if (isdigit(string[i])) {
if (found){
nums = i;
}
else{
nume = i;
nums = i;
found = 1;
}
}
else {
2002-10-12 11:37:38 +00:00
if (found) break;
}
}
if (found) {
if (tail) strcpy(tail, &string[nume+1]);
if (head) {
strcpy(head,string);
head[nums]=0;
}
2002-10-12 11:37:38 +00:00
if (numlen) *numlen = nume-nums+1;
return ((int)atoi(&(string[nums])));
}
if (tail) strcpy(tail, string + len);
if (head) {
strncpy(head, string, len);
head[len] = '\0';
}
2002-10-12 11:37:38 +00:00
if (numlen) *numlen=0;
return 0;
}
void BLI_stringenc(char *string, const char *head, const char *tail, unsigned short numlen, int pic)
2002-10-12 11:37:38 +00:00
{
char fmtstr[16]="";
if(pic < 0) pic= 0;
sprintf(fmtstr, "%%s%%.%dd%%s", numlen);
sprintf(string, fmtstr, head, pic, tail);
2002-10-12 11:37:38 +00:00
}
/* Foo.001 -> "Foo", 1
* Returns the length of "Foo" */
int BLI_split_name_num(char *left, int *nr, const char *name, const char delim)
{
int a;
*nr= 0;
a= strlen(name);
memcpy(left, name, (a + 1) * sizeof(char));
if(a>1 && name[a-1]==delim) return a;
while(a--) {
if( name[a]==delim ) {
left[a]= 0;
*nr= atol(name+a+1);
/* casting down to an int, can overflow for large numbers */
if(*nr < 0)
*nr= 0;
return a;
}
if( isdigit(name[a])==0 ) break;
left[a]= 0;
}
for(a= 0; name[a]; a++)
left[a]= name[a];
return a;
}
2002-10-12 11:37:38 +00:00
void BLI_newname(char *name, int add)
2002-10-12 11:37:38 +00:00
{
char head[UNIQUE_NAME_MAX], tail[UNIQUE_NAME_MAX];
2002-10-12 11:37:38 +00:00
int pic;
unsigned short digits;
pic = BLI_stringdec(name, head, tail, &digits);
/* are we going from 100 -> 99 or from 10 -> 9 */
2002-10-12 11:37:38 +00:00
if (add < 0 && digits < 4 && digits > 0) {
int i, exp;
exp = 1;
for (i = digits; i > 1; i--) exp *= 10;
if (pic >= exp && (pic + add) < exp) digits--;
}
pic += add;
if (digits==4 && pic<0) pic= 0;
2002-10-12 11:37:38 +00:00
BLI_stringenc(name, head, tail, digits, pic);
}
int BLI_uniquename_cb(int (*unique_check)(void *, const char *), void *arg, const char defname[], char delim, char *name, short name_len)
{
if(name[0] == '\0') {
BLI_strncpy(name, defname, name_len);
}
if(unique_check(arg, name)) {
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';
}
} while(unique_check(arg, tempname));
BLI_strncpy(name, tempname, name_len);
return 1;
}
return 0;
}
/* little helper macro for BLI_uniquename */
#ifndef GIVE_STRADDR
#define GIVE_STRADDR(data, offset) ( ((char *)data) + offset )
#endif
/* Generic function to set a unique name. It is only designed to be used in situations
* where the name is part of the struct, and also that the name is at most UNIQUE_NAME_MAX chars long.
*
* For places where this is used, see constraint.c for example...
*
* name_offs: should be calculated using offsetof(structname, membername) macro from stddef.h
* len: maximum length of string (to prevent overflows, etc.)
* defname: the name that should be used by default if none is specified already
* delim: the character which acts as a delimeter between parts of the name
*/
static int uniquename_find_dupe(ListBase *list, void *vlink, const char *name, short name_offs)
{
Link *link;
for (link = list->first; link; link= link->next) {
if (link != vlink) {
if (!strcmp(GIVE_STRADDR(link, name_offs), name)) {
return 1;
}
}
}
return 0;
}
static int uniquename_unique_check(void *arg, const char *name)
{
struct {ListBase *lb; void *vlink; short name_offs;} *data= arg;
return uniquename_find_dupe(data->lb, data->vlink, name, data->name_offs);
}
void BLI_uniquename(ListBase *list, void *vlink, const char defname[], char delim, short name_offs, short name_len)
{
struct {ListBase *lb; void *vlink; short name_offs;} data;
data.lb= list;
data.vlink= vlink;
data.name_offs= name_offs;
assert((name_len > 1) && (name_len <= UNIQUE_NAME_MAX));
/* See if we are given an empty string */
if (ELEM(NULL, vlink, defname))
return;
BLI_uniquename_cb(uniquename_unique_check, &data, defname, delim, GIVE_STRADDR(vlink, name_offs), name_len);
}
/* ******************** string encoding ***************** */
/* This is quite an ugly function... its purpose is to
* take the dir name, make it absolute, and clean it up, replacing
* excess file entry stuff (like /tmp/../tmp/../)
* note that dir isn't protected for max string names...
*
* If relbase is NULL then its ignored
*/
void BLI_cleanup_path(const char *relabase, char *dir)
{
short a;
char *start, *eind;
if (relabase) {
BLI_path_abs(dir, relabase);
} else {
if (dir[0]=='/' && dir[1]=='/') {
if (dir[2]== '\0') {
return; /* path is "//" - cant clean it */
}
dir = dir+2; /* skip the first // */
}
}
/* Note
* memmove( start, eind, strlen(eind)+1 );
* is the same as
* strcpy( start, eind );
* except strcpy should not be used because there is overlap,
* so use memmove's slightly more obscure syntax - Campbell
*/
#ifdef WIN32
/* Note, this should really be moved to the file selector,
* since this function is used in many areas */
if(strcmp(dir, ".")==0) { /* happens for example in FILE_MAIN */
get_default_root(dir);
return;
}
while ( (start = strstr(dir, "\\..\\")) ) {
eind = start + strlen("\\..\\") - 1;
a = start-dir-1;
while (a>0) {
if (dir[a] == '\\') break;
a--;
}
if (a<0) {
break;
} else {
memmove( dir+a, eind, strlen(eind)+1 );
}
}
while ( (start = strstr(dir,"\\.\\")) ){
eind = start + strlen("\\.\\") - 1;
memmove( start, eind, strlen(eind)+1 );
}
while ( (start = strstr(dir,"\\\\" )) ){
eind = start + strlen("\\\\") - 1;
memmove( start, eind, strlen(eind)+1 );
}
#else
if(dir[0]=='.') { /* happens, for example in FILE_MAIN */
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 + (4 - 1) /* strlen("/../") - 1 */;
a = start-dir-1;
while (a>0) {
if (dir[a] == '/') break;
a--;
}
if (a<0) {
break;
} else {
memmove( dir+a, eind, strlen(eind)+1 );
}
}
while ( (start = strstr(dir,"/./")) ){
eind = start + (3 - 1) /* strlen("/./") - 1 */;
memmove( start, eind, strlen(eind)+1 );
}
while ( (start = strstr(dir,"//" )) ){
eind = start + (2 - 1) /* strlen("//") - 1 */;
memmove( start, eind, strlen(eind)+1 );
}
#endif
}
void BLI_cleanup_dir(const char *relabase, char *dir)
{
BLI_cleanup_path(relabase, dir);
BLI_add_slash(dir);
}
void BLI_cleanup_file(const char *relabase, char *dir)
{
BLI_cleanup_path(relabase, dir);
BLI_del_slash(dir);
}
void BLI_path_rel(char *file, const char *relfile)
2002-10-12 11:37:38 +00:00
{
char * lslash;
char temp[FILE_MAXDIR+FILE_MAXFILE];
char res[FILE_MAXDIR+FILE_MAXFILE];
/* if file is already relative, bail out */
if(file[0]=='/' && file[1]=='/') return;
/* also bail out if relative path is not set */
if (relfile[0] == 0) return;
#ifdef WIN32
if (BLI_strnlen(relfile, 3) > 2 && relfile[1] != ':') {
char* ptemp;
/* fix missing volume name in relative base,
can happen with old recent-files.txt files */
get_default_root(temp);
ptemp = &temp[2];
if (relfile[0] != '\\' && relfile[0] != '/') {
ptemp++;
}
BLI_strncpy(ptemp, relfile, FILE_MAXDIR + FILE_MAXFILE-3);
} else {
BLI_strncpy(temp, relfile, FILE_MAXDIR + FILE_MAXFILE);
}
if (BLI_strnlen(file, 3) > 2) {
if ( temp[1] == ':' && file[1] == ':' && temp[0] != file[0] )
return;
}
#else
BLI_strncpy(temp, relfile, FILE_MAX);
#endif
BLI_char_switch(temp, '\\', '/');
BLI_char_switch(file, '\\', '/');
/* remove /./ which confuse the following slash counting... */
BLI_cleanup_path(NULL, file);
BLI_cleanup_path(NULL, temp);
/* the last slash in the file indicates where the path part ends */
lslash = BLI_last_slash(temp);
if (lslash)
{
/* find the prefix of the filename that is equal for both filenames.
This is replaced by the two slashes at the beginning */
char *p= temp;
char *q= file;
#ifdef WIN32
while (tolower(*p) == tolower(*q))
#else
while (*p == *q)
#endif
{
++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
*/
if (*q != '/') {
while ( (q >= file) && (*q != '/') ) { --q; --p; }
}
else if (*p != '/') {
while ( (p >= temp) && (*p != '/') ) { --p; --q; }
}
strcpy(res, "//");
/* p now points to the slash that is at the beginning of the part
where the path is different from the relative path.
We count the number of directories we need to go up in the
hierarchy to arrive at the common 'prefix' of the path
*/
while (p && p < lslash) {
if (*p == '/')
strcat(res, "../");
++p;
2002-10-12 11:37:38 +00:00
}
strcat(res, q+1); /* don't copy the slash at the beginning */
#ifdef WIN32
BLI_char_switch(res+2, '/', '\\');
#endif
strcpy(file, res);
2002-10-12 11:37:38 +00:00
}
}
int BLI_has_parent(char *path)
{
int len;
int slashes = 0;
BLI_clean(path);
len = BLI_add_slash(path) - 1;
while (len>=0) {
if ((path[len] == '\\') || (path[len] == '/'))
slashes++;
len--;
}
return slashes > 1;
}
int BLI_parent_dir(char *path)
2002-10-12 11:37:38 +00:00
{
static char parent_dir[]= {'.', '.', SEP, '\0'}; /* "../" or "..\\" */
char tmp[FILE_MAXDIR+FILE_MAXFILE+4];
BLI_strncpy(tmp, path, sizeof(tmp)-4);
BLI_add_slash(tmp);
strcat(tmp, parent_dir);
BLI_cleanup_dir(NULL, tmp);
if (!BLI_testextensie(tmp, parent_dir)) {
BLI_strncpy(path, tmp, sizeof(tmp));
return 1;
} else {
return 0;
}
}
static int stringframe_chars(char *path, int *char_start, int *char_end)
{
int ch_sta, ch_end, i;
/* Insert current frame: file### -> file001 */
ch_sta = ch_end = 0;
for (i = 0; path[i] != '\0'; i++) {
if (path[i] == '\\' || path[i] == '/') {
ch_end = 0; /* this is a directory name, dont use any hashes we found */
} else if (path[i] == '#') {
ch_sta = i;
ch_end = ch_sta+1;
while (path[ch_end] == '#') {
ch_end++;
}
i = ch_end-1; /* keep searching */
/* dont break, there may be a slash after this that invalidates the previous #'s */
}
}
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++]= '#';
}
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];
sprintf(tmp, "%.*s%.*d%s", ch_sta, path, ch_end-ch_sta, frame, path+ch_end);
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];
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);
return 1;
}
return 0;
}
int BLI_path_abs(char *path, const char *basepath)
2002-10-12 11:37:38 +00:00
{
int wasrelative = (strncmp(path, "//", 2)==0);
char tmp[FILE_MAX];
char base[FILE_MAX];
#ifdef WIN32
char vol[3] = {'\0', '\0', '\0'};
BLI_strncpy(vol, path, 3);
/* we are checking here if we have an absolute path that is not in the current
blend file as a lib main - we are basically checking for the case that a
UNIX root '/' is passed.
*/
if (!wasrelative && (vol[1] != ':' && (vol[0] == '\0' || vol[0] == '/' || vol[0] == '\\'))) {
char *p = path;
get_default_root(tmp);
// get rid of the slashes at the beginning of the path
while (*p == '\\' || *p == '/') {
p++;
}
strcat(tmp, p);
}
else {
BLI_strncpy(tmp, path, FILE_MAX);
}
#else
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
* will never exist on a unix os.
*
* Add a / prefix and lowercase the driveletter, remove the :
* C:\foo.JPG -> /c/foo.JPG */
if (isalpha(tmp[0]) && tmp[1] == ':' && (tmp[2]=='\\' || tmp[2]=='/') ) {
tmp[1] = tolower(tmp[0]); /* replace ':' with driveletter */
tmp[0] = '/';
/* '\' the slash will be converted later */
}
#endif
BLI_strncpy(base, basepath, sizeof(base));
/* file component is ignored, so dont bother with the trailing slash */
BLI_cleanup_path(NULL, base);
/* push slashes into unix mode - strings entering this part are
potentially messed up: having both back- and forward slashes.
Here we push into one conform direction, and at the end we
push them into the system specific dir. This ensures uniformity
of paths and solving some problems (and prevent potential future
ones) -jesterKing. */
BLI_char_switch(tmp, '\\', '/');
BLI_char_switch(base, '\\', '/');
2002-10-12 11:37:38 +00:00
/* Paths starting with // will get the blend file as their base,
* this isnt standard in any os but is uesed in blender all over the place */
if (wasrelative) {
char *lslash= BLI_last_slash(base);
2002-10-12 11:37:38 +00:00
if (lslash) {
int baselen= (int) (lslash-base) + 1;
2010-04-25 15:24:18 +00:00
/* use path for temp storage here, we copy back over it right away */
BLI_strncpy(path, tmp+2, FILE_MAX);
memcpy(tmp, base, baselen);
BLI_strncpy(tmp+baselen, path, sizeof(tmp)-baselen);
BLI_strncpy(path, tmp, FILE_MAX);
2002-10-12 11:37:38 +00:00
} else {
BLI_strncpy(path, tmp+2, FILE_MAX);
2002-10-12 11:37:38 +00:00
}
} else {
BLI_strncpy(path, tmp, FILE_MAX);
2002-10-12 11:37:38 +00:00
}
BLI_cleanup_path(NULL, path);
#ifdef WIN32
/* skip first two chars, which in case of
absolute path will be drive:/blabla and
in case of relpath //blabla/. So relpath
// will be retained, rest will be nice and
shiny win32 backward slashes :) -jesterKing
*/
BLI_char_switch(path+2, '/', '\\');
#endif
2002-10-12 11:37:38 +00:00
return wasrelative;
}
/*
* Should only be done with command line paths.
* this is NOT somthing blenders internal paths support like the // prefix
*/
int BLI_path_cwd(char *path)
{
int wasrelative = 1;
int filelen = strlen(path);
#ifdef WIN32
if (filelen >= 3 && path[1] == ':' && (path[2] == '\\' || path[2] == '/'))
wasrelative = 0;
#else
if (filelen >= 2 && path[0] == '/')
wasrelative = 0;
#endif
if (wasrelative==1) {
char cwd[FILE_MAXDIR + FILE_MAXFILE]= "";
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.");
} else {
/* uses the blend path relative to cwd important for loading relative linked files.
*
* cwd should contain c:\ etc on win32 so the relbase can be NULL
* relbase being NULL also prevents // being misunderstood as relative to the current
* 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);
BLI_make_file_string(NULL, path, cwd, origpath);
}
}
2002-10-12 11:37:38 +00:00
return wasrelative;
}
2010-06-17 14:22:54 +00:00
/* 'di's filename component is moved into 'fi', di is made a dir path */
void BLI_splitdirstring(char *di, char *fi)
2002-10-12 11:37:38 +00:00
{
char *lslash= BLI_last_slash(di);
if (lslash) {
BLI_strncpy(fi, lslash+1, FILE_MAXFILE);
2002-10-12 11:37:38 +00:00
*(lslash+1)=0;
} else {
BLI_strncpy(fi, di, FILE_MAXFILE);
2002-10-12 11:37:38 +00:00
di[0]= 0;
}
}
void BLI_getlastdir(const char* dir, char *last, const size_t maxlen)
{
const char *s = dir;
const char *lslash = NULL;
const char *prevslash = NULL;
while (*s) {
if ((*s == '\\') || (*s == '/')) {
prevslash = lslash;
lslash = s;
}
s++;
}
if (prevslash) {
BLI_strncpy(last, prevslash+1, maxlen);
} else {
BLI_strncpy(last, dir, 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)
{
#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
2002-10-12 11:37:38 +00:00
return getenv("HOME");
#else /* Windows */
const char * ret;
static char documentfolder[MAXPATHLEN];
HRESULT hResult;
2002-10-12 11:37:38 +00:00
/* Check for %HOME% env var */
2002-10-12 11:37:38 +00:00
ret = getenv("HOME");
if(ret) {
if (BLI_is_dir(ret)) return ret;
2002-10-12 11:37:38 +00:00
}
/* 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_is_dir(documentfolder)) return documentfolder;
}
2002-10-12 11:37:38 +00:00
return NULL;
#endif /* WIN32 */
2002-10-12 11:37:38 +00:00
}
/* NEW stuff, to be cleaned up when fully migrated */
/* ************************************************************* */
/* ************************************************************* */
// #define PATH_DEBUG2
static char *blender_version_decimal(const int ver)
{
static char version_str[5];
sprintf(version_str, "%d.%02d", ver/100, ver%100);
return version_str;
}
static int test_path(char *targetpath, const char *path_base, const char *path_sep, const char *folder_name)
{
char tmppath[FILE_MAX];
if(path_sep) BLI_join_dirfile(tmppath, sizeof(tmppath), path_base, path_sep);
else BLI_strncpy(tmppath, path_base, sizeof(tmppath));
/* rare cases folder_name is omitted (when looking for ~/.blender/2.xx dir only) */
if(folder_name)
BLI_make_file_string("/", targetpath, tmppath, folder_name);
else
BLI_strncpy(targetpath, tmppath, sizeof(tmppath));
if (BLI_is_dir(targetpath)) {
#ifdef PATH_DEBUG2
printf("\tpath found: %s\n", targetpath);
#endif
return 1;
}
else {
#ifdef PATH_DEBUG2
printf("\tpath missing: %s\n", targetpath);
#endif
//targetpath[0] = '\0';
return 0;
}
}
static int test_env_path(char *path, const char *envvar)
{
const 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, const char *folder_name, const char *subfolder_name, const int ver)
{
char relfolder[FILE_MAX];
#ifdef PATH_DEBUG2
printf("get_path_local...\n");
#endif
if(folder_name) {
if (subfolder_name) {
BLI_join_dirfile(relfolder, sizeof(relfolder), folder_name, subfolder_name);
} else {
BLI_strncpy(relfolder, folder_name, sizeof(relfolder));
}
}
else {
relfolder[0]= '\0';
}
/* 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;
return 0;
}
static int is_portable_install(void)
{
/* detect portable install by the existance of config folder */
const int ver= BLENDER_VERSION;
char path[FILE_MAX];
return get_path_local(path, "config", NULL, ver);
}
static int get_path_user(char *targetpath, const char *folder_name, const char *subfolder_name, const char *envvar, const int ver)
{
char user_path[FILE_MAX];
const char *user_base_path;
/* for portable install, user path is always local */
if (is_portable_install())
return get_path_local(targetpath, folder_name, subfolder_name, ver);
user_path[0] = '\0';
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(ver));
}
if(!user_path[0])
return 0;
#ifdef PATH_DEBUG2
printf("get_path_user: %s\n", user_path);
#endif
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 get_path_system(char *targetpath, const char *folder_name, const char *subfolder_name, const char *envvar, const int ver)
{
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 */
char cwd[FILE_MAX];
char relfolder[FILE_MAX];
if(folder_name) {
if (subfolder_name) {
BLI_join_dirfile(relfolder, sizeof(relfolder), folder_name, subfolder_name);
} else {
BLI_strncpy(relfolder, folder_name, sizeof(relfolder));
}
}
else {
relfolder[0]= '\0';
}
/* try CWD/release/folder_name */
if(BLI_current_working_dir(cwd, sizeof(cwd))) {
if(test_path(targetpath, 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(ver));
}
if(!system_path[0])
return 0;
#ifdef PATH_DEBUG2
printf("get_path_system: %s\n", system_path);
#endif
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);
}
}
/* 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, const char *subfolder)
{
const int ver= BLENDER_VERSION;
static char path[FILE_MAX] = "";
switch (folder_id) {
case BLENDER_DATAFILES: /* general case */
if (get_path_user(path, "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver)) break;
if (get_path_local(path, "datafiles", subfolder, ver)) break;
if (get_path_system(path, "datafiles", subfolder, "BLENDER_SYSTEM_DATAFILES", ver)) break;
return NULL;
case BLENDER_USER_DATAFILES:
if (get_path_user(path, "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver)) break;
return NULL;
case BLENDER_SYSTEM_DATAFILES:
if (get_path_local(path, "datafiles", subfolder, ver)) break;
if (get_path_system(path, "datafiles", subfolder, "BLENDER_SYSTEM_DATAFILES", ver)) break;
return NULL;
case BLENDER_USER_AUTOSAVE:
if (get_path_user(path, "autosave", subfolder, "BLENDER_USER_DATAFILES", ver)) break;
return NULL;
case BLENDER_USER_CONFIG:
if (get_path_user(path, "config", subfolder, "BLENDER_USER_CONFIG", ver)) break;
return NULL;
case BLENDER_USER_SCRIPTS:
if (get_path_user(path, "scripts", subfolder, "BLENDER_USER_SCRIPTS", ver)) break;
return NULL;
case BLENDER_SYSTEM_SCRIPTS:
if (get_path_local(path, "scripts", subfolder, ver)) break;
if (get_path_system(path, "scripts", subfolder, "BLENDER_SYSTEM_SCRIPTS", ver)) break;
return NULL;
case BLENDER_SYSTEM_PYTHON:
if (get_path_local(path, "python", subfolder, ver)) break;
if (get_path_system(path, "python", subfolder, "BLENDER_SYSTEM_PYTHON", ver)) break;
return NULL;
}
return path;
}
char *BLI_get_user_folder_notest(int folder_id, const char *subfolder)
{
const int ver= BLENDER_VERSION;
static char path[FILE_MAX] = "";
switch (folder_id) {
case BLENDER_USER_DATAFILES:
get_path_user(path, "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver);
break;
case BLENDER_USER_CONFIG:
get_path_user(path, "config", subfolder, "BLENDER_USER_CONFIG", ver);
break;
case BLENDER_USER_AUTOSAVE:
get_path_user(path, "autosave", subfolder, "BLENDER_USER_AUTOSAVE", ver);
break;
case BLENDER_USER_SCRIPTS:
get_path_user(path, "scripts", subfolder, "BLENDER_USER_SCRIPTS", ver);
break;
}
if ('\0' == path[0]) {
return NULL;
}
return path;
}
char *BLI_get_folder_create(int folder_id, const 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;
path = BLI_get_folder(folder_id, subfolder);
if (!path) {
path = BLI_get_user_folder_notest(folder_id, subfolder);
if (path) BLI_dir_create_recursive(path);
}
return path;
}
char *BLI_get_folder_version(const int id, const int ver, const int do_check)
{
static char path[FILE_MAX] = "";
int ok;
switch(id) {
case BLENDER_RESOURCE_PATH_USER:
ok= get_path_user(path, NULL, NULL, NULL, ver);
break;
case BLENDER_RESOURCE_PATH_LOCAL:
ok= get_path_local(path, NULL, NULL, ver);
break;
case BLENDER_RESOURCE_PATH_SYSTEM:
ok= get_path_system(path, NULL, NULL, NULL, ver);
break;
default:
path[0]= '\0'; /* incase do_check is false */
ok= FALSE;
BLI_assert(!"incorrect ID");
}
if((ok == FALSE) && do_check) {
return NULL;
}
return path;
}
/* End new stuff */
/* ************************************************************* */
/* ************************************************************* */
#ifdef PATH_DEBUG
#undef PATH_DEBUG
#endif
void BLI_setenv(const char *env, const char*val)
{
2011-09-21 08:40:30 +00:00
/* free windows */
#if (defined(WIN32) || defined(WIN64)) && defined(FREE_WINDOWS)
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);
MEM_freeN(envstr);
/* non-free windows */
#elif (defined(WIN32) || defined(WIN64)) /* not free windows */
_putenv_s(env, val);
#else
/* linux/osx/bsd */
setenv(env, val, 1);
#endif
}
/**
Only set an env var if already not there.
Like Unix setenv(env, val, 0);
*/
void BLI_setenv_if_new(const char *env, const char* val)
{
if(getenv(env) == NULL)
BLI_setenv(env, val);
}
void BLI_clean(char *path)
{
if(path==NULL) return;
#ifdef WIN32
if(path && BLI_strnlen(path, 3) > 2) {
BLI_char_switch(path+2, '/', '\\');
}
#else
BLI_char_switch(path, '\\', '/');
#endif
}
void BLI_char_switch(char *string, char from, char to)
2002-10-12 11:37:38 +00:00
{
if(string==NULL) return;
2002-10-12 11:37:38 +00:00
while (*string != 0) {
if (*string == from) *string = to;
string++;
}
}
void BLI_make_exist(char *dir)
{
2002-10-12 11:37:38 +00:00
int a;
BLI_char_switch(dir, ALTSEP, SEP);
2002-10-12 11:37:38 +00:00
a = strlen(dir);
while(BLI_is_dir(dir) == 0){
2002-10-12 11:37:38 +00:00
a --;
while(dir[a] != SEP){
2002-10-12 11:37:38 +00:00
a--;
if (a <= 0) break;
}
if (a >= 0) {
dir[a+1] = '\0';
}
2002-10-12 11:37:38 +00:00
else {
#ifdef WIN32
get_default_root(dir);
2002-10-12 11:37:38 +00:00
#else
strcpy(dir,"/");
#endif
2002-10-12 11:37:38 +00:00
break;
}
}
}
void BLI_make_existing_file(const char *name)
Giant commit! A full detailed description of this will be done later... is several days of work. Here's a summary: Render: - Full cleanup of render code, removing *all* globals and bad level calls all over blender. Render module is now not called abusive anymore - API-fied calls to rendering - Full recode of internal render pipeline. Is now rendering tiles by default, prepared for much smarter 'bucket' render later. - Each thread now can render a full part - Renders were tested with 4 threads, goes fine, apart from some lookup tables in softshadow and AO still - Rendering is prepared to do multiple layers and passes - No single 32 bits trick in render code anymore, all 100% floats now. Writing images/movies - moved writing images to blender kernel (bye bye 'schrijfplaatje'!) - made a new Movie handle system, also in kernel. This will enable much easier use of movies in Blender PreviewRender: - Using new render API, previewrender (in buttons) now uses regular render code to generate images. - new datafile 'preview.blend.c' has the preview scenes in it - previews get rendered in exact displayed size (1 pixel = 1 pixel) 3D Preview render - new; press Pkey in 3d window, for a panel that continuously renders (pkey is for games, i know... but we dont do that in orange now!) - this render works nearly identical to buttons-preview render, so it stops rendering on any event (mouse, keyboard, etc) - on moving/scaling the panel, the render code doesn't recreate all geometry - same for shifting/panning view - all other operations (now) regenerate the full render database still. - this is WIP... but big fun, especially for simple scenes! Compositor - Using same node system as now in use for shaders, you can composit images - works pretty straightforward... needs much more options/tools and integration with rendering still - is not threaded yet, nor is so smart to only recalculate changes... will be done soon! - the "Render Result" node will get all layers/passes as output sockets - The "Output" node renders to a builtin image, which you can view in the Image window. (yes, output nodes to render-result, and to files, is on the list!) The Bad News - "Unified Render" is removed. It might come back in some stage, but this system should be built from scratch. I can't really understand this code... I expect it is not much needed, especially with advanced layer/passes control - Panorama render, Field render, Motion blur, is not coded yet... (I had to recode every single feature in render, so...!) - Lens Flare is also not back... needs total revision, might become composit effect though (using zbuffer for visibility) - Part render is gone! (well, thats obvious, its default now). - The render window is only restored with limited functionality... I am going to check first the option to render to a Image window, so Blender can become a true single-window application. :) For example, the 'Spare render buffer' (jkey) doesnt work. - Render with border, now default creates a smaller image - No zbuffers are written yet... on the todo! - Scons files and MSVC will need work to get compiling again OK... thats what I can quickly recall. Now go compiling!
2006-01-23 22:05:47 +00:00
{
char di[FILE_MAXDIR+FILE_MAXFILE], fi[FILE_MAXFILE];
BLI_strncpy(di, name, sizeof(di));
Giant commit! A full detailed description of this will be done later... is several days of work. Here's a summary: Render: - Full cleanup of render code, removing *all* globals and bad level calls all over blender. Render module is now not called abusive anymore - API-fied calls to rendering - Full recode of internal render pipeline. Is now rendering tiles by default, prepared for much smarter 'bucket' render later. - Each thread now can render a full part - Renders were tested with 4 threads, goes fine, apart from some lookup tables in softshadow and AO still - Rendering is prepared to do multiple layers and passes - No single 32 bits trick in render code anymore, all 100% floats now. Writing images/movies - moved writing images to blender kernel (bye bye 'schrijfplaatje'!) - made a new Movie handle system, also in kernel. This will enable much easier use of movies in Blender PreviewRender: - Using new render API, previewrender (in buttons) now uses regular render code to generate images. - new datafile 'preview.blend.c' has the preview scenes in it - previews get rendered in exact displayed size (1 pixel = 1 pixel) 3D Preview render - new; press Pkey in 3d window, for a panel that continuously renders (pkey is for games, i know... but we dont do that in orange now!) - this render works nearly identical to buttons-preview render, so it stops rendering on any event (mouse, keyboard, etc) - on moving/scaling the panel, the render code doesn't recreate all geometry - same for shifting/panning view - all other operations (now) regenerate the full render database still. - this is WIP... but big fun, especially for simple scenes! Compositor - Using same node system as now in use for shaders, you can composit images - works pretty straightforward... needs much more options/tools and integration with rendering still - is not threaded yet, nor is so smart to only recalculate changes... will be done soon! - the "Render Result" node will get all layers/passes as output sockets - The "Output" node renders to a builtin image, which you can view in the Image window. (yes, output nodes to render-result, and to files, is on the list!) The Bad News - "Unified Render" is removed. It might come back in some stage, but this system should be built from scratch. I can't really understand this code... I expect it is not much needed, especially with advanced layer/passes control - Panorama render, Field render, Motion blur, is not coded yet... (I had to recode every single feature in render, so...!) - Lens Flare is also not back... needs total revision, might become composit effect though (using zbuffer for visibility) - Part render is gone! (well, thats obvious, its default now). - The render window is only restored with limited functionality... I am going to check first the option to render to a Image window, so Blender can become a true single-window application. :) For example, the 'Spare render buffer' (jkey) doesnt work. - Render with border, now default creates a smaller image - No zbuffers are written yet... on the todo! - Scons files and MSVC will need work to get compiling again OK... thats what I can quickly recall. Now go compiling!
2006-01-23 22:05:47 +00:00
BLI_splitdirstring(di, fi);
/* test exist */
if (BLI_exists(di) == 0) {
BLI_dir_create_recursive(di);
Giant commit! A full detailed description of this will be done later... is several days of work. Here's a summary: Render: - Full cleanup of render code, removing *all* globals and bad level calls all over blender. Render module is now not called abusive anymore - API-fied calls to rendering - Full recode of internal render pipeline. Is now rendering tiles by default, prepared for much smarter 'bucket' render later. - Each thread now can render a full part - Renders were tested with 4 threads, goes fine, apart from some lookup tables in softshadow and AO still - Rendering is prepared to do multiple layers and passes - No single 32 bits trick in render code anymore, all 100% floats now. Writing images/movies - moved writing images to blender kernel (bye bye 'schrijfplaatje'!) - made a new Movie handle system, also in kernel. This will enable much easier use of movies in Blender PreviewRender: - Using new render API, previewrender (in buttons) now uses regular render code to generate images. - new datafile 'preview.blend.c' has the preview scenes in it - previews get rendered in exact displayed size (1 pixel = 1 pixel) 3D Preview render - new; press Pkey in 3d window, for a panel that continuously renders (pkey is for games, i know... but we dont do that in orange now!) - this render works nearly identical to buttons-preview render, so it stops rendering on any event (mouse, keyboard, etc) - on moving/scaling the panel, the render code doesn't recreate all geometry - same for shifting/panning view - all other operations (now) regenerate the full render database still. - this is WIP... but big fun, especially for simple scenes! Compositor - Using same node system as now in use for shaders, you can composit images - works pretty straightforward... needs much more options/tools and integration with rendering still - is not threaded yet, nor is so smart to only recalculate changes... will be done soon! - the "Render Result" node will get all layers/passes as output sockets - The "Output" node renders to a builtin image, which you can view in the Image window. (yes, output nodes to render-result, and to files, is on the list!) The Bad News - "Unified Render" is removed. It might come back in some stage, but this system should be built from scratch. I can't really understand this code... I expect it is not much needed, especially with advanced layer/passes control - Panorama render, Field render, Motion blur, is not coded yet... (I had to recode every single feature in render, so...!) - Lens Flare is also not back... needs total revision, might become composit effect though (using zbuffer for visibility) - Part render is gone! (well, thats obvious, its default now). - The render window is only restored with limited functionality... I am going to check first the option to render to a Image window, so Blender can become a true single-window application. :) For example, the 'Spare render buffer' (jkey) doesnt work. - Render with border, now default creates a smaller image - No zbuffers are written yet... on the todo! - Scons files and MSVC will need work to get compiling again OK... thats what I can quickly recall. Now go compiling!
2006-01-23 22:05:47 +00:00
}
}
void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file)
2002-10-12 11:37:38 +00:00
{
int sl;
2002-10-12 11:37:38 +00:00
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 */
}
2005-06-10 13:12:59 +00:00
/* we first push all slashes into unix mode, just to make sure we don't get
any mess with slashes later on. -jesterKing */
/* constant strings can be passed for those parameters - don't change them - elubie */
/*
BLI_char_switch(relabase, '\\', '/');
BLI_char_switch(dir, '\\', '/');
BLI_char_switch(file, '\\', '/');
*/
2002-10-12 11:37:38 +00:00
/* Resolve relative references */
if (relabase && dir[0] == '/' && dir[1] == '/') {
char *lslash;
/* Get the file name, chop everything past the last slash (ie. the filename) */
strcpy(string, relabase);
lslash= BLI_last_slash(string);
2002-10-12 11:37:38 +00:00
if(lslash) *(lslash+1)= 0;
dir+=2; /* Skip over the relative reference */
}
#ifdef WIN32
else {
if (BLI_strnlen(dir, 3) >= 2 && dir[1] == ':' ) {
BLI_strncpy(string, dir, 3);
dir += 2;
}
else { /* no drive specified */
/* first option: get the drive from the relabase if it has one */
if (relabase && strlen(relabase) >= 2 && relabase[1] == ':' ) {
BLI_strncpy(string, relabase, 3);
string[2] = '\\';
string[3] = '\0';
}
else { /* we're out of luck here, guessing the first valid drive, usually c:\ */
get_default_root(string);
}
/* ignore leading slashes */
while (*dir == '/' || *dir == '\\') dir++;
}
}
#endif
2002-10-12 11:37:38 +00:00
strcat(string, dir);
/* Make sure string ends in one (and only one) slash */
/* first trim all slashes from the end of the string */
sl = strlen(string);
while (sl>0 && ( string[sl-1] == '/' || string[sl-1] == '\\') ) {
string[sl-1] = '\0';
sl--;
}
/* since we've now removed all slashes, put back one slash at the end. */
strcat(string, "/");
2002-10-12 11:37:38 +00:00
while (*file && (*file == '/' || *file == '\\')) /* Trim slashes from the front of file */
file++;
strcat (string, file);
/* Push all slashes to the system preferred direction */
2005-06-10 13:12:59 +00:00
BLI_clean(string);
2002-10-12 11:37:38 +00:00
}
int BLI_testextensie(const char *str, const char *ext)
2002-10-12 11:37:38 +00:00
{
short a, b;
int retval;
2002-10-12 11:37:38 +00:00
a= strlen(str);
b= strlen(ext);
2002-10-12 11:37:38 +00:00
if(a==0 || b==0 || b>=a) {
retval = 0;
} else if (BLI_strcasecmp(ext, str + a - b)) {
retval = 0;
} else {
retval = 1;
2002-10-12 11:37:38 +00:00
}
2002-10-12 11:37:38 +00:00
return (retval);
}
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;
}
2011-04-10 09:37:04 +00:00
int BLI_replace_extension(char *path, size_t maxlen, const char *ext)
{
size_t path_len= strlen(path);
size_t ext_len= strlen(ext);
2011-04-10 09:37:04 +00:00
size_t a;
for(a= path_len - 1; a >= 0; a--) {
if (ELEM3(path[a], '.', '/', '\\')) {
break;
}
}
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;
memcpy(path+a, ext, ext_len + 1);
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(const char *string, char *dir, char *file, const size_t dirlen, const size_t filelen)
{
char *lslash_str = BLI_last_slash(string);
size_t lslash= lslash_str ? (size_t)(lslash_str - string) + 1 : 0;
if (dir) {
if (lslash) {
BLI_strncpy( dir, string, MIN2(dirlen, lslash + 1)); /* +1 to include the slash and the last char */
}
else {
dir[0] = '\0';
}
}
if (file) {
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 *dst, const size_t maxlen, const char *dir, const char *file)
{
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 (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;
}
BLI_strncpy(dst + dirlen, file, maxlen - dirlen);
}
/* 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
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.
*/
int BKE_rebase_path(char *abs, size_t abs_len, char *rel, size_t rel_len, const char *base_dir, const char *src_dir, const char *dest_dir)
{
char path[FILE_MAX];
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;
if (abs)
abs[0]= 0;
if (rel)
rel[0]= 0;
BLI_split_dir_part(base_dir, blend_dir, sizeof(blend_dir));
if (src_dir[0]=='\0')
return 0;
BLI_strncpy(path, src_dir, sizeof(path));
/* expand "//" in filename and get absolute path */
BLI_path_abs(path, base_dir);
/* get the directory part */
BLI_split_dirfile(path, dir, base, sizeof(dir), sizeof(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 (BLI_path_cmp(dir, blend_dir) == 0) {
BLI_join_dirfile(dest_path, sizeof(dest_path), dest_dir, base);
}
/* "below" */
else {
/* rel = image_path_dir - blend_dir */
BLI_strncpy(rel_dir, dir + len, sizeof(rel_dir));
BLI_join_dirfile(dest_path, sizeof(dest_path), dest_dir, rel_dir);
BLI_join_dirfile(dest_path, sizeof(dest_path), dest_path, base);
}
}
/* image is out of current directory */
else {
BLI_join_dirfile(dest_path, sizeof(dest_path), dest_dir, base);
}
if (abs)
BLI_strncpy(abs, dest_path, abs_len);
if (rel) {
strncat(rel, rel_dir, rel_len);
strncat(rel, base, rel_len);
}
/* return 2 if src=dest */
if (BLI_path_cmp(path, dest_path) == 0) {
// if (G.f & G_DEBUG) printf("%s and %s are the same file\n", path, dest_path);
return 2;
}
return 1;
}
char *BLI_first_slash(char *string)
{
char *ffslash, *fbslash;
ffslash= strchr(string, '/');
fbslash= strchr(string, '\\');
if (!ffslash) return fbslash;
else if (!fbslash) return ffslash;
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);
if (len==0 || string[len-1] != SEP) {
string[len] = SEP;
string[len+1] = '\0';
return len+1;
}
return len;
}
/* removes a slash if there is one */
void BLI_del_slash(char *string)
{
int len = strlen(string);
while (len) {
if (string[len-1] == SEP) {
string[len-1] = '\0';
len--;
} else {
break;
}
}
}
2002-10-12 11:37:38 +00:00
static int add_win32_extension(char *name)
{
int retval = 0;
int type;
type = BLI_exists(name);
2002-10-12 11:37:38 +00:00
if ((type == 0) || S_ISDIR(type)) {
#ifdef _WIN32
char filename[FILE_MAXDIR+FILE_MAXFILE];
char ext[FILE_MAXDIR+FILE_MAXFILE];
const char *extensions = getenv("PATHEXT");
2002-10-12 11:37:38 +00:00
if (extensions) {
char *temp;
do {
strcpy(filename, name);
temp = strstr(extensions, ";");
if (temp) {
strncpy(ext, extensions, temp - extensions);
ext[temp - extensions] = 0;
extensions = temp + 1;
strcat(filename, ext);
} else {
strcat(filename, extensions);
}
type = BLI_exists(filename);
2002-10-12 11:37:38 +00:00
if (type && (! S_ISDIR(type))) {
retval = 1;
strcpy(name, filename);
break;
}
} while (temp);
}
#endif
} else {
retval = 1;
}
return (retval);
}
/*
* 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)
2002-10-12 11:37:38 +00:00
{
char filename[FILE_MAXDIR+FILE_MAXFILE];
const char *path = NULL, *temp;
2002-10-12 11:37:38 +00:00
#ifdef _WIN32
const char *separator = ";";
2002-10-12 11:37:38 +00:00
#else
const char *separator = ":";
2002-10-12 11:37:38 +00:00
#endif
#ifdef WITH_BINRELOC
/* 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);
free((void *)path);
return;
}
#endif
#ifdef _WIN32
if(GetModuleFileName(0, fullname, maxlen)) {
if(!BLI_exists(fullname)) {
printf("path can't be found: \"%.*s\"\n", maxlen, fullname);
MessageBox(NULL, "path contains invalid characters or is too long (see console)", "Error", MB_OK);
}
return;
}
#endif
/* unix and non linux */
if (name && name[0]) {
BLI_strncpy(fullname, name, maxlen);
2002-10-12 11:37:38 +00:00
if (name[0] == '.') {
char wdir[FILE_MAX]= "";
BLI_current_working_dir(wdir, sizeof(wdir)); /* backup cwd to restore after */
// not needed but avoids annoying /./ in name
if(name[1]==SEP)
BLI_join_dirfile(fullname, maxlen, wdir, name+2);
else
BLI_join_dirfile(fullname, maxlen, wdir, name);
add_win32_extension(fullname); /* XXX, doesnt respect length */
}
else if (BLI_last_slash(name)) {
2002-10-12 11:37:38 +00:00
// full path
BLI_strncpy(fullname, name, maxlen);
2002-10-12 11:37:38 +00:00
add_win32_extension(fullname);
} else {
// search for binary in $PATH
path = getenv("PATH");
if (path) {
do {
temp = strstr(path, separator);
2002-10-12 11:37:38 +00:00
if (temp) {
strncpy(filename, path, temp - path);
filename[temp - path] = 0;
path = temp + 1;
} else {
strncpy(filename, path, sizeof(filename));
}
BLI_join_dirfile(fullname, maxlen, fullname, name);
2002-10-12 11:37:38 +00:00
if (add_win32_extension(filename)) {
BLI_strncpy(fullname, filename, maxlen);
2002-10-12 11:37:38 +00:00
break;
}
} while (temp);
}
}
2010-10-18 06:52:10 +00:00
#if defined(DEBUG)
2002-10-12 11:37:38 +00:00
if (strcmp(name, fullname)) {
printf("guessing '%s' == '%s'\n", name, fullname);
}
#endif
}
}
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 (userdir && BLI_is_dir(userdir)) {
BLI_strncpy(fullname, userdir, maxlen);
}
#ifdef WIN32
if (fullname[0] == '\0') {
const char *tmp = getenv("TEMP"); /* Windows */
if (tmp && BLI_is_dir(tmp)) {
2011-02-13 06:19:15 +00:00
BLI_strncpy(fullname, tmp, maxlen);
}
}
#else
/* Other OS's - Try TMP and TMPDIR */
if (fullname[0] == '\0') {
const char *tmp = getenv("TMP");
if (tmp && BLI_is_dir(tmp)) {
BLI_strncpy(fullname, tmp, maxlen);
}
}
if (fullname[0] == '\0') {
const char *tmp = getenv("TMPDIR");
if (tmp && BLI_is_dir(tmp)) {
BLI_strncpy(fullname, tmp, maxlen);
}
}
#endif
if (fullname[0] == '\0') {
BLI_strncpy(fullname, "/tmp/", maxlen);
} else {
/* add a trailing slash if needed */
BLI_add_slash(fullname);
#ifdef WIN32
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)
{
size_t inbytesleft=strlen(original);
size_t outbytesleft=512;
size_t rv=0;
iconv_t cd;
if (NULL == code) {
code = locale_charset();
}
cd=iconv_open("UTF-8", code);
if (cd == (iconv_t)(-1)) {
printf("iconv_open Error");
*utf_8='\0';
return ;
}
rv=iconv(cd, &original, &inbytesleft, &utf_8, &outbytesleft);
if (rv == (size_t) -1) {
printf("iconv Error\n");
return ;
}
*utf_8 = '\0';
iconv_close(cd);
}
#endif // WITH_ICONV
Giant commit! A full detailed description of this will be done later... is several days of work. Here's a summary: Render: - Full cleanup of render code, removing *all* globals and bad level calls all over blender. Render module is now not called abusive anymore - API-fied calls to rendering - Full recode of internal render pipeline. Is now rendering tiles by default, prepared for much smarter 'bucket' render later. - Each thread now can render a full part - Renders were tested with 4 threads, goes fine, apart from some lookup tables in softshadow and AO still - Rendering is prepared to do multiple layers and passes - No single 32 bits trick in render code anymore, all 100% floats now. Writing images/movies - moved writing images to blender kernel (bye bye 'schrijfplaatje'!) - made a new Movie handle system, also in kernel. This will enable much easier use of movies in Blender PreviewRender: - Using new render API, previewrender (in buttons) now uses regular render code to generate images. - new datafile 'preview.blend.c' has the preview scenes in it - previews get rendered in exact displayed size (1 pixel = 1 pixel) 3D Preview render - new; press Pkey in 3d window, for a panel that continuously renders (pkey is for games, i know... but we dont do that in orange now!) - this render works nearly identical to buttons-preview render, so it stops rendering on any event (mouse, keyboard, etc) - on moving/scaling the panel, the render code doesn't recreate all geometry - same for shifting/panning view - all other operations (now) regenerate the full render database still. - this is WIP... but big fun, especially for simple scenes! Compositor - Using same node system as now in use for shaders, you can composit images - works pretty straightforward... needs much more options/tools and integration with rendering still - is not threaded yet, nor is so smart to only recalculate changes... will be done soon! - the "Render Result" node will get all layers/passes as output sockets - The "Output" node renders to a builtin image, which you can view in the Image window. (yes, output nodes to render-result, and to files, is on the list!) The Bad News - "Unified Render" is removed. It might come back in some stage, but this system should be built from scratch. I can't really understand this code... I expect it is not much needed, especially with advanced layer/passes control - Panorama render, Field render, Motion blur, is not coded yet... (I had to recode every single feature in render, so...!) - Lens Flare is also not back... needs total revision, might become composit effect though (using zbuffer for visibility) - Part render is gone! (well, thats obvious, its default now). - The render window is only restored with limited functionality... I am going to check first the option to render to a Image window, so Blender can become a true single-window application. :) For example, the 'Spare render buffer' (jkey) doesnt work. - Render with border, now default creates a smaller image - No zbuffers are written yet... on the todo! - Scons files and MSVC will need work to get compiling again OK... thats what I can quickly recall. Now go compiling!
2006-01-23 22:05:47 +00:00