2011-02-23 10:52:22 +00:00
|
|
|
/*
|
2008-01-07 19:13:47 +00:00
|
|
|
* ***** 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
|
2008-01-07 19:13:47 +00:00
|
|
|
* 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.
|
|
|
|
*
|
2008-01-07 19:13:47 +00:00
|
|
|
* ***** END GPL LICENSE BLOCK *****
|
2009-12-13 15:48:57 +00:00
|
|
|
*
|
|
|
|
* 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>
|
2010-11-07 08:49:07 +00:00
|
|
|
#include <assert.h>
|
2002-10-12 11:37:38 +00:00
|
|
|
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
2011-10-21 17:37:38 +00:00
|
|
|
#include "DNA_listBase.h"
|
2008-02-13 13:55:22 +00:00
|
|
|
|
2013-03-04 18:36:37 +00:00
|
|
|
#include "BLI_utildefines.h"
|
2009-12-13 17:46:30 +00:00
|
|
|
#include "BLI_fileops.h"
|
|
|
|
#include "BLI_path_util.h"
|
|
|
|
#include "BLI_string.h"
|
2011-11-21 11:53:29 +00:00
|
|
|
#include "BLI_string_utf8.h"
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2012-12-15 15:59:25 +00:00
|
|
|
#include "../blenkernel/BKE_blender.h" /* BLENDER_VERSION, bad level include (no function call) */
|
2007-02-22 10:20:27 +00:00
|
|
|
|
2010-07-04 21:14:59 +00:00
|
|
|
#include "GHOST_Path-api.h"
|
2009-09-04 10:40:41 +00:00
|
|
|
|
2012-01-12 16:31:27 +00:00
|
|
|
#if defined WIN32 && !defined _LIBC || defined __sun
|
2011-11-02 22:00:22 +00:00
|
|
|
# include "BLI_fnmatch.h" /* use fnmatch included in blenlib */
|
2010-09-24 06:20:43 +00:00
|
|
|
#else
|
2011-02-27 08:31:10 +00:00
|
|
|
# ifndef _GNU_SOURCE
|
|
|
|
# define _GNU_SOURCE
|
|
|
|
# endif
|
|
|
|
# include <fnmatch.h>
|
2010-09-24 06:20:43 +00:00
|
|
|
#endif
|
2002-10-12 11:37:38 +00:00
|
|
|
|
|
|
|
#ifdef WIN32
|
2013-03-29 00:50:52 +00:00
|
|
|
# include "utf_winfunc.h"
|
|
|
|
# include "utfconv.h"
|
2011-11-02 22:00:22 +00:00
|
|
|
# include <io.h>
|
|
|
|
# ifdef _WIN32_IE
|
|
|
|
# undef _WIN32_IE
|
|
|
|
# endif
|
|
|
|
# define _WIN32_IE 0x0501
|
|
|
|
# include <windows.h>
|
|
|
|
# include <shlobj.h>
|
|
|
|
# include "BLI_winstuff.h"
|
2010-03-20 18:52:03 +00:00
|
|
|
#else /* non windows */
|
2011-11-02 22:00:22 +00:00
|
|
|
# ifdef WITH_BINRELOC
|
|
|
|
# include "binreloc.h"
|
|
|
|
# endif
|
2010-03-20 18:52:03 +00:00
|
|
|
#endif /* WIN32 */
|
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
/* local */
|
2010-11-07 08:49:07 +00:00
|
|
|
#define UNIQUE_NAME_MAX 128
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
static char bprogname[FILE_MAX]; /* full path to program executable */
|
|
|
|
static char bprogdir[FILE_MAX]; /* full path to directory in which executable is located */
|
2012-05-12 15:13:06 +00:00
|
|
|
static char btempdir[FILE_MAX]; /* temporary directory */
|
2010-12-04 13:00:28 +00:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
/* implementation */
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
|
|
|
* Looks for a sequence of decimal digits in string, preceding any filename extension,
|
|
|
|
* returning the integer value if found, or 0 if not.
|
|
|
|
*
|
|
|
|
* \param string String to scan.
|
|
|
|
* \param head Optional area to return copy of part of string prior to digits, or before dot if no digits.
|
|
|
|
* \param tail Optional area to return copy of part of string following digits, or from dot if no digits.
|
|
|
|
* \param numlen Optional to return number of digits found.
|
|
|
|
*/
|
2010-04-23 23:01:50 +00:00
|
|
|
int BLI_stringdec(const char *string, char *head, char *tail, unsigned short *numlen)
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
2013-03-04 19:27:51 +00:00
|
|
|
unsigned short nums = 0, nume = 0;
|
|
|
|
short i;
|
|
|
|
bool found_digit = false;
|
|
|
|
const char * const lslash = BLI_last_slash(string);
|
|
|
|
const unsigned short string_len = strlen(string);
|
|
|
|
const unsigned short lslash_len = lslash != NULL ? (int)(lslash - string) : 0;
|
|
|
|
unsigned short name_end = string_len;
|
|
|
|
|
|
|
|
while (name_end > lslash_len && string[--name_end] != '.') {} /* name ends at dot if present */
|
|
|
|
if (name_end == lslash_len && string[name_end] != '.') name_end = string_len;
|
|
|
|
|
|
|
|
for (i = name_end - 1; i >= lslash_len; i--) {
|
2002-10-12 11:37:38 +00:00
|
|
|
if (isdigit(string[i])) {
|
2013-03-04 19:27:51 +00:00
|
|
|
if (found_digit) {
|
2002-10-12 11:37:38 +00:00
|
|
|
nums = i;
|
|
|
|
}
|
2012-03-24 06:18:31 +00:00
|
|
|
else {
|
2002-10-12 11:37:38 +00:00
|
|
|
nume = i;
|
|
|
|
nums = i;
|
2013-03-04 19:27:51 +00:00
|
|
|
found_digit = true;
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
}
|
2007-10-04 10:50:15 +00:00
|
|
|
else {
|
2013-03-04 19:27:51 +00:00
|
|
|
if (found_digit) break;
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
}
|
2013-03-24 01:51:54 +00:00
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
if (found_digit) {
|
2012-05-12 15:13:06 +00:00
|
|
|
if (tail) strcpy(tail, &string[nume + 1]);
|
2010-07-04 17:14:06 +00:00
|
|
|
if (head) {
|
2012-04-29 15:47:02 +00:00
|
|
|
strcpy(head, string);
|
2012-05-12 15:13:06 +00:00
|
|
|
head[nums] = 0;
|
2010-07-04 17:14:06 +00:00
|
|
|
}
|
2012-05-12 15:13:06 +00:00
|
|
|
if (numlen) *numlen = nume - nums + 1;
|
2002-10-12 11:37:38 +00:00
|
|
|
return ((int)atoi(&(string[nums])));
|
|
|
|
}
|
2013-03-24 01:51:54 +00:00
|
|
|
else {
|
|
|
|
if (tail) strcpy(tail, string + name_end);
|
|
|
|
if (head) {
|
2013-03-26 15:52:43 +00:00
|
|
|
/* name_end points to last character of head,
|
|
|
|
* make it +1 so null-terminator is nicely placed
|
|
|
|
*/
|
|
|
|
BLI_strncpy(head, string, name_end + 1);
|
2013-03-24 01:51:54 +00:00
|
|
|
}
|
|
|
|
if (numlen) *numlen = 0;
|
|
|
|
return 0;
|
2010-07-04 17:14:06 +00:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
|
|
|
* Returns in area pointed to by string a string of the form "<head><pic><tail>", where pic
|
|
|
|
* is formatted as numlen digits with leading zeroes.
|
|
|
|
*/
|
2010-04-23 23:01:50 +00:00
|
|
|
void BLI_stringenc(char *string, const char *head, const char *tail, unsigned short numlen, int pic)
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
2013-03-10 11:02:21 +00:00
|
|
|
sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail);
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
|
|
|
* Looks for a numeric suffix preceded by delim character on the end of
|
|
|
|
* name, puts preceding part into *left and value of suffix into *nr.
|
|
|
|
* Returns the length of *left.
|
|
|
|
*
|
|
|
|
* Foo.001 -> "Foo", 1
|
2013-03-10 05:11:18 +00:00
|
|
|
* Returning the length of "Foo"
|
2013-03-04 19:27:51 +00:00
|
|
|
*
|
|
|
|
* \param left Where to return copy of part preceding delim
|
|
|
|
* \param nr Where to return value of numeric suffix
|
|
|
|
* \param name String to split
|
|
|
|
* \param delim Delimiter character
|
2013-03-10 05:11:18 +00:00
|
|
|
* \return Length of \a left
|
2013-03-04 19:27:51 +00:00
|
|
|
*/
|
2010-11-07 08:49:07 +00:00
|
|
|
int BLI_split_name_num(char *left, int *nr, const char *name, const char delim)
|
2010-11-01 07:19:41 +00:00
|
|
|
{
|
2013-03-10 05:11:18 +00:00
|
|
|
const int name_len = strlen(name);
|
2011-06-05 04:52:32 +00:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
*nr = 0;
|
2013-03-10 05:11:18 +00:00
|
|
|
memcpy(left, name, (name_len + 1) * sizeof(char));
|
|
|
|
|
|
|
|
/* name doesn't end with a delimiter "foo." */
|
|
|
|
if ((name_len > 1 && name[name_len - 1] == delim) == 0) {
|
|
|
|
int a = name_len;
|
|
|
|
while (a--) {
|
|
|
|
if (name[a] == delim) {
|
|
|
|
left[a] = '\0'; /* truncate left part here */
|
|
|
|
*nr = atol(name + a + 1);
|
|
|
|
/* casting down to an int, can overflow for large numbers */
|
|
|
|
if (*nr < 0)
|
|
|
|
*nr = 0;
|
|
|
|
return a;
|
|
|
|
}
|
|
|
|
else if (isdigit(name[a]) == 0) {
|
|
|
|
/* non-numeric suffix - give up */
|
|
|
|
break;
|
|
|
|
}
|
2010-11-01 07:19:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-10 05:11:18 +00:00
|
|
|
return name_len;
|
2010-11-01 07:19:41 +00:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
|
|
|
* Looks for a string of digits within name (using BLI_stringdec) and adjusts it by add.
|
|
|
|
*/
|
2007-10-04 10:50:15 +00:00
|
|
|
void BLI_newname(char *name, int add)
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
2010-11-07 08:49:07 +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);
|
|
|
|
|
2003-04-26 16:02:26 +00:00
|
|
|
/* 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;
|
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
if (digits == 4 && pic < 0) pic = 0;
|
2002-10-12 11:37:38 +00:00
|
|
|
BLI_stringenc(name, head, tail, digits, pic);
|
|
|
|
}
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
|
|
|
* Ensures name is unique (according to criteria specified by caller in unique_check callback),
|
|
|
|
* incrementing its numeric suffix as necessary. Returns true if name had to be adjusted.
|
|
|
|
*
|
2013-03-05 03:17:46 +00:00
|
|
|
* \param unique_check Return true if name is not unique
|
2013-03-04 19:27:51 +00:00
|
|
|
* \param arg Additional arg to unique_check--meaning is up to caller
|
|
|
|
* \param defname To initialize name if latter is empty
|
|
|
|
* \param delim Delimits numeric suffix in name
|
|
|
|
* \param name Name to be ensured unique
|
|
|
|
* \param name_len Maximum length of name area
|
|
|
|
* \return true if there if the name was changed
|
|
|
|
*/
|
2013-03-26 07:29:01 +00:00
|
|
|
bool BLI_uniquename_cb(bool (*unique_check)(void *arg, const char *name),
|
2013-03-04 19:27:51 +00:00
|
|
|
void *arg, const char *defname, char delim, char *name, short name_len)
|
2010-11-07 08:49:07 +00:00
|
|
|
{
|
2012-03-24 06:18:31 +00:00
|
|
|
if (name[0] == '\0') {
|
2010-11-07 08:49:07 +00:00
|
|
|
BLI_strncpy(name, defname, name_len);
|
|
|
|
}
|
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (unique_check(arg, name)) {
|
2012-05-12 15:13:06 +00:00
|
|
|
char numstr[16];
|
|
|
|
char tempname[UNIQUE_NAME_MAX];
|
|
|
|
char left[UNIQUE_NAME_MAX];
|
|
|
|
int number;
|
|
|
|
int len = BLI_split_name_num(left, &number, name, delim);
|
2010-11-07 09:33:10 +00:00
|
|
|
do {
|
2013-03-04 19:27:51 +00:00
|
|
|
const int numlen = BLI_snprintf(numstr, sizeof(numstr), "%c%03d", delim, ++number);
|
2011-11-21 11:53:29 +00:00
|
|
|
|
|
|
|
/* highly unlikely the string only has enough room for the number
|
|
|
|
* but support anyway */
|
|
|
|
if ((len == 0) || (numlen >= name_len)) {
|
|
|
|
/* number is know not to be utf-8 */
|
|
|
|
BLI_strncpy(tempname, numstr, name_len);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
char *tempname_buf;
|
2012-05-12 15:13:06 +00:00
|
|
|
tempname[0] = '\0';
|
2012-02-23 02:23:42 +00:00
|
|
|
tempname_buf = BLI_strncat_utf8(tempname, left, name_len - numlen);
|
2011-11-21 11:53:29 +00:00
|
|
|
memcpy(tempname_buf, numstr, numlen + 1);
|
2010-11-07 08:49:07 +00:00
|
|
|
}
|
2012-03-24 06:18:31 +00:00
|
|
|
} while (unique_check(arg, tempname));
|
2010-11-07 08:49:07 +00:00
|
|
|
|
|
|
|
BLI_strncpy(name, tempname, name_len);
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
return true;
|
2010-11-07 08:49:07 +00:00
|
|
|
}
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
return false;
|
2010-11-07 08:49:07 +00:00
|
|
|
}
|
|
|
|
|
2008-01-20 23:53:13 +00:00
|
|
|
/* little helper macro for BLI_uniquename */
|
|
|
|
#ifndef GIVE_STRADDR
|
2012-05-12 15:13:06 +00:00
|
|
|
#define GIVE_STRADDR(data, offset) ( ((char *)data) + offset)
|
2008-01-20 23:53:13 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Generic function to set a unique name. It is only designed to be used in situations
|
2010-11-07 08:49:07 +00:00
|
|
|
* where the name is part of the struct, and also that the name is at most UNIQUE_NAME_MAX chars long.
|
2008-01-20 23:53:13 +00:00
|
|
|
*
|
|
|
|
* For places where this is used, see constraint.c for example...
|
|
|
|
*
|
2012-05-12 15:13:06 +00:00
|
|
|
* 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 delimiter between parts of the name
|
2008-01-20 23:53:13 +00:00
|
|
|
*/
|
2013-03-04 19:27:51 +00:00
|
|
|
static bool uniquename_find_dupe(ListBase *list, void *vlink, const char *name, short name_offs)
|
2008-01-20 23:53:13 +00:00
|
|
|
{
|
|
|
|
Link *link;
|
2010-11-01 07:19:41 +00:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
for (link = list->first; link; link = link->next) {
|
2010-11-01 07:19:41 +00:00
|
|
|
if (link != vlink) {
|
|
|
|
if (!strcmp(GIVE_STRADDR(link, name_offs), name)) {
|
2013-03-04 19:27:51 +00:00
|
|
|
return true;
|
2010-11-01 07:19:41 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
return false;
|
2010-11-01 07:19:41 +00:00
|
|
|
}
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
static bool uniquename_unique_check(void *arg, const char *name)
|
2010-11-07 08:49:07 +00:00
|
|
|
{
|
2012-05-12 15:13:06 +00:00
|
|
|
struct {ListBase *lb; void *vlink; short name_offs; } *data = arg;
|
2010-11-07 08:49:07 +00:00
|
|
|
return uniquename_find_dupe(data->lb, data->vlink, name, data->name_offs);
|
|
|
|
}
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
|
|
|
* Ensures that the specified block has a unique name within the containing list,
|
|
|
|
* incrementing its numeric suffix as necessary.
|
|
|
|
*
|
|
|
|
* \param list List containing the block
|
|
|
|
* \param vlink The block to check the name for
|
|
|
|
* \param defname To initialize block name if latter is empty
|
|
|
|
* \param delim Delimits numeric suffix in name
|
|
|
|
* \param name_offs Offset of name within block structure
|
|
|
|
* \param name_len Maximum length of name area
|
|
|
|
*/
|
|
|
|
void BLI_uniquename(ListBase *list, void *vlink, const char *defname, char delim, short name_offs, short name_len)
|
2010-11-07 08:49:07 +00:00
|
|
|
{
|
2012-05-12 15:13:06 +00:00
|
|
|
struct {ListBase *lb; void *vlink; short name_offs; } data;
|
|
|
|
data.lb = list;
|
|
|
|
data.vlink = vlink;
|
|
|
|
data.name_offs = name_offs;
|
2010-11-07 08:49:07 +00:00
|
|
|
|
|
|
|
assert((name_len > 1) && (name_len <= UNIQUE_NAME_MAX));
|
|
|
|
|
2008-01-20 23:53:13 +00:00
|
|
|
/* See if we are given an empty string */
|
|
|
|
if (ELEM(NULL, vlink, defname))
|
|
|
|
return;
|
|
|
|
|
2010-11-07 08:49:07 +00:00
|
|
|
BLI_uniquename_cb(uniquename_unique_check, &data, defname, delim, GIVE_STRADDR(vlink, name_offs), name_len);
|
2008-01-20 23:53:13 +00:00
|
|
|
}
|
|
|
|
|
2010-11-07 08:49:07 +00:00
|
|
|
|
|
|
|
|
2005-12-14 13:21:32 +00:00
|
|
|
/* ******************** 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...
|
2008-04-28 21:29:15 +00:00
|
|
|
*
|
|
|
|
* If relbase is NULL then its ignored
|
2005-12-14 13:21:32 +00:00
|
|
|
*/
|
|
|
|
|
2013-03-05 04:35:14 +00:00
|
|
|
void BLI_cleanup_path(const char *relabase, char *path)
|
2005-12-14 13:21:32 +00:00
|
|
|
{
|
2011-12-21 20:56:49 +00:00
|
|
|
ptrdiff_t a;
|
2005-12-14 13:21:32 +00:00
|
|
|
char *start, *eind;
|
2008-04-28 21:29:15 +00:00
|
|
|
if (relabase) {
|
2013-03-05 04:35:14 +00:00
|
|
|
BLI_path_abs(path, relabase);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2013-03-05 04:35:14 +00:00
|
|
|
if (path[0] == '/' && path[1] == '/') {
|
|
|
|
if (path[2] == '\0') {
|
2008-06-05 13:02:00 +00:00
|
|
|
return; /* path is "//" - cant clean it */
|
|
|
|
}
|
2013-03-10 04:43:15 +00:00
|
|
|
path = path + 2; /* leave the initial "//" untouched */
|
2008-06-05 13:02:00 +00:00
|
|
|
}
|
2008-04-28 21:29:15 +00:00
|
|
|
}
|
2005-12-14 13:21:32 +00:00
|
|
|
|
2008-06-11 09:04:41 +00:00
|
|
|
/* Note
|
2012-03-11 19:09:01 +00:00
|
|
|
* memmove(start, eind, strlen(eind) + 1);
|
2008-06-11 09:04:41 +00:00
|
|
|
* is the same as
|
|
|
|
* strcpy( start, eind );
|
|
|
|
* except strcpy should not be used because there is overlap,
|
2012-03-03 20:19:11 +00:00
|
|
|
* so use memmove's slightly more obscure syntax - Campbell
|
2008-06-11 09:04:41 +00:00
|
|
|
*/
|
2005-12-14 13:21:32 +00:00
|
|
|
|
|
|
|
#ifdef WIN32
|
2008-10-06 08:02:35 +00:00
|
|
|
|
|
|
|
/* Note, this should really be moved to the file selector,
|
|
|
|
* since this function is used in many areas */
|
2013-03-05 04:35:14 +00:00
|
|
|
if (strcmp(path, ".") == 0) { /* happens for example in FILE_MAIN */
|
|
|
|
get_default_root(path);
|
2011-04-21 15:53:30 +00:00
|
|
|
return;
|
2012-10-21 05:46:41 +00:00
|
|
|
}
|
2005-12-14 13:21:32 +00:00
|
|
|
|
2013-03-05 04:35:14 +00:00
|
|
|
while ( (start = strstr(path, "\\..\\")) ) {
|
2005-12-14 13:21:32 +00:00
|
|
|
eind = start + strlen("\\..\\") - 1;
|
2013-03-05 04:35:14 +00:00
|
|
|
a = start - path - 1;
|
2012-05-12 15:13:06 +00:00
|
|
|
while (a > 0) {
|
2013-03-05 04:35:14 +00:00
|
|
|
if (path[a] == '\\') break;
|
2005-12-14 13:21:32 +00:00
|
|
|
a--;
|
|
|
|
}
|
2012-05-12 15:13:06 +00:00
|
|
|
if (a < 0) {
|
2008-06-14 16:54:46 +00:00
|
|
|
break;
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2013-03-05 04:35:14 +00:00
|
|
|
memmove(path + a, eind, strlen(eind) + 1);
|
2008-06-14 16:54:46 +00:00
|
|
|
}
|
2005-12-14 13:21:32 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 04:35:14 +00:00
|
|
|
while ( (start = strstr(path, "\\.\\")) ) {
|
2005-12-14 13:21:32 +00:00
|
|
|
eind = start + strlen("\\.\\") - 1;
|
2012-03-11 19:09:01 +00:00
|
|
|
memmove(start, eind, strlen(eind) + 1);
|
2005-12-14 13:21:32 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 04:35:14 +00:00
|
|
|
while ( (start = strstr(path, "\\\\")) ) {
|
2005-12-14 13:21:32 +00:00
|
|
|
eind = start + strlen("\\\\") - 1;
|
2012-03-11 19:09:01 +00:00
|
|
|
memmove(start, eind, strlen(eind) + 1);
|
2005-12-14 13:21:32 +00:00
|
|
|
}
|
2007-12-31 12:03:26 +00:00
|
|
|
#else
|
2013-03-05 04:35:14 +00:00
|
|
|
if (path[0] == '.') { /* happens, for example in FILE_MAIN */
|
|
|
|
path[0] = '/';
|
|
|
|
path[1] = 0;
|
2011-04-21 15:53:30 +00:00
|
|
|
return;
|
2010-09-17 15:11:12 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 04:35:14 +00:00
|
|
|
while ( (start = strstr(path, "/../")) ) {
|
|
|
|
a = start - path - 1;
|
2013-03-10 04:43:15 +00:00
|
|
|
if (a > 0) {
|
|
|
|
/* <prefix>/<parent>/../<postfix> => <prefix>/<postfix> */
|
|
|
|
eind = start + (4 - 1) /* strlen("/../") - 1 */; /* strip "/.." and keep last "/" */
|
|
|
|
while (a > 0 && path[a] != '/') { /* find start of <parent> */
|
|
|
|
a--;
|
|
|
|
}
|
|
|
|
memmove(path + a, eind, strlen(eind) + 1);
|
2012-03-11 19:09:01 +00:00
|
|
|
}
|
|
|
|
else {
|
2013-03-10 04:43:15 +00:00
|
|
|
/* support for odd paths: eg /../home/me --> /home/me
|
|
|
|
* this is a valid path in blender but we cant handle this the usual way below
|
|
|
|
* simply strip this prefix then evaluate the path as usual.
|
|
|
|
* pythons os.path.normpath() does this */
|
|
|
|
|
|
|
|
/* Note: previous version of following call used an offset of 3 instead of 4,
|
|
|
|
* which meant that the "/../home/me" example actually became "home/me".
|
|
|
|
* Using offset of 3 gives behaviour consistent with the abovementioned
|
|
|
|
* Python routine. */
|
|
|
|
memmove(path, path + 3, strlen(path + 3) + 1);
|
2008-06-14 16:54:46 +00:00
|
|
|
}
|
2005-12-14 13:21:32 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 04:35:14 +00:00
|
|
|
while ( (start = strstr(path, "/./")) ) {
|
2010-09-17 15:11:12 +00:00
|
|
|
eind = start + (3 - 1) /* strlen("/./") - 1 */;
|
2012-03-11 19:09:01 +00:00
|
|
|
memmove(start, eind, strlen(eind) + 1);
|
2005-12-14 13:21:32 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 04:35:14 +00:00
|
|
|
while ( (start = strstr(path, "//")) ) {
|
2010-09-17 15:11:12 +00:00
|
|
|
eind = start + (2 - 1) /* strlen("//") - 1 */;
|
2012-03-11 19:09:01 +00:00
|
|
|
memmove(start, eind, strlen(eind) + 1);
|
2005-12-14 13:21:32 +00:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2011-03-11 00:30:51 +00:00
|
|
|
void BLI_cleanup_dir(const char *relabase, char *dir)
|
|
|
|
{
|
|
|
|
BLI_cleanup_path(relabase, dir);
|
|
|
|
BLI_add_slash(dir);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-03-05 04:35:14 +00:00
|
|
|
void BLI_cleanup_file(const char *relabase, char *path)
|
2011-03-11 00:30:51 +00:00
|
|
|
{
|
2013-03-05 04:35:14 +00:00
|
|
|
BLI_cleanup_path(relabase, path);
|
|
|
|
BLI_del_slash(path);
|
2011-03-11 00:30:51 +00:00
|
|
|
}
|
2005-12-14 13:21:32 +00:00
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
|
|
|
* Does path begin with the special "//" prefix that Blender uses to indicate
|
|
|
|
* a path relative to the .blend file.
|
|
|
|
*/
|
|
|
|
bool BLI_path_is_rel(const char *path)
|
2012-08-29 10:32:38 +00:00
|
|
|
{
|
|
|
|
return path[0] == '/' && path[1] == '/';
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Replaces *file with a relative version (prefixed by "//") such that BLI_path_abs, given
|
|
|
|
* the same *relfile, will convert it back to its original value.
|
|
|
|
*/
|
2010-03-09 17:36:23 +00:00
|
|
|
void BLI_path_rel(char *file, const char *relfile)
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
2013-03-04 19:27:51 +00:00
|
|
|
const char *lslash;
|
2011-11-26 04:07:38 +00:00
|
|
|
char temp[FILE_MAX];
|
|
|
|
char res[FILE_MAX];
|
2008-04-28 21:29:15 +00:00
|
|
|
|
2005-12-11 22:03:04 +00:00
|
|
|
/* if file is already relative, bail out */
|
2012-08-29 10:32:38 +00:00
|
|
|
if (BLI_path_is_rel(file)) {
|
|
|
|
return;
|
|
|
|
}
|
2005-12-11 22:03:04 +00:00
|
|
|
|
2006-08-20 14:41:13 +00:00
|
|
|
/* also bail out if relative path is not set */
|
2012-08-29 10:32:38 +00:00
|
|
|
if (relfile[0] == '\0') {
|
|
|
|
return;
|
|
|
|
}
|
2006-08-20 14:41:13 +00:00
|
|
|
|
2010-12-05 23:50:55 +00:00
|
|
|
#ifdef WIN32
|
|
|
|
if (BLI_strnlen(relfile, 3) > 2 && relfile[1] != ':') {
|
2012-05-12 15:13:06 +00:00
|
|
|
char *ptemp;
|
2007-02-21 20:00:03 +00:00
|
|
|
/* fix missing volume name in relative base,
|
2012-03-03 20:19:11 +00:00
|
|
|
* can happen with old recent-files.txt files */
|
2007-02-21 20:00:03 +00:00
|
|
|
get_default_root(temp);
|
|
|
|
ptemp = &temp[2];
|
|
|
|
if (relfile[0] != '\\' && relfile[0] != '/') {
|
|
|
|
ptemp++;
|
|
|
|
}
|
2012-05-12 15:13:06 +00:00
|
|
|
BLI_strncpy(ptemp, relfile, FILE_MAX - 3);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2011-11-26 04:07:38 +00:00
|
|
|
BLI_strncpy(temp, relfile, FILE_MAX);
|
2007-02-21 20:00:03 +00:00
|
|
|
}
|
|
|
|
|
2010-12-05 23:50:55 +00:00
|
|
|
if (BLI_strnlen(file, 3) > 2) {
|
2012-05-12 15:13:06 +00:00
|
|
|
if (temp[1] == ':' && file[1] == ':' && temp[0] != file[0])
|
2005-10-24 20:52:51 +00:00
|
|
|
return;
|
|
|
|
}
|
2007-02-21 20:00:03 +00:00
|
|
|
#else
|
|
|
|
BLI_strncpy(temp, relfile, FILE_MAX);
|
2005-05-20 12:18:11 +00:00
|
|
|
#endif
|
2005-10-24 20:52:51 +00:00
|
|
|
|
|
|
|
BLI_char_switch(temp, '\\', '/');
|
|
|
|
BLI_char_switch(file, '\\', '/');
|
2008-04-28 21:29:15 +00:00
|
|
|
|
|
|
|
/* remove /./ which confuse the following slash counting... */
|
2011-03-11 00:30:51 +00:00
|
|
|
BLI_cleanup_path(NULL, file);
|
|
|
|
BLI_cleanup_path(NULL, temp);
|
2008-04-28 21:29:15 +00:00
|
|
|
|
2005-10-24 20:52:51 +00:00
|
|
|
/* the last slash in the file indicates where the path part ends */
|
|
|
|
lslash = BLI_last_slash(temp);
|
|
|
|
|
2012-03-06 18:40:15 +00:00
|
|
|
if (lslash) {
|
2005-10-24 20:52:51 +00:00
|
|
|
/* find the prefix of the filename that is equal for both filenames.
|
2012-03-03 20:19:11 +00:00
|
|
|
* This is replaced by the two slashes at the beginning */
|
2012-05-12 15:13:06 +00:00
|
|
|
char *p = temp;
|
|
|
|
char *q = file;
|
2010-06-03 13:05:45 +00:00
|
|
|
|
2011-04-08 06:47:41 +00:00
|
|
|
#ifdef WIN32
|
|
|
|
while (tolower(*p) == tolower(*q))
|
|
|
|
#else
|
|
|
|
while (*p == *q)
|
|
|
|
#endif
|
|
|
|
{
|
2012-05-09 09:24:15 +00:00
|
|
|
p++;
|
|
|
|
q++;
|
|
|
|
|
2012-03-18 07:38:51 +00:00
|
|
|
/* don't search beyond the end of the string
|
2010-06-03 13:05:45 +00:00
|
|
|
* in the rare case they match */
|
2012-05-12 15:13:06 +00:00
|
|
|
if ((*p == '\0') || (*q == '\0')) {
|
2010-06-03 13:05:45 +00:00
|
|
|
break;
|
|
|
|
}
|
2005-10-24 20:52:51 +00:00
|
|
|
}
|
2010-06-03 13:05:45 +00:00
|
|
|
|
2005-10-24 20:52:51 +00:00
|
|
|
/* we might have passed the slash when the beginning of a dir matches
|
2012-03-03 20:19:11 +00:00
|
|
|
* so we rewind. Only check on the actual filename
|
|
|
|
*/
|
2005-10-24 20:52:51 +00:00
|
|
|
if (*q != '/') {
|
|
|
|
while ( (q >= file) && (*q != '/') ) { --q; --p; }
|
2012-10-21 05:46:41 +00:00
|
|
|
}
|
2005-10-24 20:52:51 +00:00
|
|
|
else if (*p != '/') {
|
|
|
|
while ( (p >= temp) && (*p != '/') ) { --p; --q; }
|
|
|
|
}
|
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
strcpy(res, "//");
|
2005-10-24 20:52:51 +00:00
|
|
|
|
|
|
|
/* p now points to the slash that is at the beginning of the part
|
2012-03-03 20:19:11 +00:00
|
|
|
* 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
|
|
|
|
*/
|
2012-05-12 15:13:06 +00:00
|
|
|
while (p && p < lslash) {
|
2005-10-24 20:52:51 +00:00
|
|
|
if (*p == '/')
|
2012-05-12 15:13:06 +00:00
|
|
|
strcat(res, "../");
|
2012-05-09 09:24:15 +00:00
|
|
|
p++;
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
2005-10-24 20:52:51 +00:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
strcat(res, q + 1); /* don't copy the slash at the beginning */
|
2005-12-14 13:21:32 +00:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
#ifdef WIN32
|
|
|
|
BLI_char_switch(res + 2, '/', '\\');
|
2005-10-24 20:52:51 +00:00
|
|
|
#endif
|
|
|
|
strcpy(file, res);
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
2013-03-05 03:17:46 +00:00
|
|
|
* Cleans path and makes sure it ends with a slash.
|
|
|
|
* \return true if \a path has more than one other path separator in it.
|
2013-03-04 19:27:51 +00:00
|
|
|
*/
|
|
|
|
bool BLI_has_parent(char *path)
|
2009-07-10 19:19:54 +00:00
|
|
|
{
|
|
|
|
int len;
|
|
|
|
int slashes = 0;
|
|
|
|
BLI_clean(path);
|
2009-07-19 17:45:14 +00:00
|
|
|
len = BLI_add_slash(path) - 1;
|
2009-07-10 19:19:54 +00:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
while (len >= 0) {
|
2009-07-10 19:19:54 +00:00
|
|
|
if ((path[len] == '\\') || (path[len] == '/'))
|
|
|
|
slashes++;
|
|
|
|
len--;
|
|
|
|
}
|
|
|
|
return slashes > 1;
|
|
|
|
}
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
2013-03-05 03:17:46 +00:00
|
|
|
* Replaces path with the path of its parent directory, returning true if
|
2013-03-04 19:27:51 +00:00
|
|
|
* it was able to find a parent directory within the pathname.
|
|
|
|
*/
|
|
|
|
bool BLI_parent_dir(char *path)
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
2012-05-12 15:13:06 +00:00
|
|
|
static char parent_dir[] = {'.', '.', SEP, '\0'}; /* "../" or "..\\" */
|
|
|
|
char tmp[FILE_MAX + 4];
|
|
|
|
BLI_strncpy(tmp, path, sizeof(tmp) - 4);
|
2008-09-22 15:37:32 +00:00
|
|
|
BLI_add_slash(tmp);
|
2008-09-22 21:15:48 +00:00
|
|
|
strcat(tmp, parent_dir);
|
2013-03-04 19:27:51 +00:00
|
|
|
BLI_cleanup_dir(NULL, tmp); /* does all the work of normalizing the path for us */
|
2011-04-21 15:53:30 +00:00
|
|
|
|
2008-09-22 21:15:48 +00:00
|
|
|
if (!BLI_testextensie(tmp, parent_dir)) {
|
2012-10-21 05:46:41 +00:00
|
|
|
BLI_strncpy(path, tmp, sizeof(tmp));
|
2013-03-04 19:27:51 +00:00
|
|
|
return true;
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2013-03-04 19:27:51 +00:00
|
|
|
return false;
|
2008-09-22 15:37:32 +00:00
|
|
|
}
|
|
|
|
}
|
2008-05-04 09:41:15 +00:00
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
|
|
|
* Looks for a sequence of "#" characters in the last slash-separated component of *path,
|
|
|
|
* returning the indexes of the first and one past the last character in the sequence in
|
2013-03-05 03:17:46 +00:00
|
|
|
* *char_start and *char_end respectively. Returns true if such a sequence was found.
|
2013-03-04 19:27:51 +00:00
|
|
|
*/
|
|
|
|
static bool stringframe_chars(const char *path, int *char_start, int *char_end)
|
2008-05-04 09:41:15 +00:00
|
|
|
{
|
|
|
|
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] == '/') {
|
2012-03-18 07:38:51 +00:00
|
|
|
ch_end = 0; /* this is a directory name, don't use any hashes we found */
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else if (path[i] == '#') {
|
2008-05-04 09:41:15 +00:00
|
|
|
ch_sta = i;
|
2012-05-12 15:13:06 +00:00
|
|
|
ch_end = ch_sta + 1;
|
2008-05-04 09:41:15 +00:00
|
|
|
while (path[ch_end] == '#') {
|
|
|
|
ch_end++;
|
|
|
|
}
|
2012-05-12 15:13:06 +00:00
|
|
|
i = ch_end - 1; /* keep searching */
|
2008-05-04 09:41:15 +00:00
|
|
|
|
2012-03-18 07:38:51 +00:00
|
|
|
/* don't break, there may be a slash after this that invalidates the previous #'s */
|
2008-05-04 09:41:15 +00:00
|
|
|
}
|
|
|
|
}
|
2010-01-26 21:41:38 +00:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (ch_end) {
|
2012-05-12 15:13:06 +00:00
|
|
|
*char_start = ch_sta;
|
|
|
|
*char_end = ch_end;
|
2013-03-04 19:27:51 +00:00
|
|
|
return true;
|
2010-01-26 21:41:38 +00:00
|
|
|
}
|
|
|
|
else {
|
2012-05-12 15:13:06 +00:00
|
|
|
*char_start = -1;
|
|
|
|
*char_end = -1;
|
2013-03-04 19:27:51 +00:00
|
|
|
return false;
|
2010-01-26 21:41:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
|
|
|
* Ensure *path contains at least one "#" character in its last slash-separated
|
|
|
|
* component, appending one digits long if not.
|
|
|
|
*/
|
2010-01-30 22:33:47 +00:00
|
|
|
static void ensure_digits(char *path, int digits)
|
|
|
|
{
|
2013-03-04 19:27:51 +00:00
|
|
|
char *file = (char *)BLI_last_slash(path);
|
2010-01-30 22:33:47 +00:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
if (file == NULL)
|
|
|
|
file = path;
|
2010-01-30 22:33:47 +00:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (strrchr(file, '#') == NULL) {
|
2012-05-12 15:13:06 +00:00
|
|
|
int len = strlen(file);
|
2010-01-30 22:33:47 +00:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
while (digits--) {
|
2012-05-12 15:13:06 +00:00
|
|
|
file[len++] = '#';
|
2010-01-30 22:33:47 +00:00
|
|
|
}
|
2012-05-12 15:13:06 +00:00
|
|
|
file[len] = '\0';
|
2010-01-30 22:33:47 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Replaces "#" character sequence in last slash-separated component of *path
|
|
|
|
* with frame as decimal integer, with leading zeroes as necessary, to make digits digits.
|
|
|
|
*/
|
|
|
|
bool BLI_path_frame(char *path, int frame, int digits)
|
2010-01-26 21:41:38 +00:00
|
|
|
{
|
|
|
|
int ch_sta, ch_end;
|
2010-01-30 22:33:47 +00:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (digits)
|
2010-01-30 22:33:47 +00:00
|
|
|
ensure_digits(path, digits);
|
|
|
|
|
2010-01-26 21:41:38 +00:00
|
|
|
if (stringframe_chars(path, &ch_sta, &ch_end)) { /* warning, ch_end is the last # +1 */
|
2010-11-01 01:46:26 +00:00
|
|
|
char tmp[FILE_MAX];
|
2012-05-12 15:13:06 +00:00
|
|
|
sprintf(tmp, "%.*s%.*d%s", ch_sta, path, ch_end - ch_sta, frame, path + ch_end);
|
2008-05-04 09:41:15 +00:00
|
|
|
strcpy(path, tmp);
|
2013-03-05 03:17:46 +00:00
|
|
|
return true;
|
2008-05-04 09:41:15 +00:00
|
|
|
}
|
2013-03-05 03:17:46 +00:00
|
|
|
return false;
|
2008-05-04 09:41:15 +00:00
|
|
|
}
|
2006-09-16 11:42:37 +00:00
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Replaces "#" character sequence in last slash-separated component of *path
|
|
|
|
* with sta and end as decimal integers, with leading zeroes as necessary, to make digits
|
|
|
|
* digits each, with a hyphen in-between.
|
|
|
|
*/
|
|
|
|
bool BLI_path_frame_range(char *path, int sta, int end, int digits)
|
2010-01-26 21:41:38 +00:00
|
|
|
{
|
|
|
|
int ch_sta, ch_end;
|
2010-01-30 22:33:47 +00:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (digits)
|
2010-01-30 22:33:47 +00:00
|
|
|
ensure_digits(path, digits);
|
|
|
|
|
2010-01-26 21:41:38 +00:00
|
|
|
if (stringframe_chars(path, &ch_sta, &ch_end)) { /* warning, ch_end is the last # +1 */
|
2011-03-29 14:07:07 +00:00
|
|
|
char tmp[FILE_MAX];
|
2012-01-11 12:33:51 +00:00
|
|
|
BLI_snprintf(tmp, sizeof(tmp),
|
|
|
|
"%.*s%.*d-%.*d%s",
|
2012-05-12 15:13:06 +00:00
|
|
|
ch_sta, path, ch_end - ch_sta, sta, ch_end - ch_sta, end, path + ch_end);
|
2012-01-11 12:33:51 +00:00
|
|
|
BLI_strncpy(path, tmp, FILE_MAX);
|
2013-03-05 03:17:46 +00:00
|
|
|
return true;
|
2010-01-26 21:41:38 +00:00
|
|
|
}
|
2013-03-05 03:17:46 +00:00
|
|
|
return false;
|
2010-01-26 21:41:38 +00:00
|
|
|
}
|
2006-08-20 14:41:13 +00:00
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
|
|
|
* If path begins with "//", strips that and replaces it with basepath directory. Also converts
|
|
|
|
* a drive-letter prefix to something more sensible if this is a non-drive-letter-based system.
|
2013-03-05 03:17:46 +00:00
|
|
|
* Returns true if "//" prefix expansion was done.
|
2013-03-04 19:27:51 +00:00
|
|
|
*/
|
|
|
|
bool BLI_path_abs(char *path, const char *basepath)
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
2013-03-04 19:27:51 +00:00
|
|
|
const bool wasrelative = BLI_path_is_rel(path);
|
2008-03-30 16:18:01 +00:00
|
|
|
char tmp[FILE_MAX];
|
|
|
|
char base[FILE_MAX];
|
2006-08-20 14:41:13 +00:00
|
|
|
#ifdef WIN32
|
2006-09-16 11:42:37 +00:00
|
|
|
char vol[3] = {'\0', '\0', '\0'};
|
|
|
|
|
2006-10-02 16:11:57 +00:00
|
|
|
BLI_strncpy(vol, path, 3);
|
2006-09-16 11:42:37 +00:00
|
|
|
/* we are checking here if we have an absolute path that is not in the current
|
2012-03-03 20:19:11 +00:00
|
|
|
* blend file as a lib main - we are basically checking for the case that a
|
|
|
|
* UNIX root '/' is passed.
|
|
|
|
*/
|
2006-09-16 11:42:37 +00:00
|
|
|
if (!wasrelative && (vol[1] != ':' && (vol[0] == '\0' || vol[0] == '/' || vol[0] == '\\'))) {
|
|
|
|
char *p = path;
|
2006-08-20 14:41:13 +00:00
|
|
|
get_default_root(tmp);
|
|
|
|
// get rid of the slashes at the beginning of the path
|
2006-09-16 11:42:37 +00:00
|
|
|
while (*p == '\\' || *p == '/') {
|
|
|
|
p++;
|
2006-08-20 14:41:13 +00:00
|
|
|
}
|
2006-09-16 11:42:37 +00:00
|
|
|
strcat(tmp, p);
|
2006-08-20 14:41:13 +00:00
|
|
|
}
|
|
|
|
else {
|
2008-03-30 16:18:01 +00:00
|
|
|
BLI_strncpy(tmp, path, FILE_MAX);
|
2006-08-20 14:41:13 +00:00
|
|
|
}
|
|
|
|
#else
|
2010-08-12 03:37:45 +00:00
|
|
|
BLI_strncpy(tmp, path, sizeof(tmp));
|
2008-09-30 04:08:00 +00:00
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
if (isalpha(tmp[0]) && tmp[1] == ':' && (tmp[2] == '\\' || tmp[2] == '/') ) {
|
2008-09-30 04:08:00 +00:00
|
|
|
tmp[1] = tolower(tmp[0]); /* replace ':' with driveletter */
|
|
|
|
tmp[0] = '/';
|
|
|
|
/* '\' the slash will be converted later */
|
|
|
|
}
|
|
|
|
|
2006-08-20 14:41:13 +00:00
|
|
|
#endif
|
|
|
|
|
2010-08-12 03:37:45 +00:00
|
|
|
BLI_strncpy(base, basepath, sizeof(base));
|
2011-03-11 01:06:16 +00:00
|
|
|
|
2012-03-18 07:38:51 +00:00
|
|
|
/* file component is ignored, so don't bother with the trailing slash */
|
2011-03-11 01:06:16 +00:00
|
|
|
BLI_cleanup_path(NULL, base);
|
2005-05-20 12:18:11 +00:00
|
|
|
|
|
|
|
/* push slashes into unix mode - strings entering this part are
|
2012-03-03 20:19:11 +00:00
|
|
|
* 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. */
|
2005-05-20 12:18:11 +00:00
|
|
|
BLI_char_switch(tmp, '\\', '/');
|
2012-10-21 05:46:41 +00:00
|
|
|
BLI_char_switch(base, '\\', '/');
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2008-06-05 13:02:00 +00:00
|
|
|
/* Paths starting with // will get the blend file as their base,
|
2012-03-18 07:38:51 +00:00
|
|
|
* this isn't standard in any os but is used in blender all over the place */
|
2008-06-14 16:54:46 +00:00
|
|
|
if (wasrelative) {
|
2013-03-04 19:27:51 +00:00
|
|
|
const char * const lslash = BLI_last_slash(base);
|
2002-10-12 11:37:38 +00:00
|
|
|
if (lslash) {
|
2013-03-04 19:27:51 +00:00
|
|
|
const int baselen = (int) (lslash - base) + 1; /* length up to and including last "/" */
|
2010-04-25 15:24:18 +00:00
|
|
|
/* use path for temp storage here, we copy back over it right away */
|
2013-03-04 19:27:51 +00:00
|
|
|
BLI_strncpy(path, tmp + 2, FILE_MAX); /* strip "//" */
|
2008-06-05 13:02:00 +00:00
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
memcpy(tmp, base, baselen); /* prefix with base up to last "/" */
|
|
|
|
BLI_strncpy(tmp + baselen, path, sizeof(tmp) - baselen); /* append path after "//" */
|
|
|
|
BLI_strncpy(path, tmp, FILE_MAX); /* return as result */
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2013-03-04 19:27:51 +00:00
|
|
|
/* base doesn't seem to be a directory--ignore it and just strip "//" prefix on path */
|
2012-05-12 15:13:06 +00:00
|
|
|
BLI_strncpy(path, tmp + 2, FILE_MAX);
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2013-03-04 19:27:51 +00:00
|
|
|
/* base ignored */
|
2010-08-12 03:37:45 +00:00
|
|
|
BLI_strncpy(path, tmp, FILE_MAX);
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
2011-03-11 01:06:16 +00:00
|
|
|
|
|
|
|
BLI_cleanup_path(NULL, path);
|
|
|
|
|
2005-05-20 12:18:11 +00:00
|
|
|
#ifdef WIN32
|
|
|
|
/* skip first two chars, which in case of
|
2012-03-03 20:19:11 +00:00
|
|
|
* 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
|
|
|
|
*/
|
2012-05-12 15:13:06 +00:00
|
|
|
BLI_char_switch(path + 2, '/', '\\');
|
2005-05-20 12:18:11 +00:00
|
|
|
#endif
|
2008-04-28 21:29:15 +00:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
return wasrelative;
|
|
|
|
}
|
|
|
|
|
2008-09-15 01:32:53 +00:00
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Expands path relative to the current working directory, if it was relative.
|
|
|
|
* Returns true if such expansion was done.
|
|
|
|
*
|
|
|
|
* \note Should only be done with command line paths.
|
|
|
|
* this is _not_ something blenders internal paths support like the "//" prefix
|
2008-09-15 01:32:53 +00:00
|
|
|
*/
|
2013-03-05 03:17:46 +00:00
|
|
|
bool BLI_path_cwd(char *path)
|
2008-09-15 01:32:53 +00:00
|
|
|
{
|
2013-03-05 03:17:46 +00:00
|
|
|
bool wasrelative = true;
|
|
|
|
const int filelen = strlen(path);
|
2008-09-15 01:32:53 +00:00
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
if (filelen >= 3 && path[1] == ':' && (path[2] == '\\' || path[2] == '/'))
|
2013-03-05 03:17:46 +00:00
|
|
|
wasrelative = false;
|
2008-09-15 01:32:53 +00:00
|
|
|
#else
|
|
|
|
if (filelen >= 2 && path[0] == '/')
|
2013-03-05 03:17:46 +00:00
|
|
|
wasrelative = false;
|
2008-09-15 01:32:53 +00:00
|
|
|
#endif
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
if (wasrelative) {
|
2012-05-12 15:13:06 +00:00
|
|
|
char cwd[FILE_MAX] = "";
|
2012-03-18 07:38:51 +00:00
|
|
|
BLI_current_working_dir(cwd, sizeof(cwd)); /* in case the full path to the blend isn't used */
|
2008-09-15 01:32:53 +00:00
|
|
|
|
|
|
|
if (cwd[0] == '\0') {
|
2012-04-29 17:11:40 +00:00
|
|
|
printf("Could not get the current working directory - $PWD for an unknown reason.\n");
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2008-09-15 01:32:53 +00:00
|
|
|
/* uses the blend path relative to cwd important for loading relative linked files.
|
2012-03-03 20:19:11 +00:00
|
|
|
*
|
|
|
|
* 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
|
2012-03-18 07:38:51 +00:00
|
|
|
* blend file which isn't a feature we want to use in this case since were dealing
|
2012-03-03 20:19:11 +00:00
|
|
|
* with a path from the command line, rather than from inside Blender */
|
|
|
|
|
2011-11-26 04:07:38 +00:00
|
|
|
char origpath[FILE_MAX];
|
|
|
|
BLI_strncpy(origpath, path, FILE_MAX);
|
2008-09-15 01:32:53 +00:00
|
|
|
|
|
|
|
BLI_make_file_string(NULL, path, cwd, origpath);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
return wasrelative;
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Copies into *last the part of *dir following the second-last slash.
|
|
|
|
*/
|
2012-05-12 15:13:06 +00:00
|
|
|
void BLI_getlastdir(const char *dir, char *last, const size_t maxlen)
|
2009-06-30 20:34:00 +00:00
|
|
|
{
|
2009-07-09 15:40:04 +00:00
|
|
|
const char *s = dir;
|
|
|
|
const char *lslash = NULL;
|
|
|
|
const char *prevslash = NULL;
|
2009-06-30 20:34:00 +00:00
|
|
|
while (*s) {
|
|
|
|
if ((*s == '\\') || (*s == '/')) {
|
|
|
|
prevslash = lslash;
|
|
|
|
lslash = s;
|
|
|
|
}
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
if (prevslash) {
|
2012-05-12 15:13:06 +00:00
|
|
|
BLI_strncpy(last, prevslash + 1, maxlen);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2009-06-30 20:34:00 +00:00
|
|
|
BLI_strncpy(last, dir, maxlen);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-07-16 16:02:07 +00:00
|
|
|
/* This is now only used to really get the user's default document folder */
|
2010-07-15 21:39:47 +00:00
|
|
|
/* On Windows I chose the 'Users/<MyUserName>/Documents' since it's used
|
2012-03-03 20:19:11 +00:00
|
|
|
* as default location to save documents */
|
2011-09-28 05:53:40 +00:00
|
|
|
const char *BLI_getDefaultDocumentFolder(void)
|
|
|
|
{
|
2011-11-02 22:00:22 +00:00
|
|
|
#ifndef WIN32
|
2013-03-05 03:17:46 +00:00
|
|
|
const char * const xdg_documents_dir = getenv("XDG_DOCUMENTS_DIR");
|
2011-11-02 22:00:22 +00:00
|
|
|
|
2012-05-25 12:08:29 +00:00
|
|
|
if (xdg_documents_dir)
|
2012-05-12 15:13:06 +00:00
|
|
|
return xdg_documents_dir;
|
2011-11-02 22:00:22 +00:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
return getenv("HOME");
|
2011-11-02 22:00:22 +00:00
|
|
|
#else /* Windows */
|
2012-05-12 15:13:06 +00:00
|
|
|
static char documentfolder[MAXPATHLEN];
|
|
|
|
HRESULT hResult;
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
/* Check for %HOME% env var */
|
|
|
|
if (uput_getenv("HOME", documentfolder, MAXPATHLEN)) {
|
|
|
|
if (BLI_is_dir(documentfolder)) return documentfolder;
|
|
|
|
}
|
2003-08-11 18:53:23 +00:00
|
|
|
|
2012-05-12 15:13:06 +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);
|
2008-04-21 15:53:32 +00:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
if (hResult == S_OK) {
|
|
|
|
if (BLI_is_dir(documentfolder)) return documentfolder;
|
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
return NULL;
|
2011-11-02 22:00:22 +00:00
|
|
|
#endif /* WIN32 */
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
2010-07-04 15:35:23 +00:00
|
|
|
/* NEW stuff, to be cleaned up when fully migrated */
|
|
|
|
/* ************************************************************* */
|
|
|
|
/* ************************************************************* */
|
|
|
|
|
2013-02-06 11:24:13 +00:00
|
|
|
// #define PATH_DEBUG
|
2010-07-04 15:35:23 +00:00
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/* returns a formatted representation of the specified version number. Non-reentrant! */
|
2011-04-11 13:56:58 +00:00
|
|
|
static char *blender_version_decimal(const int ver)
|
2010-07-04 15:35:23 +00:00
|
|
|
{
|
|
|
|
static char version_str[5];
|
2012-05-12 15:13:06 +00:00
|
|
|
sprintf(version_str, "%d.%02d", ver / 100, ver % 100);
|
2010-07-04 15:35:23 +00:00
|
|
|
return version_str;
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Concatenates path_base, (optional) path_sep and (optional) folder_name into targetpath,
|
|
|
|
* returning true if result points to a directory.
|
|
|
|
*/
|
|
|
|
static bool test_path(char *targetpath, const char *path_base, const char *path_sep, const char *folder_name)
|
2010-07-04 15:35:23 +00:00
|
|
|
{
|
|
|
|
char tmppath[FILE_MAX];
|
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
if (path_sep) BLI_join_dirfile(tmppath, sizeof(tmppath), path_base, path_sep);
|
|
|
|
else BLI_strncpy(tmppath, path_base, sizeof(tmppath));
|
2011-04-11 13:56:58 +00:00
|
|
|
|
|
|
|
/* rare cases folder_name is omitted (when looking for ~/.blender/2.xx dir only) */
|
2012-03-24 06:18:31 +00:00
|
|
|
if (folder_name)
|
2011-04-11 13:56:58 +00:00
|
|
|
BLI_make_file_string("/", targetpath, tmppath, folder_name);
|
|
|
|
else
|
|
|
|
BLI_strncpy(targetpath, tmppath, sizeof(tmppath));
|
2013-03-05 03:17:46 +00:00
|
|
|
/* FIXME: why is "//" on front of tmppath expanded to "/" (by BLI_join_dirfile)
|
|
|
|
* if folder_name is specified but not otherwise? */
|
2011-04-11 13:56:58 +00:00
|
|
|
|
2010-07-14 10:44:34 +00:00
|
|
|
if (BLI_is_dir(targetpath)) {
|
2013-02-06 11:24:13 +00:00
|
|
|
#ifdef PATH_DEBUG
|
|
|
|
printf("\t%s found: %s\n", __func__, targetpath);
|
2010-07-04 15:35:23 +00:00
|
|
|
#endif
|
2013-03-05 03:17:46 +00:00
|
|
|
return true;
|
2010-07-04 15:35:23 +00:00
|
|
|
}
|
|
|
|
else {
|
2013-02-06 11:24:13 +00:00
|
|
|
#ifdef PATH_DEBUG
|
|
|
|
printf("\t%s missing: %s\n", __func__, targetpath);
|
2010-07-04 15:35:23 +00:00
|
|
|
#endif
|
|
|
|
//targetpath[0] = '\0';
|
2013-03-05 03:17:46 +00:00
|
|
|
return false;
|
2010-07-04 15:35:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Puts the value of the specified environment variable into *path if it exists
|
|
|
|
* and points at a directory. Returns true if this was done.
|
|
|
|
*/
|
|
|
|
static bool test_env_path(char *path, const char *envvar)
|
2010-07-04 15:35:23 +00:00
|
|
|
{
|
2012-05-12 15:13:06 +00:00
|
|
|
const char *env = envvar ? getenv(envvar) : NULL;
|
2013-03-05 03:17:46 +00:00
|
|
|
if (!env) return false;
|
2010-07-04 15:35:23 +00:00
|
|
|
|
2010-07-14 10:44:34 +00:00
|
|
|
if (BLI_is_dir(env)) {
|
2010-07-15 19:42:36 +00:00
|
|
|
BLI_strncpy(path, env, FILE_MAX);
|
2013-02-06 11:24:13 +00:00
|
|
|
#ifdef PATH_DEBUG
|
|
|
|
printf("\t%s env %s found: %s\n", __func__, envvar, env);
|
|
|
|
#endif
|
2013-03-05 03:17:46 +00:00
|
|
|
return true;
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2010-07-04 15:35:23 +00:00
|
|
|
path[0] = '\0';
|
2013-02-06 11:24:13 +00:00
|
|
|
#ifdef PATH_DEBUG
|
|
|
|
printf("\t%s env %s missing: %s\n", __func__, envvar, env);
|
|
|
|
#endif
|
2013-03-05 03:17:46 +00:00
|
|
|
return false;
|
2010-07-04 15:35:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Constructs in \a targetpath the name of a directory relative to a version-specific
|
|
|
|
* subdirectory in the parent directory of the Blender executable.
|
|
|
|
*
|
|
|
|
* \param targetpath String to return path
|
|
|
|
* \param folder_name Optional folder name within version-specific directory
|
|
|
|
* \param subfolder_name Optional subfolder name within folder_name
|
|
|
|
* \param ver To construct name of version-specific directory within bprogdir
|
|
|
|
* \return true if such a directory exists.
|
|
|
|
*/
|
|
|
|
static bool get_path_local(char *targetpath, const char *folder_name, const char *subfolder_name, const int ver)
|
2010-07-04 15:35:23 +00:00
|
|
|
{
|
2010-07-15 19:42:36 +00:00
|
|
|
char relfolder[FILE_MAX];
|
2010-07-04 15:35:23 +00:00
|
|
|
|
2013-02-06 11:24:13 +00:00
|
|
|
#ifdef PATH_DEBUG
|
|
|
|
printf("%s...\n", __func__);
|
2010-07-04 15:35:23 +00:00
|
|
|
#endif
|
2011-04-11 13:56:58 +00:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (folder_name) {
|
2011-04-11 13:56:58 +00:00
|
|
|
if (subfolder_name) {
|
|
|
|
BLI_join_dirfile(relfolder, sizeof(relfolder), folder_name, subfolder_name);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2011-04-11 13:56:58 +00:00
|
|
|
BLI_strncpy(relfolder, folder_name, sizeof(relfolder));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2012-05-12 15:13:06 +00:00
|
|
|
relfolder[0] = '\0';
|
2010-07-15 19:42:36 +00:00
|
|
|
}
|
2011-10-21 02:13:36 +00:00
|
|
|
|
2010-10-03 21:13:52 +00:00
|
|
|
/* try EXECUTABLE_DIR/2.5x/folder_name - new default directory for local blender installed files */
|
2013-03-05 03:17:46 +00:00
|
|
|
return test_path(targetpath, bprogdir, blender_version_decimal(ver), relfolder);
|
2010-07-04 15:35:23 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Is this an install with user files kept together with the Blender executable and its
|
|
|
|
* installation files.
|
|
|
|
*/
|
|
|
|
static bool is_portable_install(void)
|
Windows installer and Path changes, fixing various issues:
* Windows installer not working for non-admin users and multiple users
* Addon scripts not installing next to user configuration
* Portable install not being taken into account in all places
The main problem was the windows installer was installing system scripts in
AppData next to the user configuration directory, which is not shared between
users. Now these are installed in ProgramFiles, and only addon scripts added
by the users go to AppData.
On all platforms, addon scripts were sometimes getting installed between
system scripts, because the scripts folder in the executable directory was
given precedence over the user configuration folder, that is no longer done
now. So addons now behave like user configuration, they are preserved even
if you download a newer build of the same blender version.
If you have an installation of 2.57 on windows, the addon install location
will not change until we do the version bump to 2.58, to avoid conflicts with
the existing the installed 2.57 version.
The old behavior of giving precedence to the local folder was done to support
portable install, where all configuration is written to the local folder. This
is now implemented differently: if and only if a "config" folder exists in the
local folder, portable install will be assumed, and files will only be written
to that local folder.
2011-05-27 09:57:53 +00:00
|
|
|
{
|
2013-03-05 03:17:46 +00:00
|
|
|
/* detect portable install by the existence of config folder */
|
2012-05-12 15:13:06 +00:00
|
|
|
const int ver = BLENDER_VERSION;
|
Windows installer and Path changes, fixing various issues:
* Windows installer not working for non-admin users and multiple users
* Addon scripts not installing next to user configuration
* Portable install not being taken into account in all places
The main problem was the windows installer was installing system scripts in
AppData next to the user configuration directory, which is not shared between
users. Now these are installed in ProgramFiles, and only addon scripts added
by the users go to AppData.
On all platforms, addon scripts were sometimes getting installed between
system scripts, because the scripts folder in the executable directory was
given precedence over the user configuration folder, that is no longer done
now. So addons now behave like user configuration, they are preserved even
if you download a newer build of the same blender version.
If you have an installation of 2.57 on windows, the addon install location
will not change until we do the version bump to 2.58, to avoid conflicts with
the existing the installed 2.57 version.
The old behavior of giving precedence to the local folder was done to support
portable install, where all configuration is written to the local folder. This
is now implemented differently: if and only if a "config" folder exists in the
local folder, portable install will be assumed, and files will only be written
to that local folder.
2011-05-27 09:57:53 +00:00
|
|
|
char path[FILE_MAX];
|
|
|
|
|
|
|
|
return get_path_local(path, "config", NULL, ver);
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Returns the path of a folder within the user-files area.
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* \param targetpath String to return path
|
|
|
|
* \param folder_name default name of folder within user area
|
|
|
|
* \param subfolder_name optional name of subfolder within folder
|
|
|
|
* \param envvar name of environment variable which, if defined, overrides folder_name
|
|
|
|
* \param ver Blender version, used to construct a subdirectory name
|
|
|
|
* \return true if it was able to construct such a path.
|
|
|
|
*/
|
|
|
|
static bool get_path_user(char *targetpath, const char *folder_name, const char *subfolder_name, const char *envvar, const int ver)
|
2010-07-04 15:35:23 +00:00
|
|
|
{
|
|
|
|
char user_path[FILE_MAX];
|
2010-07-04 21:14:59 +00:00
|
|
|
const char *user_base_path;
|
Windows installer and Path changes, fixing various issues:
* Windows installer not working for non-admin users and multiple users
* Addon scripts not installing next to user configuration
* Portable install not being taken into account in all places
The main problem was the windows installer was installing system scripts in
AppData next to the user configuration directory, which is not shared between
users. Now these are installed in ProgramFiles, and only addon scripts added
by the users go to AppData.
On all platforms, addon scripts were sometimes getting installed between
system scripts, because the scripts folder in the executable directory was
given precedence over the user configuration folder, that is no longer done
now. So addons now behave like user configuration, they are preserved even
if you download a newer build of the same blender version.
If you have an installation of 2.57 on windows, the addon install location
will not change until we do the version bump to 2.58, to avoid conflicts with
the existing the installed 2.57 version.
The old behavior of giving precedence to the local folder was done to support
portable install, where all configuration is written to the local folder. This
is now implemented differently: if and only if a "config" folder exists in the
local folder, portable install will be assumed, and files will only be written
to that local folder.
2011-05-27 09:57:53 +00:00
|
|
|
|
|
|
|
/* for portable install, user path is always local */
|
|
|
|
if (is_portable_install())
|
|
|
|
return get_path_local(targetpath, folder_name, subfolder_name, ver);
|
2010-07-04 21:14:59 +00:00
|
|
|
|
2010-07-04 15:35:23 +00:00
|
|
|
user_path[0] = '\0';
|
|
|
|
|
2010-07-15 19:42:36 +00:00
|
|
|
if (test_env_path(user_path, envvar)) {
|
|
|
|
if (subfolder_name) {
|
|
|
|
return test_path(targetpath, user_path, NULL, subfolder_name);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2010-07-15 19:42:36 +00:00
|
|
|
BLI_strncpy(targetpath, user_path, FILE_MAX);
|
2013-03-05 03:17:46 +00:00
|
|
|
return true;
|
2010-07-15 19:42:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-05-25 12:08:29 +00:00
|
|
|
user_base_path = (const char *)GHOST_getUserDir(ver, blender_version_decimal(ver));
|
|
|
|
if (user_base_path)
|
|
|
|
BLI_strncpy(user_path, user_base_path, FILE_MAX);
|
2010-07-04 15:35:23 +00:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (!user_path[0])
|
2013-03-05 03:17:46 +00:00
|
|
|
return false;
|
2010-07-04 15:35:23 +00:00
|
|
|
|
2013-02-06 11:24:13 +00:00
|
|
|
#ifdef PATH_DEBUG
|
|
|
|
printf("%s: %s\n", __func__, user_path);
|
2010-07-04 15:35:23 +00:00
|
|
|
#endif
|
|
|
|
|
2010-07-15 19:42:36 +00:00
|
|
|
if (subfolder_name) {
|
|
|
|
return test_path(targetpath, user_path, folder_name, subfolder_name);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2010-07-15 19:42:36 +00:00
|
|
|
return test_path(targetpath, user_path, NULL, folder_name);
|
|
|
|
}
|
2010-07-04 15:35:23 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Returns the path of a folder within the Blender installation directory.
|
|
|
|
*
|
|
|
|
* \param targetpath String to return path
|
|
|
|
* \param folder_name default name of folder within installation area
|
|
|
|
* \param subfolder_name optional name of subfolder within folder
|
|
|
|
* \param envvar name of environment variable which, if defined, overrides folder_name
|
|
|
|
* \param ver Blender version, used to construct a subdirectory name
|
|
|
|
* \return true if it was able to construct such a path.
|
|
|
|
*/
|
|
|
|
static bool get_path_system(char *targetpath, const char *folder_name, const char *subfolder_name, const char *envvar, const int ver)
|
2010-07-04 15:35:23 +00:00
|
|
|
{
|
|
|
|
char system_path[FILE_MAX];
|
2010-07-04 21:14:59 +00:00
|
|
|
const char *system_base_path;
|
2010-10-03 21:13:52 +00:00
|
|
|
char cwd[FILE_MAX];
|
|
|
|
char relfolder[FILE_MAX];
|
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (folder_name) {
|
2011-04-11 13:56:58 +00:00
|
|
|
if (subfolder_name) {
|
|
|
|
BLI_join_dirfile(relfolder, sizeof(relfolder), folder_name, subfolder_name);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2011-04-11 13:56:58 +00:00
|
|
|
BLI_strncpy(relfolder, folder_name, sizeof(relfolder));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2012-05-12 15:13:06 +00:00
|
|
|
relfolder[0] = '\0';
|
2010-10-03 21:13:52 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/* first allow developer only overrides to the system path
|
|
|
|
* these are only used when running blender from source */
|
|
|
|
|
2010-10-03 21:13:52 +00:00
|
|
|
/* try CWD/release/folder_name */
|
2012-03-24 06:18:31 +00:00
|
|
|
if (BLI_current_working_dir(cwd, sizeof(cwd))) {
|
|
|
|
if (test_path(targetpath, cwd, "release", relfolder)) {
|
2013-03-05 03:17:46 +00:00
|
|
|
return true;
|
2011-02-13 03:21:27 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-10-03 21:13:52 +00:00
|
|
|
/* try EXECUTABLE_DIR/release/folder_name */
|
2012-03-24 06:18:31 +00:00
|
|
|
if (test_path(targetpath, bprogdir, "release", relfolder))
|
2013-03-05 03:17:46 +00:00
|
|
|
return true;
|
|
|
|
|
2010-10-03 21:13:52 +00:00
|
|
|
/* end developer overrides */
|
|
|
|
|
|
|
|
|
|
|
|
|
2010-07-04 21:14:59 +00:00
|
|
|
system_path[0] = '\0';
|
2010-07-04 15:35:23 +00:00
|
|
|
|
2010-07-15 19:42:36 +00:00
|
|
|
if (test_env_path(system_path, envvar)) {
|
|
|
|
if (subfolder_name) {
|
|
|
|
return test_path(targetpath, system_path, NULL, subfolder_name);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2010-07-15 19:42:36 +00:00
|
|
|
BLI_strncpy(targetpath, system_path, FILE_MAX);
|
2013-03-05 03:17:46 +00:00
|
|
|
return true;
|
2010-07-15 19:42:36 +00:00
|
|
|
}
|
|
|
|
}
|
2010-07-04 21:14:59 +00:00
|
|
|
|
2012-05-25 12:08:29 +00:00
|
|
|
system_base_path = (const char *)GHOST_getSystemDir(ver, blender_version_decimal(ver));
|
|
|
|
if (system_base_path)
|
|
|
|
BLI_strncpy(system_path, system_base_path, FILE_MAX);
|
2010-07-04 15:35:23 +00:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (!system_path[0])
|
2013-03-05 03:17:46 +00:00
|
|
|
return false;
|
2010-07-04 15:35:23 +00:00
|
|
|
|
2013-02-06 11:24:13 +00:00
|
|
|
#ifdef PATH_DEBUG
|
|
|
|
printf("%s: %s\n", __func__, system_path);
|
2010-07-04 15:35:23 +00:00
|
|
|
#endif
|
|
|
|
|
2010-07-15 19:42:36 +00:00
|
|
|
if (subfolder_name) {
|
|
|
|
/* try $BLENDERPATH/folder_name/subfolder_name */
|
|
|
|
return test_path(targetpath, system_path, folder_name, subfolder_name);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2010-07-15 19:42:36 +00:00
|
|
|
/* try $BLENDERPATH/folder_name */
|
|
|
|
return test_path(targetpath, system_path, NULL, folder_name);
|
|
|
|
}
|
2010-07-04 15:35:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* get a folder out of the 'folder_id' presets for paths */
|
|
|
|
/* returns the path if found, NULL string if not */
|
2013-03-05 03:17:46 +00:00
|
|
|
const char *BLI_get_folder(int folder_id, const char *subfolder)
|
2010-07-04 15:35:23 +00:00
|
|
|
{
|
2012-05-12 15:13:06 +00:00
|
|
|
const int ver = BLENDER_VERSION;
|
2010-07-04 15:35:23 +00:00
|
|
|
static char path[FILE_MAX] = "";
|
|
|
|
|
|
|
|
switch (folder_id) {
|
2012-05-12 15:13:06 +00:00
|
|
|
case BLENDER_DATAFILES: /* general case */
|
|
|
|
if (get_path_user(path, "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver)) break;
|
Windows installer and Path changes, fixing various issues:
* Windows installer not working for non-admin users and multiple users
* Addon scripts not installing next to user configuration
* Portable install not being taken into account in all places
The main problem was the windows installer was installing system scripts in
AppData next to the user configuration directory, which is not shared between
users. Now these are installed in ProgramFiles, and only addon scripts added
by the users go to AppData.
On all platforms, addon scripts were sometimes getting installed between
system scripts, because the scripts folder in the executable directory was
given precedence over the user configuration folder, that is no longer done
now. So addons now behave like user configuration, they are preserved even
if you download a newer build of the same blender version.
If you have an installation of 2.57 on windows, the addon install location
will not change until we do the version bump to 2.58, to avoid conflicts with
the existing the installed 2.57 version.
The old behavior of giving precedence to the local folder was done to support
portable install, where all configuration is written to the local folder. This
is now implemented differently: if and only if a "config" folder exists in the
local folder, portable install will be assumed, and files will only be written
to that local folder.
2011-05-27 09:57:53 +00:00
|
|
|
if (get_path_local(path, "datafiles", subfolder, ver)) break;
|
2011-04-11 13:56:58 +00:00
|
|
|
if (get_path_system(path, "datafiles", subfolder, "BLENDER_SYSTEM_DATAFILES", ver)) break;
|
2010-07-04 15:35:23 +00:00
|
|
|
return NULL;
|
|
|
|
|
2010-07-10 11:09:26 +00:00
|
|
|
case BLENDER_USER_DATAFILES:
|
2012-05-12 15:13:06 +00:00
|
|
|
if (get_path_user(path, "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver)) break;
|
2010-07-04 15:35:23 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
case BLENDER_SYSTEM_DATAFILES:
|
2011-04-11 13:56:58 +00:00
|
|
|
if (get_path_local(path, "datafiles", subfolder, ver)) break;
|
2012-05-12 15:13:06 +00:00
|
|
|
if (get_path_system(path, "datafiles", subfolder, "BLENDER_SYSTEM_DATAFILES", ver)) break;
|
2010-07-04 15:35:23 +00:00
|
|
|
return NULL;
|
|
|
|
|
2010-07-15 21:39:47 +00:00
|
|
|
case BLENDER_USER_AUTOSAVE:
|
2012-05-12 15:13:06 +00:00
|
|
|
if (get_path_user(path, "autosave", subfolder, "BLENDER_USER_DATAFILES", ver)) break;
|
2010-07-15 21:39:47 +00:00
|
|
|
return NULL;
|
|
|
|
|
2010-07-04 15:35:23 +00:00
|
|
|
case BLENDER_USER_CONFIG:
|
2011-04-11 13:56:58 +00:00
|
|
|
if (get_path_user(path, "config", subfolder, "BLENDER_USER_CONFIG", ver)) break;
|
2010-07-04 15:35:23 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
case BLENDER_USER_SCRIPTS:
|
2011-07-04 13:33:47 +00:00
|
|
|
if (get_path_user(path, "scripts", subfolder, "BLENDER_USER_SCRIPTS", ver)) break;
|
2010-07-04 15:35:23 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
case BLENDER_SYSTEM_SCRIPTS:
|
2011-04-11 13:56:58 +00:00
|
|
|
if (get_path_local(path, "scripts", subfolder, ver)) break;
|
|
|
|
if (get_path_system(path, "scripts", subfolder, "BLENDER_SYSTEM_SCRIPTS", ver)) break;
|
2010-07-04 15:35:23 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
case BLENDER_SYSTEM_PYTHON:
|
2011-04-11 13:56:58 +00:00
|
|
|
if (get_path_local(path, "python", subfolder, ver)) break;
|
|
|
|
if (get_path_system(path, "python", subfolder, "BLENDER_SYSTEM_PYTHON", ver)) break;
|
2010-07-04 15:35:23 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Returns the path to a folder in the user area without checking that it actually exists first.
|
|
|
|
*/
|
|
|
|
const char *BLI_get_user_folder_notest(int folder_id, const char *subfolder)
|
2010-07-04 15:35:23 +00:00
|
|
|
{
|
2012-05-12 15:13:06 +00:00
|
|
|
const int ver = BLENDER_VERSION;
|
2010-07-04 15:35:23 +00:00
|
|
|
static char path[FILE_MAX] = "";
|
|
|
|
|
|
|
|
switch (folder_id) {
|
|
|
|
case BLENDER_USER_DATAFILES:
|
2011-04-11 13:56:58 +00:00
|
|
|
get_path_user(path, "datafiles", subfolder, "BLENDER_USER_DATAFILES", ver);
|
2010-07-04 15:35:23 +00:00
|
|
|
break;
|
|
|
|
case BLENDER_USER_CONFIG:
|
2011-04-11 13:56:58 +00:00
|
|
|
get_path_user(path, "config", subfolder, "BLENDER_USER_CONFIG", ver);
|
2010-07-15 21:39:47 +00:00
|
|
|
break;
|
|
|
|
case BLENDER_USER_AUTOSAVE:
|
2011-04-11 13:56:58 +00:00
|
|
|
get_path_user(path, "autosave", subfolder, "BLENDER_USER_AUTOSAVE", ver);
|
2010-07-04 15:35:23 +00:00
|
|
|
break;
|
2010-10-03 20:00:22 +00:00
|
|
|
case BLENDER_USER_SCRIPTS:
|
2011-04-11 13:56:58 +00:00
|
|
|
get_path_user(path, "scripts", subfolder, "BLENDER_USER_SCRIPTS", ver);
|
2010-10-03 20:00:22 +00:00
|
|
|
break;
|
2010-07-04 15:35:23 +00:00
|
|
|
}
|
|
|
|
if ('\0' == path[0]) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Returns the path to a folder in the user area, creating it if it doesn't exist.
|
|
|
|
*/
|
|
|
|
const char *BLI_get_folder_create(int folder_id, const char *subfolder)
|
2010-07-04 15:35:23 +00:00
|
|
|
{
|
2013-03-05 03:17:46 +00:00
|
|
|
const char *path;
|
2010-07-04 15:35:23 +00:00
|
|
|
|
|
|
|
/* only for user folders */
|
2010-10-03 20:00:22 +00:00
|
|
|
if (!ELEM4(folder_id, BLENDER_USER_DATAFILES, BLENDER_USER_CONFIG, BLENDER_USER_SCRIPTS, BLENDER_USER_AUTOSAVE))
|
2010-07-04 15:35:23 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
path = BLI_get_folder(folder_id, subfolder);
|
|
|
|
|
|
|
|
if (!path) {
|
|
|
|
path = BLI_get_user_folder_notest(folder_id, subfolder);
|
2011-10-22 15:35:49 +00:00
|
|
|
if (path) BLI_dir_create_recursive(path);
|
2010-07-04 15:35:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Returns the path of the top-level version-specific local, user or system directory.
|
|
|
|
* If do_check, then the result will be NULL if the directory doesn't exist.
|
|
|
|
*/
|
|
|
|
const char *BLI_get_folder_version(const int id, const int ver, const bool do_check)
|
2011-04-11 13:56:58 +00:00
|
|
|
{
|
|
|
|
static char path[FILE_MAX] = "";
|
2013-03-05 03:17:46 +00:00
|
|
|
bool ok;
|
2012-04-28 06:31:57 +00:00
|
|
|
switch (id) {
|
2012-05-12 15:13:06 +00:00
|
|
|
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'; /* in case do_check is false */
|
2013-03-05 03:17:46 +00:00
|
|
|
ok = false;
|
2012-05-12 15:13:06 +00:00
|
|
|
BLI_assert(!"incorrect ID");
|
2011-04-11 13:56:58 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
if (!ok && do_check) {
|
2011-04-11 13:56:58 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return path;
|
|
|
|
}
|
2010-07-04 15:35:23 +00:00
|
|
|
|
|
|
|
/* End new stuff */
|
|
|
|
/* ************************************************************* */
|
|
|
|
/* ************************************************************* */
|
|
|
|
|
|
|
|
|
|
|
|
|
2009-09-21 03:16:26 +00:00
|
|
|
#ifdef PATH_DEBUG
|
2013-02-06 11:24:13 +00:00
|
|
|
# undef PATH_DEBUG
|
2009-09-21 03:16:26 +00:00
|
|
|
#endif
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Sets the specified environment variable to the specified value.
|
|
|
|
*/
|
2012-05-12 15:13:06 +00:00
|
|
|
void BLI_setenv(const char *env, const char *val)
|
2009-07-21 09:26:28 +00:00
|
|
|
{
|
2011-09-21 08:40:30 +00:00
|
|
|
/* free windows */
|
|
|
|
#if (defined(WIN32) || defined(WIN64)) && defined(FREE_WINDOWS)
|
2012-05-12 15:13:06 +00:00
|
|
|
char *envstr = MEM_mallocN(sizeof(char) * (strlen(env) + strlen(val) + 2), "envstr"); /* one for = another for \0 */
|
2009-07-21 09:26:28 +00:00
|
|
|
|
|
|
|
sprintf(envstr, "%s=%s", env, val);
|
|
|
|
putenv(envstr);
|
2010-02-08 13:55:31 +00:00
|
|
|
MEM_freeN(envstr);
|
2009-07-21 09:26:28 +00:00
|
|
|
|
|
|
|
/* non-free windows */
|
|
|
|
#elif (defined(WIN32) || defined(WIN64)) /* not free windows */
|
2012-03-20 02:17:37 +00:00
|
|
|
uputenv(env, val);
|
|
|
|
|
|
|
|
|
2009-07-21 09:26:28 +00:00
|
|
|
#else
|
|
|
|
/* linux/osx/bsd */
|
|
|
|
setenv(env, val, 1);
|
|
|
|
#endif
|
|
|
|
}
|
2009-04-11 02:18:24 +00:00
|
|
|
|
2009-10-19 18:44:09 +00:00
|
|
|
|
|
|
|
/**
|
2012-03-03 20:19:11 +00:00
|
|
|
* Only set an env var if already not there.
|
|
|
|
* Like Unix setenv(env, val, 0);
|
2013-03-05 03:17:46 +00:00
|
|
|
*
|
|
|
|
* (not used anywhere).
|
2009-10-19 18:44:09 +00:00
|
|
|
*/
|
2012-05-12 15:13:06 +00:00
|
|
|
void BLI_setenv_if_new(const char *env, const char *val)
|
2009-10-19 18:44:09 +00:00
|
|
|
{
|
2012-03-24 06:18:31 +00:00
|
|
|
if (getenv(env) == NULL)
|
2009-10-19 18:44:09 +00:00
|
|
|
BLI_setenv(env, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Changes to the path separators to the native ones for this OS.
|
|
|
|
*/
|
2005-05-20 12:18:11 +00:00
|
|
|
void BLI_clean(char *path)
|
|
|
|
{
|
|
|
|
#ifdef WIN32
|
2012-03-24 06:18:31 +00:00
|
|
|
if (path && BLI_strnlen(path, 3) > 2) {
|
2012-05-12 15:13:06 +00:00
|
|
|
BLI_char_switch(path + 2, '/', '\\');
|
2005-05-20 12:18:11 +00:00
|
|
|
}
|
|
|
|
#else
|
|
|
|
BLI_char_switch(path, '\\', '/');
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
2013-03-24 01:51:54 +00:00
|
|
|
* Change every \a from in \a string into \a to. The
|
|
|
|
* result will be in \a string
|
|
|
|
*
|
|
|
|
* \param string The string to work on
|
|
|
|
* \param from The character to replace
|
|
|
|
* \param to The character to replace with
|
2013-03-05 03:17:46 +00:00
|
|
|
*/
|
2005-05-20 12:18:11 +00:00
|
|
|
void BLI_char_switch(char *string, char from, char to)
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
|
|
|
while (*string != 0) {
|
|
|
|
if (*string == from) *string = to;
|
|
|
|
string++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Strips off nonexistent subdirectories from the end of *dir, leaving the path of
|
|
|
|
* the lowest-level directory that does exist.
|
|
|
|
*/
|
2011-09-28 05:53:40 +00:00
|
|
|
void BLI_make_exist(char *dir)
|
|
|
|
{
|
2002-10-12 11:37:38 +00:00
|
|
|
int a;
|
|
|
|
|
2010-10-27 06:41:48 +00:00
|
|
|
BLI_char_switch(dir, ALTSEP, SEP);
|
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
a = strlen(dir);
|
2010-10-27 06:41:48 +00:00
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
while (!BLI_is_dir(dir)) {
|
2012-05-12 15:13:06 +00:00
|
|
|
a--;
|
2012-02-23 02:23:42 +00:00
|
|
|
while (dir[a] != SEP) {
|
2002-10-12 11:37:38 +00:00
|
|
|
a--;
|
|
|
|
if (a <= 0) break;
|
|
|
|
}
|
2010-10-27 06:41:48 +00:00
|
|
|
if (a >= 0) {
|
2012-05-12 15:13:06 +00:00
|
|
|
dir[a + 1] = '\0';
|
2010-10-27 06:41:48 +00:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
else {
|
2010-10-27 06:41:48 +00:00
|
|
|
#ifdef WIN32
|
2006-08-20 14:41:13 +00:00
|
|
|
get_default_root(dir);
|
2002-10-12 11:37:38 +00:00
|
|
|
#else
|
2012-04-29 15:47:02 +00:00
|
|
|
strcpy(dir, "/");
|
2010-10-27 06:41:48 +00:00
|
|
|
#endif
|
2002-10-12 11:37:38 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Ensures that the parent directory of *name exists.
|
|
|
|
*/
|
2010-11-16 14:40:46 +00:00
|
|
|
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
|
|
|
{
|
2013-03-05 06:26:10 +00:00
|
|
|
char di[FILE_MAX];
|
|
|
|
BLI_split_dir_part(name, di, sizeof(di));
|
|
|
|
|
|
|
|
/* make if if the dir doesn't exist */
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Returns in *string the concatenation of *dir and *file (also with *relabase on the
|
|
|
|
* front if specified and *dir begins with "//"). Normalizes all occurrences of path
|
|
|
|
* separators, including ensuring there is exactly one between the copies of *dir and *file,
|
|
|
|
* and between the copies of *relabase and *dir.
|
|
|
|
*
|
|
|
|
* \param relabase Optional prefix to substitute for "//" on front of *dir
|
|
|
|
* \param string Area to return result
|
|
|
|
*/
|
|
|
|
void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file)
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
2006-02-11 15:02:23 +00:00
|
|
|
int sl;
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2011-10-23 05:56:55 +00:00
|
|
|
if (string) {
|
|
|
|
/* ensure this is always set even if dir/file are NULL */
|
2012-05-12 15:13:06 +00:00
|
|
|
string[0] = '\0';
|
2011-10-23 05:56:55 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
2005-05-20 12:18:11 +00:00
|
|
|
/* we first push all slashes into unix mode, just to make sure we don't get
|
2012-03-03 20:19:11 +00:00
|
|
|
* any mess with slashes later on. -jesterKing */
|
2005-11-05 13:09:43 +00:00
|
|
|
/* constant strings can be passed for those parameters - don't change them - elubie */
|
2012-03-03 20:19:11 +00:00
|
|
|
#if 0
|
2005-05-20 12:18:11 +00:00
|
|
|
BLI_char_switch(relabase, '\\', '/');
|
|
|
|
BLI_char_switch(dir, '\\', '/');
|
|
|
|
BLI_char_switch(file, '\\', '/');
|
2012-03-03 20:19:11 +00:00
|
|
|
#endif
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2012-10-21 05:46:41 +00:00
|
|
|
/* Resolve relative references */
|
2002-10-12 11:37:38 +00:00
|
|
|
if (relabase && dir[0] == '/' && dir[1] == '/') {
|
|
|
|
char *lslash;
|
|
|
|
|
|
|
|
/* Get the file name, chop everything past the last slash (ie. the filename) */
|
|
|
|
strcpy(string, relabase);
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
lslash = (char *)BLI_last_slash(string);
|
2012-05-12 15:13:06 +00:00
|
|
|
if (lslash) *(lslash + 1) = 0;
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
dir += 2; /* Skip over the relative reference */
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
2006-08-20 14:41:13 +00:00
|
|
|
#ifdef WIN32
|
|
|
|
else {
|
2012-05-12 15:13:06 +00:00
|
|
|
if (BLI_strnlen(dir, 3) >= 2 && dir[1] == ':') {
|
2006-08-20 14:41:13 +00:00
|
|
|
BLI_strncpy(string, dir, 3);
|
|
|
|
dir += 2;
|
|
|
|
}
|
|
|
|
else { /* no drive specified */
|
2012-05-12 15:13:06 +00:00
|
|
|
/* first option: get the drive from the relabase if it has one */
|
|
|
|
if (relabase && strlen(relabase) >= 2 && relabase[1] == ':') {
|
2012-10-21 05:46:41 +00:00
|
|
|
BLI_strncpy(string, relabase, 3);
|
2006-08-20 14:41:13 +00:00
|
|
|
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);
|
|
|
|
|
2012-10-21 05:46:41 +00:00
|
|
|
/* Make sure string ends in one (and only one) slash */
|
2006-02-11 15:02:23 +00:00
|
|
|
/* first trim all slashes from the end of the string */
|
|
|
|
sl = strlen(string);
|
2012-05-12 15:13:06 +00:00
|
|
|
while (sl > 0 && (string[sl - 1] == '/' || string[sl - 1] == '\\') ) {
|
|
|
|
string[sl - 1] = '\0';
|
2006-02-11 15:02:23 +00:00
|
|
|
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++;
|
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
strcat(string, file);
|
2002-10-12 11:37:38 +00:00
|
|
|
|
|
|
|
/* 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
|
|
|
}
|
|
|
|
|
2013-03-04 18:36:37 +00:00
|
|
|
/* does str end with ext. */
|
|
|
|
bool BLI_testextensie(const char *str, const char *ext)
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
2013-03-04 18:36:37 +00:00
|
|
|
const size_t a = strlen(str);
|
|
|
|
const size_t b = strlen(ext);
|
|
|
|
return !(a == 0 || b == 0 || b >= a) && (BLI_strcasecmp(ext, str + a - b) == 0);
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
2013-03-04 18:36:37 +00:00
|
|
|
/* does str end with any of the suffixes in *ext_array. */
|
|
|
|
bool BLI_testextensie_array(const char *str, const char **ext_array)
|
2010-08-03 12:34:42 +00:00
|
|
|
{
|
2012-05-12 15:13:06 +00:00
|
|
|
int i = 0;
|
2012-03-24 06:18:31 +00:00
|
|
|
while (ext_array[i]) {
|
|
|
|
if (BLI_testextensie(str, ext_array[i])) {
|
2013-03-04 18:36:37 +00:00
|
|
|
return true;
|
2010-08-03 12:34:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
2013-03-04 18:36:37 +00:00
|
|
|
return false;
|
2010-08-03 12:34:42 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Semicolon separated wildcards, eg:
|
|
|
|
* '*.zip;*.py;*.exe'
|
|
|
|
* does str match any of the semicolon-separated glob patterns in fnmatch.
|
|
|
|
*/
|
2013-03-04 18:36:37 +00:00
|
|
|
bool BLI_testextensie_glob(const char *str, const char *ext_fnmatch)
|
2010-09-24 06:20:43 +00:00
|
|
|
{
|
2012-05-12 15:13:06 +00:00
|
|
|
const char *ext_step = ext_fnmatch;
|
2010-09-24 06:20:43 +00:00
|
|
|
char pattern[16];
|
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
while (ext_step[0]) {
|
2010-09-24 06:20:43 +00:00
|
|
|
char *ext_next;
|
|
|
|
int len_ext;
|
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
if ((ext_next = strchr(ext_step, ';'))) {
|
|
|
|
len_ext = (int)(ext_next - ext_step) + 1;
|
2010-09-24 06:20:43 +00:00
|
|
|
}
|
|
|
|
else {
|
2012-05-12 15:13:06 +00:00
|
|
|
len_ext = sizeof(pattern);
|
2010-09-24 06:20:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
BLI_strncpy(pattern, ext_step, len_ext);
|
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
if (fnmatch(pattern, str, FNM_CASEFOLD) == 0) {
|
2013-03-04 18:36:37 +00:00
|
|
|
return true;
|
2010-09-24 06:20:43 +00:00
|
|
|
}
|
|
|
|
ext_step += len_ext;
|
|
|
|
}
|
|
|
|
|
2013-03-04 18:36:37 +00:00
|
|
|
return false;
|
2010-09-24 06:20:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Removes any existing extension on the end of \a path and appends \a ext.
|
|
|
|
* \return false if there was no room.
|
|
|
|
*/
|
|
|
|
bool BLI_replace_extension(char *path, size_t maxlen, const char *ext)
|
Merge image related changes from the render branch. This includes the image
tile cache code in imbuf, but it is not hooked up to the render engine.
Imbuf module: some small refactoring and removing a lot of unused or old code
(about 6.5k lines).
* Added a ImFileType struct with callbacks to make adding an file format type,
or making changes to the API easier.
* Move imbuf init/exit code into IMB_init()/IMB_exit() functions.
* Increased mipmap levels from 10 to 20, you run into this limit already with
a 2k image.
* Removed hamx, amiga, anim5 format support.
* Removed colormap saving, only simple colormap code now for reading tga.
* Removed gen_dynlibtiff.py, editing this is almost as much work as just
editing the code directly.
* Functions removed that were only used for sequencer plugin API:
IMB_anim_nextpic, IMB_clever_double, IMB_antialias, IMB_gamwarp,
IMB_scalefieldImBuf, IMB_scalefastfieldImBuf, IMB_onethird, IMB_halflace,
IMB_dit0, IMB_dit2, IMB_cspace
* Write metadata info into OpenEXR images. Can be viewed with the command
line utility 'exrheader'
For the image tile cache code, see this page:
http://wiki.blender.org/index.php/Dev:2.5/Source/Imaging/ImageTileCache
2010-05-07 15:18:04 +00:00
|
|
|
{
|
2013-03-05 03:17:46 +00:00
|
|
|
const size_t path_len = strlen(path);
|
|
|
|
const size_t ext_len = strlen(ext);
|
2011-12-21 20:56:49 +00:00
|
|
|
ssize_t a;
|
Merge image related changes from the render branch. This includes the image
tile cache code in imbuf, but it is not hooked up to the render engine.
Imbuf module: some small refactoring and removing a lot of unused or old code
(about 6.5k lines).
* Added a ImFileType struct with callbacks to make adding an file format type,
or making changes to the API easier.
* Move imbuf init/exit code into IMB_init()/IMB_exit() functions.
* Increased mipmap levels from 10 to 20, you run into this limit already with
a 2k image.
* Removed hamx, amiga, anim5 format support.
* Removed colormap saving, only simple colormap code now for reading tga.
* Removed gen_dynlibtiff.py, editing this is almost as much work as just
editing the code directly.
* Functions removed that were only used for sequencer plugin API:
IMB_anim_nextpic, IMB_clever_double, IMB_antialias, IMB_gamwarp,
IMB_scalefieldImBuf, IMB_scalefastfieldImBuf, IMB_onethird, IMB_halflace,
IMB_dit0, IMB_dit2, IMB_cspace
* Write metadata info into OpenEXR images. Can be viewed with the command
line utility 'exrheader'
For the image tile cache code, see this page:
http://wiki.blender.org/index.php/Dev:2.5/Source/Imaging/ImageTileCache
2010-05-07 15:18:04 +00:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
for (a = path_len - 1; a >= 0; a--) {
|
2011-10-31 00:23:42 +00:00
|
|
|
if (ELEM3(path[a], '.', '/', '\\')) {
|
Merge image related changes from the render branch. This includes the image
tile cache code in imbuf, but it is not hooked up to the render engine.
Imbuf module: some small refactoring and removing a lot of unused or old code
(about 6.5k lines).
* Added a ImFileType struct with callbacks to make adding an file format type,
or making changes to the API easier.
* Move imbuf init/exit code into IMB_init()/IMB_exit() functions.
* Increased mipmap levels from 10 to 20, you run into this limit already with
a 2k image.
* Removed hamx, amiga, anim5 format support.
* Removed colormap saving, only simple colormap code now for reading tga.
* Removed gen_dynlibtiff.py, editing this is almost as much work as just
editing the code directly.
* Functions removed that were only used for sequencer plugin API:
IMB_anim_nextpic, IMB_clever_double, IMB_antialias, IMB_gamwarp,
IMB_scalefieldImBuf, IMB_scalefastfieldImBuf, IMB_onethird, IMB_halflace,
IMB_dit0, IMB_dit2, IMB_cspace
* Write metadata info into OpenEXR images. Can be viewed with the command
line utility 'exrheader'
For the image tile cache code, see this page:
http://wiki.blender.org/index.php/Dev:2.5/Source/Imaging/ImageTileCache
2010-05-07 15:18:04 +00:00
|
|
|
break;
|
2011-03-08 08:33:52 +00:00
|
|
|
}
|
|
|
|
}
|
Merge image related changes from the render branch. This includes the image
tile cache code in imbuf, but it is not hooked up to the render engine.
Imbuf module: some small refactoring and removing a lot of unused or old code
(about 6.5k lines).
* Added a ImFileType struct with callbacks to make adding an file format type,
or making changes to the API easier.
* Move imbuf init/exit code into IMB_init()/IMB_exit() functions.
* Increased mipmap levels from 10 to 20, you run into this limit already with
a 2k image.
* Removed hamx, amiga, anim5 format support.
* Removed colormap saving, only simple colormap code now for reading tga.
* Removed gen_dynlibtiff.py, editing this is almost as much work as just
editing the code directly.
* Functions removed that were only used for sequencer plugin API:
IMB_anim_nextpic, IMB_clever_double, IMB_antialias, IMB_gamwarp,
IMB_scalefieldImBuf, IMB_scalefastfieldImBuf, IMB_onethird, IMB_halflace,
IMB_dit0, IMB_dit2, IMB_cspace
* Write metadata info into OpenEXR images. Can be viewed with the command
line utility 'exrheader'
For the image tile cache code, see this page:
http://wiki.blender.org/index.php/Dev:2.5/Source/Imaging/ImageTileCache
2010-05-07 15:18:04 +00:00
|
|
|
|
2011-12-21 20:56:49 +00:00
|
|
|
if ((a < 0) || (path[a] != '.')) {
|
2012-05-12 15:13:06 +00:00
|
|
|
a = path_len;
|
2011-11-15 07:35:28 +00:00
|
|
|
}
|
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (a + ext_len >= maxlen)
|
2013-03-05 03:17:46 +00:00
|
|
|
return false;
|
2011-10-31 00:23:42 +00:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
memcpy(path + a, ext, ext_len + 1);
|
2013-03-05 03:17:46 +00:00
|
|
|
return true;
|
2011-10-31 00:23:42 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Strip's trailing '.'s and adds the extension only when needed
|
|
|
|
*/
|
|
|
|
bool BLI_ensure_extension(char *path, size_t maxlen, const char *ext)
|
2011-10-31 00:23:42 +00:00
|
|
|
{
|
2013-03-05 03:17:46 +00:00
|
|
|
const size_t path_len = strlen(path);
|
|
|
|
const size_t ext_len = strlen(ext);
|
2011-12-21 20:56:49 +00:00
|
|
|
ssize_t a;
|
2011-10-31 00:23:42 +00:00
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/* first check the extension is already there */
|
2011-10-31 00:23:42 +00:00
|
|
|
if ( (ext_len <= path_len) &&
|
|
|
|
(strcmp(path + (path_len - ext_len), ext) == 0))
|
|
|
|
{
|
2013-03-05 03:17:46 +00:00
|
|
|
return true;
|
2011-10-31 00:23:42 +00:00
|
|
|
}
|
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
for (a = path_len - 1; a >= 0; a--) {
|
2011-10-31 00:23:42 +00:00
|
|
|
if (path[a] == '.') {
|
2012-05-12 15:13:06 +00:00
|
|
|
path[a] = '\0';
|
2011-10-31 00:23:42 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
a++;
|
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
if (a + ext_len >= maxlen)
|
2013-03-05 03:17:46 +00:00
|
|
|
return false;
|
Merge image related changes from the render branch. This includes the image
tile cache code in imbuf, but it is not hooked up to the render engine.
Imbuf module: some small refactoring and removing a lot of unused or old code
(about 6.5k lines).
* Added a ImFileType struct with callbacks to make adding an file format type,
or making changes to the API easier.
* Move imbuf init/exit code into IMB_init()/IMB_exit() functions.
* Increased mipmap levels from 10 to 20, you run into this limit already with
a 2k image.
* Removed hamx, amiga, anim5 format support.
* Removed colormap saving, only simple colormap code now for reading tga.
* Removed gen_dynlibtiff.py, editing this is almost as much work as just
editing the code directly.
* Functions removed that were only used for sequencer plugin API:
IMB_anim_nextpic, IMB_clever_double, IMB_antialias, IMB_gamwarp,
IMB_scalefieldImBuf, IMB_scalefastfieldImBuf, IMB_onethird, IMB_halflace,
IMB_dit0, IMB_dit2, IMB_cspace
* Write metadata info into OpenEXR images. Can be viewed with the command
line utility 'exrheader'
For the image tile cache code, see this page:
http://wiki.blender.org/index.php/Dev:2.5/Source/Imaging/ImageTileCache
2010-05-07 15:18:04 +00:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
memcpy(path + a, ext, ext_len + 1);
|
2013-03-05 03:17:46 +00:00
|
|
|
return true;
|
Merge image related changes from the render branch. This includes the image
tile cache code in imbuf, but it is not hooked up to the render engine.
Imbuf module: some small refactoring and removing a lot of unused or old code
(about 6.5k lines).
* Added a ImFileType struct with callbacks to make adding an file format type,
or making changes to the API easier.
* Move imbuf init/exit code into IMB_init()/IMB_exit() functions.
* Increased mipmap levels from 10 to 20, you run into this limit already with
a 2k image.
* Removed hamx, amiga, anim5 format support.
* Removed colormap saving, only simple colormap code now for reading tga.
* Removed gen_dynlibtiff.py, editing this is almost as much work as just
editing the code directly.
* Functions removed that were only used for sequencer plugin API:
IMB_anim_nextpic, IMB_clever_double, IMB_antialias, IMB_gamwarp,
IMB_scalefieldImBuf, IMB_scalefastfieldImBuf, IMB_onethird, IMB_halflace,
IMB_dit0, IMB_dit2, IMB_cspace
* Write metadata info into OpenEXR images. Can be viewed with the command
line utility 'exrheader'
For the image tile cache code, see this page:
http://wiki.blender.org/index.php/Dev:2.5/Source/Imaging/ImageTileCache
2010-05-07 15:18:04 +00:00
|
|
|
}
|
|
|
|
|
2010-04-23 23:01:50 +00:00
|
|
|
/* Converts "/foo/bar.txt" to "/foo/" and "bar.txt"
|
2008-04-26 13:08:57 +00:00
|
|
|
* - 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
|
|
|
|
* */
|
2011-10-15 03:56:05 +00:00
|
|
|
void BLI_split_dirfile(const char *string, char *dir, char *file, const size_t dirlen, const size_t filelen)
|
2008-04-26 13:08:57 +00:00
|
|
|
{
|
2013-03-04 19:27:51 +00:00
|
|
|
const char *lslash_str = BLI_last_slash(string);
|
2013-03-05 03:17:46 +00:00
|
|
|
const size_t lslash = lslash_str ? (size_t)(lslash_str - string) + 1 : 0;
|
2010-04-23 23:01:50 +00:00
|
|
|
|
2008-04-26 13:08:57 +00:00
|
|
|
if (dir) {
|
|
|
|
if (lslash) {
|
2012-03-11 19:09:01 +00:00
|
|
|
BLI_strncpy(dir, string, MIN2(dirlen, lslash + 1)); /* +1 to include the slash and the last char */
|
2011-10-15 03:56:05 +00:00
|
|
|
}
|
|
|
|
else {
|
2008-04-26 13:08:57 +00:00
|
|
|
dir[0] = '\0';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (file) {
|
2012-05-12 15:13:06 +00:00
|
|
|
BLI_strncpy(file, string + lslash, filelen);
|
2008-04-26 13:08:57 +00:00
|
|
|
}
|
|
|
|
}
|
2005-03-28 15:29:59 +00:00
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Copies the parent directory part of string into *dir, max length dirlen.
|
|
|
|
*/
|
2011-10-21 02:13:36 +00:00
|
|
|
void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen)
|
|
|
|
{
|
|
|
|
BLI_split_dirfile(string, dir, NULL, dirlen, 0);
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Copies the leaf filename part of string into *file, max length filelen.
|
|
|
|
*/
|
2011-10-21 02:13:36 +00:00
|
|
|
void BLI_split_file_part(const char *string, char *file, const size_t filelen)
|
|
|
|
{
|
|
|
|
BLI_split_dirfile(string, NULL, file, 0, filelen);
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Simple appending of filename to dir, does not check for valid path!
|
|
|
|
* Puts result into *dst, which may be same area as *dir.
|
|
|
|
*/
|
2011-10-12 11:18:46 +00:00
|
|
|
void BLI_join_dirfile(char *dst, const size_t maxlen, const char *dir, const char *file)
|
2007-02-28 21:37:14 +00:00
|
|
|
{
|
2012-05-12 15:13:06 +00:00
|
|
|
size_t dirlen = BLI_strnlen(dir, maxlen);
|
2011-10-11 05:21:24 +00:00
|
|
|
|
2011-10-12 11:18:46 +00:00
|
|
|
if (dst != dir) {
|
2012-05-20 19:49:27 +00:00
|
|
|
if (dirlen == maxlen) {
|
2011-10-12 11:18:46 +00:00
|
|
|
memcpy(dst, dir, dirlen);
|
2012-05-12 15:13:06 +00:00
|
|
|
dst[dirlen - 1] = '\0';
|
2011-10-12 11:18:46 +00:00
|
|
|
return; /* dir fills the path */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
memcpy(dst, dir, dirlen + 1);
|
|
|
|
}
|
|
|
|
}
|
2010-07-04 15:35:23 +00:00
|
|
|
|
2011-10-12 11:18:46 +00:00
|
|
|
if (dirlen + 1 >= maxlen) {
|
|
|
|
return; /* fills the path */
|
|
|
|
}
|
2011-10-11 05:21:24 +00:00
|
|
|
|
2011-10-12 11:18:46 +00:00
|
|
|
/* inline BLI_add_slash */
|
2012-03-12 06:53:47 +00:00
|
|
|
if ((dirlen > 0) && (dst[dirlen - 1] != SEP)) {
|
|
|
|
dst[dirlen++] = SEP;
|
2012-05-12 15:13:06 +00:00
|
|
|
dst[dirlen] = '\0';
|
2007-02-28 21:37:14 +00:00
|
|
|
}
|
2011-10-12 11:18:46 +00:00
|
|
|
|
|
|
|
if (dirlen >= maxlen) {
|
|
|
|
return; /* fills the path */
|
|
|
|
}
|
|
|
|
|
|
|
|
if (file == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
BLI_strncpy(dst + dirlen, file, maxlen - dirlen);
|
2007-02-28 21:37:14 +00:00
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* like pythons os.path.basename()
|
|
|
|
*
|
|
|
|
* \return The pointer into \a path string immediately after last slash,
|
|
|
|
* or start of \a path if none found.
|
|
|
|
*/
|
2013-03-04 19:27:51 +00:00
|
|
|
const char *BLI_path_basename(const char *path)
|
2010-06-05 21:19:59 +00:00
|
|
|
{
|
2013-03-04 19:27:51 +00:00
|
|
|
const char * const filename = BLI_last_slash(path);
|
2010-06-05 21:19:59 +00:00
|
|
|
return filename ? filename + 1 : path;
|
|
|
|
}
|
2010-02-26 11:50:59 +00:00
|
|
|
|
2012-03-03 20:19:11 +00:00
|
|
|
/**
|
|
|
|
* Produce image export path.
|
2012-06-22 15:38:49 +00:00
|
|
|
*
|
|
|
|
* Returns:
|
|
|
|
* 0 if image filename is empty or if destination path
|
|
|
|
* matches image path (i.e. both are the same file).
|
|
|
|
* 2 if source is identical to destination.
|
2012-07-16 23:23:33 +00:00
|
|
|
* 1 if rebase was successful
|
2012-06-22 15:38:49 +00:00
|
|
|
* -------------------------------------------------------------
|
|
|
|
* Hint: Trailing slash in dest_dir is optional.
|
2012-03-03 20:19:11 +00:00
|
|
|
*
|
|
|
|
* Logic:
|
|
|
|
*
|
2012-06-22 15:38:49 +00:00
|
|
|
* - if an image is "below" current .blend file directory:
|
|
|
|
* rebuild the same dir structure in dest_dir
|
2012-03-03 20:19:11 +00:00
|
|
|
*
|
2012-06-22 15:38:49 +00:00
|
|
|
* Example:
|
|
|
|
* src : //textures/foo/bar.png
|
|
|
|
* dest: [dest_dir]/textures/foo/bar.png.
|
2012-03-03 20:19:11 +00:00
|
|
|
*
|
|
|
|
* - if an image is not "below" current .blend file directory,
|
2012-06-22 15:38:49 +00:00
|
|
|
* disregard it's path and copy it into the destination
|
|
|
|
* directory.
|
2012-03-03 20:19:11 +00:00
|
|
|
*
|
2012-06-22 15:38:49 +00:00
|
|
|
* Example:
|
|
|
|
* src : //../foo/bar.png becomes
|
|
|
|
* dest: [dest_dir]/bar.png.
|
2012-03-03 20:19:11 +00:00
|
|
|
*
|
2012-06-22 15:38:49 +00:00
|
|
|
* This logic ensures that all image paths are relative and
|
2012-03-03 20:19:11 +00:00
|
|
|
* that a user gets his images in one place. It'll also provide
|
|
|
|
* consistent behavior across exporters.
|
2012-06-22 15:38:49 +00:00
|
|
|
* IMPORTANT NOTE: If base_dir contains an empty string, then
|
|
|
|
* this function returns wrong results!
|
|
|
|
* XXX: test on empty base_dir and return an error ?
|
2010-02-26 11:50:59 +00:00
|
|
|
*/
|
2013-03-05 03:17:46 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* \param abs Optional string to return new full path
|
|
|
|
* \param abs_len Size of *abs string
|
|
|
|
* \param rel Optional area to return new path relative to parent directory of .blend file
|
|
|
|
* (only meaningful if item is in a subdirectory thereof)
|
|
|
|
* \param rel_len Size of *rel area
|
|
|
|
* \param base_dir Path of .blend file
|
|
|
|
* \param src_dir Original path of item (any initial "//" will be expanded to
|
|
|
|
* parent directory of .blend file)
|
|
|
|
* \param dest_dir New directory into which item will be moved
|
|
|
|
* \return bli_rebase_state
|
|
|
|
*
|
|
|
|
* \note Not actually used anywhere!
|
|
|
|
*/
|
|
|
|
int BLI_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)
|
2010-02-26 11:50:59 +00:00
|
|
|
{
|
2013-03-05 03:17:46 +00:00
|
|
|
char path[FILE_MAX]; /* original full path of item */
|
|
|
|
char dir[FILE_MAX]; /* directory part of src_dir */
|
|
|
|
char base[FILE_MAX]; /* basename part of src_dir */
|
2012-05-12 15:13:06 +00:00
|
|
|
char blend_dir[FILE_MAX]; /* directory, where current .blend file resides */
|
2010-02-26 11:50:59 +00:00
|
|
|
char dest_path[FILE_MAX];
|
|
|
|
char rel_dir[FILE_MAX];
|
|
|
|
int len;
|
|
|
|
|
|
|
|
if (abs)
|
2012-05-12 15:13:06 +00:00
|
|
|
abs[0] = 0;
|
2010-02-26 11:50:59 +00:00
|
|
|
|
|
|
|
if (rel)
|
2012-05-12 15:13:06 +00:00
|
|
|
rel[0] = 0;
|
2010-02-26 11:50:59 +00:00
|
|
|
|
2011-10-21 02:13:36 +00:00
|
|
|
BLI_split_dir_part(base_dir, blend_dir, sizeof(blend_dir));
|
2010-02-26 11:50:59 +00:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
if (src_dir[0] == '\0')
|
2012-06-22 15:38:49 +00:00
|
|
|
return BLI_REBASE_NO_SRCDIR;
|
2010-02-26 11:50:59 +00:00
|
|
|
|
|
|
|
BLI_strncpy(path, src_dir, sizeof(path));
|
|
|
|
|
|
|
|
/* expand "//" in filename and get absolute path */
|
2010-03-09 17:36:23 +00:00
|
|
|
BLI_path_abs(path, base_dir);
|
2010-02-26 11:50:59 +00:00
|
|
|
|
|
|
|
/* get the directory part */
|
2011-10-15 03:56:05 +00:00
|
|
|
BLI_split_dirfile(path, dir, base, sizeof(dir), sizeof(base));
|
2010-02-26 11:50:59 +00:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
len = strlen(blend_dir);
|
2010-02-26 11:50:59 +00:00
|
|
|
|
|
|
|
rel_dir[0] = 0;
|
|
|
|
|
|
|
|
/* if image is "below" current .blend file directory */
|
2012-12-25 14:09:51 +00:00
|
|
|
if (!BLI_path_ncmp(path, blend_dir, len)) {
|
2010-02-26 11:50:59 +00:00
|
|
|
|
2011-04-06 06:03:48 +00:00
|
|
|
if (BLI_path_cmp(dir, blend_dir) == 0) {
|
2013-03-05 03:17:46 +00:00
|
|
|
/* image is directly in .blend file parent directory => put directly in dest_dir */
|
2011-02-13 03:21:27 +00:00
|
|
|
BLI_join_dirfile(dest_path, sizeof(dest_path), dest_dir, base);
|
2010-02-26 11:50:59 +00:00
|
|
|
}
|
|
|
|
else {
|
2013-03-05 03:17:46 +00:00
|
|
|
/* "below" (in subdirectory of .blend file parent directory) => put in same relative directory structure in dest_dir */
|
2010-02-26 11:50:59 +00:00
|
|
|
/* rel = image_path_dir - blend_dir */
|
|
|
|
BLI_strncpy(rel_dir, dir + len, sizeof(rel_dir));
|
2013-03-05 03:17:46 +00:00
|
|
|
/* subdirectories relative to blend_dir */
|
2011-02-13 03:21:27 +00:00
|
|
|
BLI_join_dirfile(dest_path, sizeof(dest_path), dest_dir, rel_dir);
|
2013-03-05 03:17:46 +00:00
|
|
|
/* same subdirectories relative to dest_dir */
|
2011-02-13 03:21:27 +00:00
|
|
|
BLI_join_dirfile(dest_path, sizeof(dest_path), dest_path, base);
|
2013-03-05 03:17:46 +00:00
|
|
|
/* keeping original item basename */
|
2010-02-26 11:50:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2013-03-05 03:17:46 +00:00
|
|
|
/* image is out of current directory -- just put straight in dest_dir */
|
2010-02-26 11:50:59 +00:00
|
|
|
else {
|
2011-02-13 03:21:27 +00:00
|
|
|
BLI_join_dirfile(dest_path, sizeof(dest_path), dest_dir, base);
|
2010-02-26 11:50:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (abs)
|
2011-05-02 10:22:49 +00:00
|
|
|
BLI_strncpy(abs, dest_path, abs_len);
|
2010-02-26 11:50:59 +00:00
|
|
|
|
|
|
|
if (rel) {
|
2011-05-02 10:22:49 +00:00
|
|
|
strncat(rel, rel_dir, rel_len);
|
2013-03-05 03:17:46 +00:00
|
|
|
strncat(rel, base, rel_len); /* FIXME: could overflow rel area! */
|
2010-02-26 11:50:59 +00:00
|
|
|
}
|
|
|
|
|
2012-07-01 09:54:44 +00:00
|
|
|
/* return 2 if (src == dest) */
|
2011-04-06 06:03:48 +00:00
|
|
|
if (BLI_path_cmp(path, dest_path) == 0) {
|
2012-03-31 00:59:17 +00:00
|
|
|
// if (G.debug & G_DEBUG) printf("%s and %s are the same file\n", path, dest_path);
|
2012-06-22 15:38:49 +00:00
|
|
|
return BLI_REBASE_IDENTITY;
|
2010-02-26 11:50:59 +00:00
|
|
|
}
|
|
|
|
|
2012-06-22 15:38:49 +00:00
|
|
|
return BLI_REBASE_OK;
|
2010-02-26 11:50:59 +00:00
|
|
|
}
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
|
|
|
* Returns pointer to the leftmost path separator in string. Not actually used anywhere.
|
|
|
|
*/
|
|
|
|
const char *BLI_first_slash(const char *string)
|
2011-09-28 05:53:40 +00:00
|
|
|
{
|
2013-03-04 19:27:51 +00:00
|
|
|
char * const ffslash = strchr(string, '/');
|
|
|
|
char * const fbslash = strchr(string, '\\');
|
2010-10-27 06:41:48 +00:00
|
|
|
|
|
|
|
if (!ffslash) return fbslash;
|
|
|
|
else if (!fbslash) return ffslash;
|
|
|
|
|
|
|
|
if ((intptr_t)ffslash < (intptr_t)fbslash) return ffslash;
|
|
|
|
else return fbslash;
|
|
|
|
}
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
|
|
|
* Returns pointer to the rightmost path separator in string.
|
|
|
|
*/
|
|
|
|
const char *BLI_last_slash(const char *string)
|
2011-09-28 05:53:40 +00:00
|
|
|
{
|
2013-03-04 19:27:51 +00:00
|
|
|
const char * const lfslash = strrchr(string, '/');
|
|
|
|
const char * const lbslash = strrchr(string, '\\');
|
2010-10-27 06:41:48 +00:00
|
|
|
|
|
|
|
if (!lfslash) return lbslash;
|
|
|
|
else if (!lbslash) return lfslash;
|
|
|
|
|
|
|
|
if ((intptr_t)lfslash < (intptr_t)lbslash) return lbslash;
|
|
|
|
else return lfslash;
|
|
|
|
}
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
|
|
|
* Appends a slash to string if there isn't one there already.
|
|
|
|
* Returns the new length of the string.
|
|
|
|
*/
|
2011-09-28 05:53:40 +00:00
|
|
|
int BLI_add_slash(char *string)
|
|
|
|
{
|
2010-10-27 06:41:48 +00:00
|
|
|
int len = strlen(string);
|
2012-05-12 15:13:06 +00:00
|
|
|
if (len == 0 || string[len - 1] != SEP) {
|
2011-10-11 05:21:24 +00:00
|
|
|
string[len] = SEP;
|
2012-05-12 15:13:06 +00:00
|
|
|
string[len + 1] = '\0';
|
|
|
|
return len + 1;
|
2010-10-27 06:41:48 +00:00
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
|
|
|
* Removes the last slash and everything after it to the end of string, if there is one.
|
|
|
|
*/
|
2011-09-28 05:53:40 +00:00
|
|
|
void BLI_del_slash(char *string)
|
|
|
|
{
|
2010-10-27 06:41:48 +00:00
|
|
|
int len = strlen(string);
|
|
|
|
while (len) {
|
2012-05-12 15:13:06 +00:00
|
|
|
if (string[len - 1] == SEP) {
|
|
|
|
string[len - 1] = '\0';
|
2010-10-27 06:41:48 +00:00
|
|
|
len--;
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2010-10-27 06:41:48 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-02-26 11:50:59 +00:00
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Tries appending each of the semicolon-separated extensions in the PATHEXT
|
|
|
|
* environment variable (Windows-only) onto *name in turn until such a file is found.
|
|
|
|
* Returns success/failure.
|
|
|
|
*/
|
2002-10-12 11:37:38 +00:00
|
|
|
static int add_win32_extension(char *name)
|
|
|
|
{
|
|
|
|
int retval = 0;
|
|
|
|
int type;
|
|
|
|
|
2011-10-21 22:33:41 +00:00
|
|
|
type = BLI_exists(name);
|
2002-10-12 11:37:38 +00:00
|
|
|
if ((type == 0) || S_ISDIR(type)) {
|
|
|
|
#ifdef _WIN32
|
2011-11-26 04:07:38 +00:00
|
|
|
char filename[FILE_MAX];
|
|
|
|
char ext[FILE_MAX];
|
2011-02-26 15:28:56 +00:00
|
|
|
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);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2002-10-12 11:37:38 +00:00
|
|
|
strcat(filename, extensions);
|
|
|
|
}
|
|
|
|
|
2011-10-21 22:33:41 +00:00
|
|
|
type = BLI_exists(filename);
|
2012-05-12 15:13:06 +00:00
|
|
|
if (type && (!S_ISDIR(type))) {
|
2002-10-12 11:37:38 +00:00
|
|
|
retval = 1;
|
|
|
|
strcpy(name, filename);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (temp);
|
|
|
|
}
|
|
|
|
#endif
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2002-10-12 11:37:38 +00:00
|
|
|
retval = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return (retval);
|
|
|
|
}
|
|
|
|
|
2012-03-03 20:19:11 +00:00
|
|
|
/**
|
|
|
|
* 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
|
|
|
|
*/
|
2011-10-21 17:37:38 +00:00
|
|
|
static void bli_where_am_i(char *fullname, const size_t maxlen, const char *name)
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
2011-11-26 04:07:38 +00:00
|
|
|
char filename[FILE_MAX];
|
2011-02-26 15:28:56 +00:00
|
|
|
const char *path = NULL, *temp;
|
2011-02-13 03:21:27 +00:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
#ifdef _WIN32
|
2010-12-03 17:05:21 +00:00
|
|
|
const char *separator = ";";
|
2002-10-12 11:37:38 +00:00
|
|
|
#else
|
2010-12-03 17:05:21 +00:00
|
|
|
const char *separator = ":";
|
2002-10-12 11:37:38 +00:00
|
|
|
#endif
|
|
|
|
|
2008-01-18 15:10:17 +00:00
|
|
|
|
2011-02-21 13:13:08 +00:00
|
|
|
#ifdef WITH_BINRELOC
|
2011-10-17 06:39:13 +00:00
|
|
|
/* linux uses binreloc since argv[0] is not reliable, call br_init( NULL ) first */
|
2012-04-29 17:11:40 +00:00
|
|
|
path = br_find_exe(NULL);
|
2008-01-18 15:10:17 +00:00
|
|
|
if (path) {
|
2011-02-13 03:21:27 +00:00
|
|
|
BLI_strncpy(fullname, path, maxlen);
|
2011-02-26 15:28:56 +00:00
|
|
|
free((void *)path);
|
2008-01-18 15:10:17 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
2009-11-08 21:33:43 +00:00
|
|
|
|
|
|
|
#ifdef _WIN32
|
2012-05-12 15:13:06 +00:00
|
|
|
wchar_t *fullname_16 = MEM_mallocN(maxlen * sizeof(wchar_t), "ProgramPath");
|
2012-03-24 06:18:31 +00:00
|
|
|
if (GetModuleFileNameW(0, fullname_16, maxlen)) {
|
2012-04-29 15:47:02 +00:00
|
|
|
conv_utf_16_to_8(fullname_16, fullname, maxlen);
|
2012-03-24 06:18:31 +00:00
|
|
|
if (!BLI_exists(fullname)) {
|
2011-05-06 01:03:36 +00:00
|
|
|
printf("path can't be found: \"%.*s\"\n", maxlen, fullname);
|
2011-07-20 06:05:47 +00:00
|
|
|
MessageBox(NULL, "path contains invalid characters or is too long (see console)", "Error", MB_OK);
|
2011-05-06 01:03:36 +00:00
|
|
|
}
|
2012-03-20 02:17:37 +00:00
|
|
|
MEM_freeN(fullname_16);
|
2009-11-08 21:33:43 +00:00
|
|
|
return;
|
|
|
|
}
|
2012-03-20 02:17:37 +00:00
|
|
|
|
|
|
|
MEM_freeN(fullname_16);
|
2009-11-08 21:33:43 +00:00
|
|
|
#endif
|
|
|
|
|
2008-01-18 15:10:17 +00:00
|
|
|
/* unix and non linux */
|
2011-02-13 03:21:27 +00:00
|
|
|
if (name && name[0]) {
|
2012-03-20 02:17:37 +00:00
|
|
|
|
2011-02-13 03:21:27 +00:00
|
|
|
BLI_strncpy(fullname, name, maxlen);
|
2002-10-12 11:37:38 +00:00
|
|
|
if (name[0] == '.') {
|
2012-05-12 15:13:06 +00:00
|
|
|
char wdir[FILE_MAX] = "";
|
|
|
|
BLI_current_working_dir(wdir, sizeof(wdir)); /* backup cwd to restore after */
|
2011-02-13 03:21:27 +00:00
|
|
|
|
2009-07-19 17:45:14 +00:00
|
|
|
// not needed but avoids annoying /./ in name
|
2012-05-12 15:13:06 +00:00
|
|
|
if (name[1] == SEP)
|
|
|
|
BLI_join_dirfile(fullname, maxlen, wdir, name + 2);
|
2009-07-19 17:45:14 +00:00
|
|
|
else
|
2011-02-13 03:21:27 +00:00
|
|
|
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
|
2011-02-13 03:21:27 +00:00
|
|
|
BLI_strncpy(fullname, name, maxlen);
|
2002-10-12 11:37:38 +00:00
|
|
|
add_win32_extension(fullname);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2002-10-12 11:37:38 +00:00
|
|
|
// search for binary in $PATH
|
|
|
|
path = getenv("PATH");
|
|
|
|
if (path) {
|
|
|
|
do {
|
2009-10-19 18:44:09 +00:00
|
|
|
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;
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2002-10-12 11:37:38 +00:00
|
|
|
strncpy(filename, path, sizeof(filename));
|
|
|
|
}
|
2011-02-13 03:21:27 +00:00
|
|
|
BLI_join_dirfile(fullname, maxlen, fullname, name);
|
2002-10-12 11:37:38 +00:00
|
|
|
if (add_win32_extension(filename)) {
|
2011-02-13 03:21:27 +00:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-21 17:37:38 +00:00
|
|
|
void BLI_init_program_path(const char *argv0)
|
|
|
|
{
|
|
|
|
bli_where_am_i(bprogname, sizeof(bprogname), argv0);
|
|
|
|
BLI_split_dir_part(bprogname, bprogdir, sizeof(bprogdir));
|
|
|
|
}
|
|
|
|
|
2013-03-24 01:51:54 +00:00
|
|
|
/**
|
|
|
|
* Path to executable
|
|
|
|
*/
|
2011-10-21 17:37:38 +00:00
|
|
|
const char *BLI_program_path(void)
|
|
|
|
{
|
|
|
|
return bprogname;
|
|
|
|
}
|
|
|
|
|
2013-03-24 01:51:54 +00:00
|
|
|
/**
|
|
|
|
* Path to directory of executable
|
|
|
|
*/
|
2011-10-21 17:37:38 +00:00
|
|
|
const char *BLI_program_dir(void)
|
|
|
|
{
|
|
|
|
return bprogdir;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2012-03-03 20:19:11 +00:00
|
|
|
* 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 maxlen The size of the fullname buffer
|
|
|
|
* \param userdir Directory specified in user preferences
|
|
|
|
*/
|
2011-11-14 16:05:44 +00:00
|
|
|
static void BLI_where_is_temp(char *fullname, const size_t maxlen, char *userdir)
|
2008-02-13 13:55:22 +00:00
|
|
|
{
|
|
|
|
fullname[0] = '\0';
|
|
|
|
|
2011-10-21 17:37:38 +00:00
|
|
|
if (userdir && BLI_is_dir(userdir)) {
|
|
|
|
BLI_strncpy(fullname, userdir, maxlen);
|
2008-02-13 13:55:22 +00:00
|
|
|
}
|
|
|
|
|
2008-02-21 08:43:13 +00:00
|
|
|
|
2008-02-20 20:07:37 +00:00
|
|
|
#ifdef WIN32
|
2008-02-21 08:43:13 +00:00
|
|
|
if (fullname[0] == '\0') {
|
2011-02-26 15:28:56 +00:00
|
|
|
const char *tmp = getenv("TEMP"); /* Windows */
|
2010-07-14 10:44:34 +00:00
|
|
|
if (tmp && BLI_is_dir(tmp)) {
|
2011-02-13 06:19:15 +00:00
|
|
|
BLI_strncpy(fullname, tmp, maxlen);
|
2008-02-20 20:07:37 +00:00
|
|
|
}
|
2008-02-21 08:43:13 +00:00
|
|
|
}
|
2008-02-20 20:07:37 +00:00
|
|
|
#else
|
2008-02-21 08:43:13 +00:00
|
|
|
/* Other OS's - Try TMP and TMPDIR */
|
|
|
|
if (fullname[0] == '\0') {
|
2011-02-26 15:28:56 +00:00
|
|
|
const char *tmp = getenv("TMP");
|
2010-07-14 10:44:34 +00:00
|
|
|
if (tmp && BLI_is_dir(tmp)) {
|
2011-02-13 03:21:27 +00:00
|
|
|
BLI_strncpy(fullname, tmp, maxlen);
|
2008-02-13 13:55:22 +00:00
|
|
|
}
|
2008-02-21 08:43:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (fullname[0] == '\0') {
|
2011-02-26 15:28:56 +00:00
|
|
|
const char *tmp = getenv("TMPDIR");
|
2010-07-14 10:44:34 +00:00
|
|
|
if (tmp && BLI_is_dir(tmp)) {
|
2011-02-13 03:21:27 +00:00
|
|
|
BLI_strncpy(fullname, tmp, maxlen);
|
2008-02-20 20:07:37 +00:00
|
|
|
}
|
2008-02-13 13:55:22 +00:00
|
|
|
}
|
2012-05-19 13:55:54 +00:00
|
|
|
#endif
|
2008-02-13 13:55:22 +00:00
|
|
|
|
|
|
|
if (fullname[0] == '\0') {
|
2011-02-13 03:21:27 +00:00
|
|
|
BLI_strncpy(fullname, "/tmp/", maxlen);
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
|
|
|
else {
|
2008-02-13 13:55:22 +00:00
|
|
|
/* add a trailing slash if needed */
|
|
|
|
BLI_add_slash(fullname);
|
2010-09-28 00:14:02 +00:00
|
|
|
#ifdef WIN32
|
2012-03-24 06:18:31 +00:00
|
|
|
if (userdir && userdir != fullname) {
|
2011-10-21 17:37:38 +00:00
|
|
|
BLI_strncpy(userdir, fullname, maxlen); /* also set user pref to show %TEMP%. /tmp/ is just plain confusing for Windows users. */
|
2011-02-13 03:21:27 +00:00
|
|
|
}
|
2010-09-28 00:14:02 +00:00
|
|
|
#endif
|
2008-02-13 13:55:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Sets btempdir to userdir if specified and is a valid directory, otherwise
|
|
|
|
* chooses a suitable OS-specific temporary directory.
|
|
|
|
*/
|
2011-10-21 17:37:38 +00:00
|
|
|
void BLI_init_temporary_dir(char *userdir)
|
|
|
|
{
|
|
|
|
BLI_where_is_temp(btempdir, FILE_MAX, userdir);
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
2013-03-24 01:51:54 +00:00
|
|
|
* Path to temporary directory (with trailing slash)
|
2013-03-05 03:17:46 +00:00
|
|
|
*/
|
2011-10-21 17:37:38 +00:00
|
|
|
const char *BLI_temporary_dir(void)
|
|
|
|
{
|
|
|
|
return btempdir;
|
|
|
|
}
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
2013-03-24 01:51:54 +00:00
|
|
|
* Path to the system temporary directory (with trailing slash)
|
2013-03-05 03:17:46 +00:00
|
|
|
*/
|
2011-10-21 17:37:38 +00:00
|
|
|
void BLI_system_temporary_dir(char *dir)
|
|
|
|
{
|
|
|
|
BLI_where_is_temp(dir, FILE_MAX, NULL);
|
|
|
|
}
|
|
|
|
|
2007-09-02 17:25:03 +00:00
|
|
|
#ifdef WITH_ICONV
|
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/**
|
|
|
|
* Converts a string encoded in the charset named by *code to UTF-8.
|
|
|
|
* Opens a new iconv context each time it is run, which is probably not the
|
|
|
|
* most efficient. */
|
2008-09-20 11:08:35 +00:00
|
|
|
void BLI_string_to_utf8(char *original, char *utf_8, const char *code)
|
2007-09-02 17:25:03 +00:00
|
|
|
{
|
2012-05-12 15:13:06 +00:00
|
|
|
size_t inbytesleft = strlen(original);
|
|
|
|
size_t outbytesleft = 512;
|
|
|
|
size_t rv = 0;
|
2007-09-02 17:25:03 +00:00
|
|
|
iconv_t cd;
|
|
|
|
|
|
|
|
if (NULL == code) {
|
|
|
|
code = locale_charset();
|
|
|
|
}
|
2012-05-12 15:13:06 +00:00
|
|
|
cd = iconv_open("UTF-8", code);
|
2007-09-02 17:25:03 +00:00
|
|
|
|
|
|
|
if (cd == (iconv_t)(-1)) {
|
|
|
|
printf("iconv_open Error");
|
2012-05-12 15:13:06 +00:00
|
|
|
*utf_8 = '\0';
|
2012-02-27 10:35:39 +00:00
|
|
|
return;
|
2007-09-02 17:25:03 +00:00
|
|
|
}
|
2012-05-12 15:13:06 +00:00
|
|
|
rv = iconv(cd, &original, &inbytesleft, &utf_8, &outbytesleft);
|
2007-09-02 17:25:03 +00:00
|
|
|
if (rv == (size_t) -1) {
|
|
|
|
printf("iconv Error\n");
|
2013-03-05 03:17:46 +00:00
|
|
|
iconv_close(cd);
|
2012-02-27 10:35:39 +00:00
|
|
|
return;
|
2007-09-02 17:25:03 +00:00
|
|
|
}
|
|
|
|
*utf_8 = '\0';
|
|
|
|
iconv_close(cd);
|
|
|
|
}
|
|
|
|
#endif // WITH_ICONV
|