diff --git a/release/environment-macosx b/release/environment-macosx new file mode 100644 index 00000000000..d41d5ec485f --- /dev/null +++ b/release/environment-macosx @@ -0,0 +1,18 @@ +# This is a Blender Environment Variable config file. +# +# Comment lines start with "#", other lines will be split at the "=" +# and the part before will be used as env var name and the part after +# as env var value. The value can make reference to previous or +# prelaunch variables with "${}" and the content will be replaced. +# Once set, values of variables will not be overwritten. +# +# BLENDER_SHARE should be /Library/Application Support/Blender for typical installs. +# BLENDER_VERSION will be set by the program before processing this file. +BLENDER_USER_BASE=${HOME}/Library/Application Support/Blender/${BLENDER_VERSION} +BLENDER_SYSTEM_BASE=${BLENDER_SHARE}/${BLENDER_VERSION} +BLENDER_USER_DATAFILES=${HOME}/Library/Application Support/Blender/${BLENDER_VERSION}/datafiles +BLENDER_SYSTEM_DATAFILES=${BLENDER_SHARE}/${BLENDER_VERSION}/datafiles +BLENDER_USER_PY=${HOME}/Library/Application Support/Blender/${BLENDER_VERSION}/py +BLENDER_SYSTEM_PY=${BLENDER_SHARE}/${BLENDER_VERSION}/py +BLENDER_USER_PLUGINS=${HOME}/Library/Application Support/Blender/${BLENDER_VERSION}/plugins +BLENDER_SYSTEM_PLUGINS=${BLENDER_SHARE}/${BLENDER_VERSION}/plugins diff --git a/release/environment-mswindows b/release/environment-mswindows new file mode 100644 index 00000000000..f8890f89af8 --- /dev/null +++ b/release/environment-mswindows @@ -0,0 +1,18 @@ +# This is a Blender Environment Variable config file. +# +# Comment lines start with "#", other lines will be split at the "=" +# and the part before will be used as env var name and the part after +# as env var value. The value can make reference to previous or +# prelaunch variables with "%%" and the content will be replaced. +# Once set, values of variables will not be overwritten. +# +# BLENDER_SHARE should be COMMON_APPDATA\\Blender Foundation\\Blender for typical installs. +# BLENDER_VERSION will be set by the program before processing this file. +BLENDER_USER_BASE=%USERPROFILE%\\Blender Foundation\\Blender\\%BLENDER_VERSION% +BLENDER_SYSTEM_BASE=%BLENDER_SHARE%\\%BLENDER_VERSION% +BLENDER_USER_DATAFILES=%USERPROFILE%\\Blender Foundation\\%BLENDER_VERSION%\\datafiles +BLENDER_SYSTEM_DATAFILES=%BLENDER_SHARE%\\%BLENDER_VERSION%\\datafiles +BLENDER_USER_PY=%USERPROFILE%\\Blender Foundation\\%BLENDER_VERSION%\\py +BLENDER_SYSTEM_PY=%BLENDER_SHARE%\\%BLENDER_VERSION%\\py +BLENDER_USER_PLUGINS=%USERPROFILE%\\Blender Foundation\\%BLENDER_VERSION%\\plugins +BLENDER_SYSTEM_PLUGINS=%BLENDER_SHARE%\\%BLENDER_VERSION%\\plugins diff --git a/release/environment-unix b/release/environment-unix new file mode 100644 index 00000000000..8a13c288306 --- /dev/null +++ b/release/environment-unix @@ -0,0 +1,18 @@ +# This is a Blender Environment Variable config file. +# +# Comment lines start with "#", other lines will be split at the "=" +# and the part before will be used as env var name and the part after +# as env var value. The value can make reference to previous or +# prelaunch variables with "${}" and the content will be replaced. +# Once set, values of variables will not be overwritten. +# +# BLENDER_SHARE should be /usr/share/blender for typical distro installs. +# BLENDER_VERSION will be set by the program before processing this file. +BLENDER_USER_BASE=${HOME}/.blender/${BLENDER_VERSION} +BLENDER_SYSTEM_BASE=${BLENDER_SHARE}/${BLENDER_VERSION} +BLENDER_USER_DATAFILES=${HOME}/.blender/${BLENDER_VERSION}/datafiles +BLENDER_SYSTEM_DATAFILES=${BLENDER_SHARE}/${BLENDER_VERSION}/datafiles +BLENDER_USER_PY=${HOME}/.blender/${BLENDER_VERSION}/py +BLENDER_SYSTEM_PY=${BLENDER_SHARE}/${BLENDER_VERSION}/py +BLENDER_USER_PLUGINS=${HOME}/.blender/${BLENDER_VERSION}/plugins +BLENDER_SYSTEM_PLUGINS=${BLENDER_SHARE}/${BLENDER_VERSION}/plugins diff --git a/source/blender/blenlib/BLI_bfile.h b/source/blender/blenlib/BLI_bfile.h index 92543558a19..15374b4d445 100644 --- a/source/blender/blenlib/BLI_bfile.h +++ b/source/blender/blenlib/BLI_bfile.h @@ -38,9 +38,9 @@ #define BFILE_NORMAL (0) /* No supervision, just translate // if needed, RISKY */ #define BFILE_RAW (1<<0) -/* Path is relative to config dirs */ +/* Path is based in env vars specified by "envvars" */ #define BFILE_CONFIG (1<<1) -/* Path is for current session temp file */ +/* Path is for current session temp files */ #define BFILE_TEMP (1<<2) /* Config handling, special cases: */ @@ -50,6 +50,17 @@ /* Compression to apply on close: */ #define BFILE_GZIP (1<<5) +/** + For the envvars param. + */ +typedef enum BEnvVarFamilies { + BENV_NONE, + BENV_BASE, + BENV_DATAFILES, + BENV_PYTHON, + BENV_PLUGINS +} BEnvVarFam; + /** File descriptor for Blender abstracted file access. */ @@ -58,22 +69,23 @@ typedef struct { int fd; /* Anything below should not be touched directly */ - int uflags; /* Special options requested by upper level, copy of bflags */ - char *fpath; /* Final/requested path name */ - char *tpath; /* Temp path name if applicable */ - int type; /* Own flags, common classification of open and fopen */ - int error; /* An op caused an error, unsafe to replace older files */ + int uflags; /* Special options requested by upper level, copy of bflags */ + BEnvVarFam evars; /* What kind of file, describe the env vars to use */ + char *fpath; /* Final/requested path name */ + char *tpath; /* Temp path name if applicable */ + int classf; /* Own flags, common classification of open and fopen */ + int error; /* An op caused an error, unsafe to replace older files */ } BFILE; /** Open a BFILE* with fopen()-like syntax. */ -BFILE *BLI_bfile_fopen(const char *path, const char *mode, int bflags); +BFILE *BLI_bfile_fopen(const char *path, const char *mode, int bflags, BEnvVarFam envvars); /** Open a BFILE* with open()-like syntax. */ -BFILE *BLI_bfile_open(const char *pathname, int flags, int bflags); +BFILE *BLI_bfile_open(const char *pathname, int flags, int bflags, BEnvVarFam envvars); /** Get the FILE* associated with the BFILE*. diff --git a/source/blender/blenlib/BLI_util.h b/source/blender/blenlib/BLI_util.h index 1ce7a8cdb77..7642b71eea0 100644 --- a/source/blender/blenlib/BLI_util.h +++ b/source/blender/blenlib/BLI_util.h @@ -51,6 +51,7 @@ char *BLI_gethome_folder(char *folder_name, int flag); #define BLI_GETHOME_ALL (BLI_GETHOME_SYSTEM|BLI_GETHOME_LOCAL|BLI_GETHOME_USER) void BLI_setenv(const char *env, const char *val); +void BLI_setenv_if_new(const char *env, const char* val); void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file); void BLI_make_exist(char *dir); diff --git a/source/blender/blenlib/intern/BLI_bfile.c b/source/blender/blenlib/intern/BLI_bfile.c index a7ce1df5712..4de1e6ce4a1 100644 --- a/source/blender/blenlib/intern/BLI_bfile.c +++ b/source/blender/blenlib/intern/BLI_bfile.c @@ -25,89 +25,70 @@ */ #include - +#include #ifndef WIN32 #include #else #include #include "BLI_winstuff.h" #endif - +#include #include #include #include +#include #include "MEM_guardedalloc.h" - +#include "BKE_utildefines.h" +#include "BKE_blender.h" +#include "BLI_util.h" +#include "BLI_fileops.h" +#include "BLI_storage.h" #include "BLI_bfile.h" -// This would provide config paths and their oldest viable version -// so if there is an uncompatible change, user's old versions are not loaded -//#include "bfile_tables.h" +/* Internal bfile classification flags */ +#define BCF_OPEN (0) +#define BCF_FOPEN (1<<0) +#define BCF_READ (1<<1) +#define BCF_WRITE (1<<2) +#define BCF_AT_END (1<<3) +#define BCF_DISCARD (1<<4) -/* Internal bfile type flags */ -#define BTF_OPEN (0) -#define BTF_FOPEN (1<<0) -#define BTF_READ (1<<1) -#define BTF_WRITE (1<<2) -#define BTF_AT_END (1<<3) -#define BTF_DISCARD (1<<4) +/* Declaration of internal functions */ +void chomp(char* line); +void expand_envvars(char* src, char* dst); +void fill_paths(BFILE *bfile, const char *path); +char* find_in_pathlist(char* filename, char* pathlist); +void init_vars_from_file(const char* path); +void setup_temp(); +/*** Exported functions ***/ -void fill_paths(BFILE *bfile, const char *path) { - char* source_path = NULL; - int bflags = bfile->uflags; - - if(bflags & BFILE_NORMAL || bflags & BFILE_RAW) { -// bfile->fpath is path with // replaced - } - if(bflags & BFILE_TEMP) { -// bfile->fpath is tempdir+path - } - if(bflags & BFILE_CONFIG) { -// bfile->fpath is userdir+version+path -// source_path is first hit in (if using fallback to older versions) -// userdir+curversion+path (... userdir+limitversion+path) sysdir+path -// (limitversion is based in path, using some kind of regex or "tables") - } - - if(bfile->type & BTF_WRITE && !(bflags & BFILE_RAW)) { - /* Generate temp path */ - // bfile->tpath is fpath+randstring - if(!(bfile->type & BTF_DISCARD)) { - /* Copy data to tpath */ - if(source_path) { - // copy it from older version or sys version - } - } - } else { - bfile->tpath = bfile->fpath; - } -} - -BFILE *BLI_bfile_fopen(const char *path, const char *mode, int bflags) { +BFILE *BLI_bfile_fopen(const char *path, const char *mode, int bflags, + BEnvVarFam envvars) +{ BFILE *bfile; bfile = MEM_mallocN(sizeof(BFILE), "bfile-fopen"); - bfile->type = BTF_FOPEN; + bfile->classf = BCF_FOPEN; bfile->uflags = bflags; /* From fopen() doc, we can guess some logic: - r BTF_READ - r+ BTF_READ | BTF_WRITE - w BTF_DISCARD | BTF_WRITE - w+ BTF_DISCARD | BTF_WRITE | BTF_READ - a BTF_AT_END | BTF_WRITE - a+ BTF_AT_END | BTF_WRITE | BTF_READ + r BCF_READ + r+ BCF_READ | BCF_WRITE + w BCF_DISCARD | BCF_WRITE + w+ BCF_DISCARD | BCF_WRITE | BCF_READ + a BCF_AT_END | BCF_WRITE + a+ BCF_AT_END | BCF_WRITE | BCF_READ */ if(strchr(mode, 'r')) - bfile->type |= BTF_READ; + bfile->classf |= BCF_READ; if(strchr(mode, 'w')) - bfile->type |= (BTF_DISCARD | BTF_WRITE); + bfile->classf |= (BCF_DISCARD | BCF_WRITE); if(strchr(mode, 'a')) - bfile->type |= (BTF_AT_END | BTF_WRITE); + bfile->classf |= (BCF_AT_END | BCF_WRITE); if(strchr(mode, '+')) - bfile->type |= (BTF_READ | BTF_WRITE); + bfile->classf |= (BCF_READ | BCF_WRITE); fill_paths(bfile, path); @@ -118,24 +99,26 @@ BFILE *BLI_bfile_fopen(const char *path, const char *mode, int bflags) { } -BFILE *BLI_bfile_open(const char *pathname, int flags, int bflags) { +BFILE *BLI_bfile_open(const char *pathname, int flags, int bflags, + BEnvVarFam envvars) +{ BFILE *bfile; bfile = MEM_mallocN(sizeof(BFILE), "bfile-open"); - bfile->type = BTF_OPEN; + bfile->classf = BCF_OPEN; bfile->uflags = bflags; /* Easy mapping for open() */ if(flags & O_RDONLY) - bfile->type |= BTF_READ; + bfile->classf |= BCF_READ; if(flags & O_WRONLY) - bfile->type |= BTF_WRITE; + bfile->classf |= BCF_WRITE; if(flags & O_RDWR) - bfile->type |= (BTF_READ | BTF_WRITE); + bfile->classf |= (BCF_READ | BCF_WRITE); if(flags & O_APPEND) - bfile->type |= BTF_AT_END; + bfile->classf |= BCF_AT_END; if(flags & O_TRUNC) - bfile->type |= BTF_DISCARD; + bfile->classf |= BCF_DISCARD; fill_paths(bfile, pathname); @@ -180,7 +163,9 @@ ssize_t BLI_bfile_read(BFILE *f, void *buf, size_t count) { } -size_t BLI_bfile_fwrite(const void *ptr, size_t size, size_t nmemb, BFILE *f) { +size_t BLI_bfile_fwrite(const void *ptr, size_t size, size_t nmemb, + BFILE *f) +{ size_t ret; ret = fwrite(ptr, size, nmemb, f->stream); @@ -205,7 +190,7 @@ size_t BLI_bfile_fread(void *ptr, size_t size, size_t nmemb, BFILE *f) { void BLI_bfile_close(BFILE *bfile) { - if((bfile->type | BTF_WRITE) && + if((bfile->classf | BCF_WRITE) && !(bfile->uflags | BFILE_RAW)) { /* Make sure data is on disk */ /* Move to final name if no errors */ @@ -234,3 +219,296 @@ void BLI_bfile_set_error(BFILE *bfile, int error) { bfile->error = error; } } + + +#if defined(WIN32) + #define LAST_SESSION_FILE "%HOME%\\Blender\\last-session FIXME FIXME FIXME" + #define ENVIRONMENT_FILE "FIXME" + #define SHARED_DIRECTORY "FIXME TOO" +#elif defined(OSX) + #define LAST_SESSION_FILE "${HOME}/Library/Application Support/Blender/last-session" + #define ENVIRONMENT_FILE "${HOME}/Library/Application Support/Blender/${BLENDER_VERSION}/environment" + #define SHARED_DIRECTORY "/Library/Application Support/Blender" +#else + #define LAST_SESSION_FILE "${HOME}/.blender/last-session" + #define ENVIRONMENT_FILE "${HOME}/.blender/${BLENDER_VERSION}/environment" + #define SHARED_DIRECTORY "/usr/share/blender" +#endif +void BLI_bfile_init_vars() { + char file[MAXPATHLEN]; + char temp[MAXPATHLEN]; + extern char bprogname[]; + FILE* fp; + + /* This one is unconditional */ + sprintf(temp, "%d", BLENDER_VERSION); + BLI_setenv("BLENDER_VERSION", temp); + + /* Is this unpack&run? */ + sprintf(temp, "%s/%d/environment", dirname(bprogname), BLENDER_VERSION); + if(BLI_exist(temp)) { + BLI_setenv_if_new("BLENDER_SHARE", dirname(bprogname)); + } else { + BLI_setenv_if_new("BLENDER_SHARE", SHARED_DIRECTORY); + } + + expand_envvars(LAST_SESSION_FILE, file); + fp = fopen(file, "r"); + /* 1st line, read previous version */ + if (fp && (fscanf(fp, "%3c\n", temp) == 1)) { + temp[3] = '\0'; + BLI_setenv("BLENDER_VERSION_PREV", temp); + /* 2nd line, read previous session path if needed */ + if(!getenv("BLENDER_TEMP")) { + if ((fgets(temp, MAXPATHLEN, fp) != NULL)) { + /* Clean any \n */ + chomp(temp); + /* Check the dir is still there or generate new one */ + if(!BLI_exist(temp)) { + setup_temp(); + } + } else { + /* We have to generate it for sure */ + setup_temp(); + } + } + } else { + /* Probably new user, or only <=249 before */ + BLI_setenv("BLENDER_VERSION_PREV", "0"); + setup_temp(); + } + + if(fp) { + fclose(fp); + } + + /* Load vars from user and system files */ + expand_envvars(ENVIRONMENT_FILE, file); + init_vars_from_file(file); + sprintf(temp, "/%d/environment", BLENDER_VERSION); + BLI_make_file_string("/", file, getenv("BLENDER_SHARE"), temp); + init_vars_from_file(file); +} + + +/*** Internal functions ***/ + +/** + Eliminate trailing EOL by writing a \0 over it. + Name taken from Perl. + */ +void chomp(char* line) { + int len = strlen(line); +#ifndef WIN32 + if (line[len - 1] == '\n') { + line[len - 1] = '\0'; + } +#else + if ((line[len - 2] == '\r' ) && ((line[len - 1] == '\n'))) { + line[len - 2] = '\0'; + } +#endif /* WIN32 */ +} + + +/** + Parse a file with lines like FOO=bar (comment lines have # as first + character) assigning to envvar FOO the value bar if FOO does not + exist yet. + Any white space before FOO, around the = or trailing will be used, + so beware. + */ +#define MAX_LINE 4096 +#define ENV_VAR 256 +#define VAR_LEN 8192 +void init_vars_from_file(const char* path) { + char line[MAX_LINE]; + char name[ENV_VAR]; + FILE *fp; + char* separator; + char expanded[VAR_LEN]; + + fp = fopen(path, "r"); + if (!fp) return; + + while (fgets(line, MAX_LINE, fp) != NULL) { + /* Ignore comment lines */ + if (line[0] == '#') + continue; + + /* Split into envvar name and contents */ + separator = strchr(line, '='); + if(separator && ((separator - line) < ENV_VAR)) { + /* First remove EOL */ + chomp(line); + strncpy(name, line, separator - line); + name[separator - line] = '\0'; + expand_envvars(separator + 1, expanded); + BLI_setenv_if_new(name, expanded); + } + } + fclose(fp); +} + + +/** + Look for ${} (or %%) env vars in src and expand if the var + exists (even if empty value). If not exist, the name is left as is. + The process is done all over src, and nested ${${}} is not supported. + src must be \0 terminated, and dst must be big enough. +*/ +#ifndef WIN32 + #define ENVVAR_PREFFIX "${" + #define ENVVAR_P_SIZE 2 + #define ENVVAR_SUFFIX "}" + #define ENVVAR_S_SIZE 1 +#else + #define ENVVAR_PREFFIX "%" + #define ENVVAR_P_SIZE 1 + #define ENVVAR_SUFFIX "%" + #define ENVVAR_S_SIZE 1 +#endif /* WIN32 */ +void expand_envvars(char* src, char* dst) { + char* hit1; + char* hit2; + char name[ENV_VAR]; + char* value; + int prevlen; + int done = 0; + char* source = src; + + dst[0] = '\0'; + while (!done) { + hit1 = strstr(source, ENVVAR_PREFFIX); + if (hit1) { + hit2 = strstr(hit1 + ENVVAR_P_SIZE, ENVVAR_SUFFIX); + if (hit2) { + /* "Copy" the leading part, if any */ + if (hit1 != source) { + prevlen = strlen(dst); + strncat(dst, source, hit1 - source); + dst[prevlen + (hit1 - source)] = '\0'; + } + /* Figure the name of the env var we just found */ + strncpy(name, hit1 + ENVVAR_P_SIZE, + hit2 - (hit1 + ENVVAR_P_SIZE)); + name[hit2 - (hit1 + ENVVAR_P_SIZE)] = '\0'; + /* See if we can get something with that name */ + value = getenv(name); + if (value) { + /* Push the var value */ + strcat(dst, value); + } else { + /* Leave the var name, so it is clear that it failed */ + strcat(dst, ENVVAR_PREFFIX); + strcat(dst, name); + strcat(dst, ENVVAR_SUFFIX); + } + /* Continue after closing mark, like a new string */ + source = hit2 + ENVVAR_S_SIZE; + } else { + /* Non terminated var so "copy as is" and finish */ + strcat(dst, source); + done = 1; + } + } else { + /* "Copy" whatever is left */ + strcat(dst, source); + done = 1; + } + } +} + + +/** + Return a full path if the filename exists when combined + with any item from pathlist. Or NULL otherwise. + */ +#ifdef WIN32 + #define SEPARATOR ';' +#else + #define SEPARATOR ':' +#endif +char* find_in_pathlist(char* filename, char* pathlist) { + char first[FILE_MAX + 10]; + char* rest = NULL; + + /* Separate first path from rest, use typical separator for current OS */ + rest = strchr(pathlist, SEPARATOR); + if (rest) { + strncpy(first, pathlist, rest - pathlist); + first[rest - pathlist] = '\0'; + /* Skip the separator so it becomes a valid new pathlist */ + rest++; + } else { + strcpy(first, pathlist); + } + + /* Check if combination exists */ + BLI_add_slash(first); + strcat(first, filename); + if(BLI_exist(first)) { + return strdup(first); + } + + /* First path failed, try with rest of paths if possible */ + if(rest) { + return find_in_pathlist(filename, rest); + } else { + return NULL; + } +} + + +/** + Setup fpath and tpath based in the needs of the bfile. + */ +void fill_paths(BFILE *bfile, const char *path) { + char* source_path = NULL; + int bflags = bfile->uflags; + + if(bflags & BFILE_NORMAL || bflags & BFILE_RAW) { +// bfile->fpath is path with // replaced + } + if(bflags & BFILE_TEMP) { +// bfile->fpath is tempdir+path + } + if(bflags & BFILE_CONFIG) { +// bfile->fpath is userdir+version+path +// source_path is first hit in (if using fallback to older versions) +// userdir+curversion+path (... userdir+limitversion+path) sysdir+path +// (limitversion is based in path, using some kind of regex or "tables") + } + + if(bfile->classf & BCF_WRITE && !(bflags & BFILE_RAW)) { + /* Generate temp path */ + // bfile->tpath is fpath+randstring + if(!(bfile->classf & BCF_DISCARD)) { + /* Copy data to tpath */ + if(source_path) { + // copy it from older version or sys version + } + } + } else { + bfile->tpath = bfile->fpath; + } +} + + +/** + Create a temp directory in safe and multiuser way. + */ +void setup_temp() { + char template[MAXPATHLEN]; + char* tempdir; + + if(getenv("TMPDIR")) { + sprintf(template, "%s/blender-XXXXXX", getenv("TMPDIR")); + } else { + sprintf(template, "/tmp/blender-XXXXXX"); +// MacOSX NSTemporaryDirectory and WIN32 ??? + } + tempdir = mkdtemp(template); + BLI_setenv("BLENDER_TEMP", tempdir); +} + diff --git a/source/blender/blenlib/intern/util.c b/source/blender/blenlib/intern/util.c index 387d1881d3c..e2acbf21624 100644 --- a/source/blender/blenlib/intern/util.c +++ b/source/blender/blenlib/intern/util.c @@ -992,6 +992,18 @@ void BLI_setenv(const char *env, const char*val) #endif } + +/** + Only set an env var if already not there. + Like Unix setenv(env, val, 0); + */ +void BLI_setenv_if_new(const char *env, const char* val) +{ + if(getenv(env) == NULL) + BLI_setenv(env, val); +} + + void BLI_clean(char *path) { if(path==0) return; @@ -1374,10 +1386,10 @@ void BLI_where_am_i(char *fullname, const char *name) char *path = NULL, *temp; #ifdef _WIN32 - char *seperator = ";"; + char *separator = ";"; char slash = '\\'; #else - char *seperator = ":"; + char *separator = ":"; char slash = '/'; #endif @@ -1415,7 +1427,7 @@ void BLI_where_am_i(char *fullname, const char *name) path = getenv("PATH"); if (path) { do { - temp = strstr(path, seperator); + temp = strstr(path, separator); if (temp) { strncpy(filename, path, temp - path); filename[temp - path] = 0;