This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/blenlib/intern/path_util.c

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1820 lines
44 KiB
C
Raw Normal View History

/*
2002-10-12 11:37:38 +00:00
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
2002-10-12 11:37:38 +00:00
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
2010-02-12 13:34:04 +00:00
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
2002-10-12 11:37:38 +00:00
*
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
* All rights reserved.
* various string, file, list operations.
2002-10-12 11:37:38 +00:00
*/
/** \file
* \ingroup bli
2011-02-27 20:37:56 +00:00
*/
2002-10-12 11:37:38 +00:00
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
2002-10-12 11:37:38 +00:00
#include "DNA_listBase.h"
#include "BLI_fileops.h"
#include "BLI_fnmatch.h"
#include "BLI_path_util.h"
#include "BLI_string.h"
#include "BLI_string_utf8.h"
#include "BLI_utildefines.h"
2002-10-12 11:37:38 +00:00
#ifdef WIN32
# include "utf_winfunc.h"
# include "utfconv.h"
# include <io.h>
# ifdef _WIN32_IE
# undef _WIN32_IE
# endif
# define _WIN32_IE 0x0501
# include "BLI_alloca.h"
# include "BLI_winstuff.h"
# include <shlobj.h>
# include <windows.h>
#else
2021-10-04 13:12:38 +11:00
# include <unistd.h>
#endif /* WIN32 */
#include "MEM_guardedalloc.h"
/* Declarations */
#ifdef WIN32
/**
* Return true if the path is absolute ie starts with a drive specifier
* (eg A:\) or is a UNC path.
*/
static bool BLI_path_is_abs(const char *name);
#endif /* WIN32 */
// #define DEBUG_STRSIZE
2002-10-12 11:37:38 +00:00
/* implementation */
int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort *r_num_len)
2002-10-12 11:37:38 +00:00
{
2017-10-28 17:48:45 +11:00
uint nums = 0, nume = 0;
int i;
bool found_digit = false;
const char *const lslash = BLI_path_slash_rfind(string);
2017-10-28 17:48:45 +11:00
const uint string_len = strlen(string);
const uint lslash_len = lslash != NULL ? (int)(lslash - string) : 0;
uint 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 >= (int)lslash_len; i--) {
2002-10-12 11:37:38 +00:00
if (isdigit(string[i])) {
if (found_digit) {
2002-10-12 11:37:38 +00:00
nums = i;
}
else {
2002-10-12 11:37:38 +00:00
nume = i;
nums = i;
found_digit = true;
2002-10-12 11:37:38 +00:00
}
}
else {
2019-03-27 13:16:10 +11:00
if (found_digit) {
break;
}
2002-10-12 11:37:38 +00:00
}
}
if (found_digit) {
const long long int ret = strtoll(&(string[nums]), NULL, 10);
if (ret >= INT_MIN && ret <= INT_MAX) {
if (tail) {
strcpy(tail, &string[nume + 1]);
}
if (head) {
strcpy(head, string);
head[nums] = 0;
}
if (r_num_len) {
*r_num_len = nume - nums + 1;
}
return (int)ret;
}
2002-10-12 11:37:38 +00:00
}
if (tail) {
strcpy(tail, string + name_end);
}
if (head) {
/* name_end points to last character of head,
* make it +1 so null-terminator is nicely placed
*/
BLI_strncpy(head, string, name_end + 1);
}
if (r_num_len) {
*r_num_len = 0;
}
return 0;
2002-10-12 11:37:38 +00:00
}
void BLI_path_sequence_encode(
char *string, const char *head, const char *tail, unsigned short numlen, int pic)
2002-10-12 11:37:38 +00:00
{
sprintf(string, "%s%.*d%s", head, numlen, MAX2(0, pic), tail);
2002-10-12 11:37:38 +00:00
}
static int BLI_path_unc_prefix_len(const char *path); /* defined below in same file */
void BLI_path_normalize(const char *relabase, char *path)
{
ptrdiff_t a;
char *start, *eind;
if (relabase) {
BLI_path_abs(path, relabase);
}
else {
if (path[0] == '/' && path[1] == '/') {
if (path[2] == '\0') {
return; /* path is "//" - can't clean it */
}
path = path + 2; /* leave the initial "//" untouched */
}
}
/* Note
* memmove(start, eind, strlen(eind) + 1);
* is the same as
2015-02-07 04:33:48 +11:00
* strcpy(start, eind);
* except strcpy should not be used because there is overlap,
* so use memmove's slightly more obscure syntax - Campbell
*/
2018-06-17 16:32:54 +02:00
#ifdef WIN32
while ((start = strstr(path, "\\..\\"))) {
eind = start + strlen("\\..\\") - 1;
a = start - path - 1;
2012-05-12 15:13:06 +00:00
while (a > 0) {
2019-03-27 13:16:10 +11:00
if (path[a] == '\\') {
break;
}
a--;
}
2012-05-12 15:13:06 +00:00
if (a < 0) {
break;
}
else {
memmove(path + a, eind, strlen(eind) + 1);
}
}
while ((start = strstr(path, "\\.\\"))) {
eind = start + strlen("\\.\\") - 1;
memmove(start, eind, strlen(eind) + 1);
}
/* remove two consecutive backslashes, but skip the UNC prefix,
* which needs to be preserved */
while ((start = strstr(path + BLI_path_unc_prefix_len(path), "\\\\"))) {
eind = start + strlen("\\\\") - 1;
memmove(start, eind, strlen(eind) + 1);
}
#else
while ((start = strstr(path, "/../"))) {
a = start - path - 1;
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);
}
else {
2021-07-07 12:55:19 +10:00
/* Support for odd paths: eg `/../home/me` --> `/home/me`
* this is a valid path in blender but we can't handle this the usual way below
* simply strip this prefix then evaluate the path as usual.
2021-07-07 12:55:19 +10:00
* Python's `os.path.normpath()` does this. */
/* NOTE: previous version of following call used an offset of 3 instead of 4,
2021-07-07 12:55:19 +10:00
* which meant that the `/../home/me` example actually became `home/me`.
2020-07-10 16:04:09 +10:00
* Using offset of 3 gives behavior consistent with the aforementioned
* Python routine. */
memmove(path, path + 3, strlen(path + 3) + 1);
}
}
while ((start = strstr(path, "/./"))) {
eind = start + (3 - 1) /* strlen("/./") - 1 */;
memmove(start, eind, strlen(eind) + 1);
}
while ((start = strstr(path, "//"))) {
eind = start + (2 - 1) /* strlen("//") - 1 */;
memmove(start, eind, strlen(eind) + 1);
}
#endif
}
void BLI_path_normalize_dir(const char *relabase, char *dir)
{
/* Would just create an unexpected "/" path, just early exit entirely. */
if (dir[0] == '\0') {
return;
}
BLI_path_normalize(relabase, dir);
BLI_path_slash_ensure(dir);
}
bool BLI_filename_make_safe(char *fname)
{
const char *invalid =
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f"
"/\\?*:|\"<>";
char *fn;
bool changed = false;
if (*fname == '\0') {
return changed;
}
for (fn = fname; *fn && (fn = strpbrk(fn, invalid)); fn++) {
*fn = '_';
changed = true;
}
/* Forbid only dots. */
2019-03-27 13:16:10 +11:00
for (fn = fname; *fn == '.'; fn++) {
/* pass */
}
if (*fn == '\0') {
*fname = '_';
changed = true;
}
#ifdef WIN32
{
const size_t len = strlen(fname);
const char *invalid_names[] = {
"con", "prn", "aux", "null", "com1", "com2", "com3", "com4",
"com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3",
"lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9", NULL,
};
char *lower_fname = BLI_strdup(fname);
const char **iname;
/* Forbid trailing dot (trailing space has already been replaced above). */
if (fname[len - 1] == '.') {
fname[len - 1] = '_';
changed = true;
}
/* Check for forbidden names - not we have to check all combination
* of upper and lower cases, hence the usage of lower_fname
* (more efficient than using BLI_strcasestr repeatedly). */
BLI_str_tolower_ascii(lower_fname, len);
for (iname = invalid_names; *iname; iname++) {
if (strstr(lower_fname, *iname) == lower_fname) {
const size_t iname_len = strlen(*iname);
/* Only invalid if the whole name is made of the invalid chunk, or it has an
* (assumed extension) dot just after. This means it will also catch 'valid'
* names like 'aux.foo.bar', but should be
* good enough for us! */
if ((iname_len == len) || (lower_fname[iname_len] == '.')) {
*fname = '_';
changed = true;
break;
}
}
}
MEM_freeN(lower_fname);
}
#endif
return changed;
}
bool BLI_path_make_safe(char *path)
{
/* Simply apply BLI_filename_make_safe() over each component of the path.
2015-10-07 15:02:06 +11:00
* Luckily enough, same 'safe' rules applies to filenames and dirnames. */
char *curr_slash, *curr_path = path;
bool changed = false;
bool skip_first = false;
#ifdef WIN32
if (BLI_path_is_abs(path)) {
/* Do not make safe 'C:' in 'C:\foo\bar'... */
skip_first = true;
}
#endif
for (curr_slash = (char *)BLI_path_slash_find(curr_path); curr_slash;
curr_slash = (char *)BLI_path_slash_find(curr_path)) {
const char backup = *curr_slash;
*curr_slash = '\0';
if (!skip_first && (*curr_path != '\0') && BLI_filename_make_safe(curr_path)) {
changed = true;
}
skip_first = false;
curr_path = curr_slash + 1;
*curr_slash = backup;
}
if (BLI_filename_make_safe(curr_path)) {
changed = true;
}
return changed;
}
bool BLI_path_is_rel(const char *path)
{
return path[0] == '/' && path[1] == '/';
}
bool BLI_path_is_unc(const char *name)
{
return name[0] == '\\' && name[1] == '\\';
}
/**
* Returns the length of the identifying prefix
* of a UNC path which can start with '\\' (short version)
* or '\\?\' (long version)
* If the path is not a UNC path, return 0
*/
static int BLI_path_unc_prefix_len(const char *path)
{
if (BLI_path_is_unc(path)) {
if ((path[2] == '?') && (path[3] == '\\')) {
/* we assume long UNC path like \\?\server\share\folder etc... */
return 4;
}
return 2;
}
return 0;
}
#if defined(WIN32)
/**
* Return true if the path is absolute ie starts with a drive specifier
* (eg A:\) or is a UNC path.
*/
static bool BLI_path_is_abs(const char *name)
{
return (name[1] == ':' && ELEM(name[2], '\\', '/')) || BLI_path_is_unc(name);
}
static wchar_t *next_slash(wchar_t *path)
{
wchar_t *slash = path;
2019-03-27 13:16:10 +11:00
while (*slash && *slash != L'\\') {
slash++;
}
return slash;
}
2021-02-05 16:23:34 +11:00
/* Adds a slash if the UNC path points to a share. */
static void BLI_path_add_slash_to_share(wchar_t *uncpath)
{
wchar_t *slash_after_server = next_slash(uncpath + 2);
if (*slash_after_server) {
wchar_t *slash_after_share = next_slash(slash_after_server + 1);
if (!(*slash_after_share)) {
slash_after_share[0] = L'\\';
slash_after_share[1] = L'\0';
}
}
}
static void BLI_path_unc_to_short(wchar_t *unc)
{
wchar_t tmp[PATH_MAX];
int len = wcslen(unc);
/* convert:
* \\?\UNC\server\share\folder\... to \\server\share\folder\...
* \\?\C:\ to C:\ and \\?\C:\folder\... to C:\folder\...
*/
if ((len > 3) && (unc[0] == L'\\') && (unc[1] == L'\\') && (unc[2] == L'?') &&
ELEM(unc[3], L'\\', L'/')) {
if ((len > 5) && (unc[5] == L':')) {
wcsncpy(tmp, unc + 4, len - 4);
tmp[len - 4] = L'\0';
wcscpy(unc, tmp);
}
else if ((len > 7) && (wcsncmp(&unc[4], L"UNC", 3) == 0) && ELEM(unc[7], L'\\', L'/')) {
tmp[0] = L'\\';
tmp[1] = L'\\';
wcsncpy(tmp + 2, unc + 8, len - 8);
tmp[len - 6] = L'\0';
wcscpy(unc, tmp);
}
}
}
void BLI_path_normalize_unc(char *path, int maxlen)
{
wchar_t *tmp_16 = alloc_utf16_from_8(path, 1);
BLI_path_normalize_unc_16(tmp_16);
conv_utf_16_to_8(tmp_16, path, maxlen);
}
void BLI_path_normalize_unc_16(wchar_t *path_16)
{
BLI_path_unc_to_short(path_16);
BLI_path_add_slash_to_share(path_16);
}
#endif
void BLI_path_rel(char *file, const char *relfile)
2002-10-12 11:37:38 +00:00
{
const char *lslash;
char temp[FILE_MAX];
char res[FILE_MAX];
2018-06-17 16:32:54 +02:00
/* if file is already relative, bail out */
if (BLI_path_is_rel(file)) {
return;
}
2018-06-17 16:32:54 +02:00
/* also bail out if relative path is not set */
if (relfile[0] == '\0') {
return;
}
#ifdef WIN32
if (BLI_strnlen(relfile, 3) > 2 && !BLI_path_is_abs(relfile)) {
2012-05-12 15:13:06 +00:00
char *ptemp;
/* fix missing volume name in relative base,
* can happen with old recent-files.txt files */
BLI_windows_get_default_root_dir(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);
}
else {
BLI_strncpy(temp, relfile, FILE_MAX);
}
if (BLI_strnlen(file, 3) > 2) {
bool is_unc = BLI_path_is_unc(file);
/* Ensure paths are both UNC paths or are both drives */
if (BLI_path_is_unc(temp) != is_unc) {
return;
}
/* Ensure both UNC paths are on the same share */
if (is_unc) {
int off;
int slash = 0;
for (off = 0; temp[off] && slash < 4; off++) {
2019-03-27 13:16:10 +11:00
if (temp[off] != file[off]) {
return;
2019-03-27 13:16:10 +11:00
}
2019-03-27 13:16:10 +11:00
if (temp[off] == '\\') {
slash++;
2019-03-27 13:16:10 +11:00
}
}
}
else if ((temp[1] == ':' && file[1] == ':') && (tolower(temp[0]) != tolower(file[0]))) {
return;
}
}
#else
BLI_strncpy(temp, relfile, FILE_MAX);
#endif
BLI_str_replace_char(temp + BLI_path_unc_prefix_len(temp), '\\', '/');
BLI_str_replace_char(file + BLI_path_unc_prefix_len(file), '\\', '/');
2018-06-17 16:32:54 +02:00
/* remove /./ which confuse the following slash counting... */
BLI_path_normalize(NULL, file);
BLI_path_normalize(NULL, temp);
2018-06-17 16:32:54 +02:00
/* the last slash in the file indicates where the path part ends */
lslash = BLI_path_slash_rfind(temp);
if (lslash) {
/* find the prefix of the filename that is equal for both filenames.
* This is replaced by the two slashes at the beginning */
const char *p = temp;
const char *q = file;
char *r = res;
#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
* in the rare case they match */
2012-05-12 15:13:06 +00:00
if ((*p == '\0') || (*q == '\0')) {
break;
}
}
2018-06-17 16:32:54 +02:00
/* we might have passed the slash when the beginning of a dir matches
* so we rewind. Only check on the actual filename
*/
if (*q != '/') {
while ((q >= file) && (*q != '/')) {
q--;
p--;
}
}
else if (*p != '/') {
while ((p >= temp) && (*p != '/')) {
p--;
q--;
}
}
r += BLI_strcpy_rlen(r, "//");
/* p now points to the slash that is at the beginning of the part
2018-06-17 16:32:54 +02: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
*/
2019-03-27 13:16:10 +11:00
if (p < temp) {
p = temp;
}
2012-05-12 15:13:06 +00:00
while (p && p < lslash) {
if (*p == '/') {
r += BLI_strcpy_rlen(r, "../");
}
2012-05-09 09:24:15 +00:00
p++;
2002-10-12 11:37:38 +00:00
}
/* don't copy the slash at the beginning */
r += BLI_strncpy_rlen(r, q + 1, FILE_MAX - (r - res));
2012-05-12 15:13:06 +00:00
#ifdef WIN32
BLI_str_replace_char(res + 2, '/', '\\');
#endif
strcpy(file, res);
2002-10-12 11:37:38 +00:00
}
}
bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char *sep)
{
#ifdef DEBUG_STRSIZE
memset(string, 0xff, sizeof(*string) * maxlen);
#endif
const size_t string_len = strlen(string);
const size_t suffix_len = strlen(suffix);
const size_t sep_len = strlen(sep);
ssize_t a;
char extension[FILE_MAX];
bool has_extension = false;
2019-03-27 13:16:10 +11:00
if (string_len + sep_len + suffix_len >= maxlen) {
return false;
2019-03-27 13:16:10 +11:00
}
for (a = string_len - 1; a >= 0; a--) {
if (string[a] == '.') {
has_extension = true;
break;
}
if (ELEM(string[a], '/', '\\')) {
break;
}
}
2019-03-27 13:16:10 +11:00
if (!has_extension) {
a = string_len;
2019-03-27 13:16:10 +11:00
}
BLI_strncpy(extension, string + a, sizeof(extension));
sprintf(string + a, "%s%s%s", sep, suffix, extension);
return true;
}
bool BLI_path_parent_dir(char *path)
2002-10-12 11:37:38 +00:00
{
2014-01-31 03:28:53 +11:00
const char parent_dir[] = {'.', '.', SEP, '\0'}; /* "../" or "..\\" */
2012-05-12 15:13:06 +00:00
char tmp[FILE_MAX + 4];
BLI_join_dirfile(tmp, sizeof(tmp), path, parent_dir);
BLI_path_normalize(NULL, tmp); /* does all the work of normalizing the path for us */
if (!BLI_path_extension_check(tmp, parent_dir)) {
strcpy(path, tmp); /* We assume pardir is always shorter... */
return true;
}
return false;
}
bool BLI_path_parent_dir_until_exists(char *dir)
{
bool valid_path = true;
/* Loop as long as cur path is not a dir, and we can get a parent path. */
while ((BLI_access(dir, R_OK) != 0) && (valid_path = BLI_path_parent_dir(dir))) {
/* pass */
}
return (valid_path && dir[0]);
}
/**
2020-12-14 19:04:43 +11: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
2020-12-14 19:04:43 +11:00
* `char_start` and `char_end` respectively. Returns true if such a sequence was found.
*/
static bool stringframe_chars(const char *path, int *char_start, int *char_end)
{
2017-10-28 17:48:45 +11:00
uint ch_sta, ch_end, i;
/* Insert current frame: file### -> file001 */
ch_sta = ch_end = 0;
for (i = 0; path[i] != '\0'; i++) {
if (ELEM(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 */
}
else if (path[i] == '#') {
ch_sta = i;
2012-05-12 15:13:06 +00:00
ch_end = ch_sta + 1;
while (path[ch_end] == '#') {
ch_end++;
}
2012-05-12 15:13:06 +00:00
i = ch_end - 1; /* keep searching */
2012-03-18 07:38:51 +00:00
/* don't break, there may be a slash after this that invalidates the previous #'s */
}
}
if (ch_end) {
2012-05-12 15:13:06 +00:00
*char_start = ch_sta;
*char_end = ch_end;
return true;
}
*char_start = -1;
*char_end = -1;
return false;
}
/**
2020-12-14 19:04:43 +11:00
* Ensure `path` contains at least one "#" character in its last slash-separated
* component, appending one digits long if not.
*/
static void ensure_digits(char *path, int digits)
{
char *file = (char *)BLI_path_slash_rfind(path);
2019-03-27 13:16:10 +11:00
if (file == NULL) {
2012-05-12 15:13:06 +00:00
file = path;
2019-03-27 13:16:10 +11:00
}
if (strrchr(file, '#') == NULL) {
2012-05-12 15:13:06 +00:00
int len = strlen(file);
while (digits--) {
2012-05-12 15:13:06 +00:00
file[len++] = '#';
}
2012-05-12 15:13:06 +00:00
file[len] = '\0';
}
}
bool BLI_path_frame(char *path, int frame, int digits)
{
int ch_sta, ch_end;
2019-03-27 13:16:10 +11:00
if (digits) {
ensure_digits(path, digits);
2019-03-27 13:16:10 +11:00
}
if (stringframe_chars(path, &ch_sta, &ch_end)) { /* warning, ch_end is the last # +1 */
char tmp[FILE_MAX];
BLI_snprintf(
tmp, sizeof(tmp), "%.*s%.*d%s", ch_sta, path, ch_end - ch_sta, frame, path + ch_end);
BLI_strncpy(path, tmp, FILE_MAX);
return true;
}
return false;
}
bool BLI_path_frame_range(char *path, int sta, int end, int digits)
{
int ch_sta, ch_end;
2019-03-27 13:16:10 +11:00
if (digits) {
ensure_digits(path, digits);
2019-03-27 13:16:10 +11:00
}
if (stringframe_chars(path, &ch_sta, &ch_end)) { /* warning, ch_end is the last # +1 */
char tmp[FILE_MAX];
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);
BLI_strncpy(path, tmp, FILE_MAX);
return true;
}
return false;
}
bool BLI_path_frame_get(char *path, int *r_frame, int *r_numdigits)
{
if (*path) {
char *file = (char *)BLI_path_slash_rfind(path);
char *c;
int len, numdigits;
numdigits = *r_numdigits = 0;
2019-03-27 13:16:10 +11:00
if (file == NULL) {
file = path;
2019-03-27 13:16:10 +11:00
}
/* first get the extension part */
len = strlen(file);
c = file + len;
/* isolate extension */
while (--c != file) {
if (*c == '.') {
c--;
break;
}
}
/* find start of number */
while (c != (file - 1) && isdigit(*c)) {
c--;
numdigits++;
}
if (numdigits) {
char prevchar;
c++;
prevchar = c[numdigits];
c[numdigits] = 0;
/* was the number really an extension? */
*r_frame = atoi(c);
c[numdigits] = prevchar;
*r_numdigits = numdigits;
return true;
}
}
return false;
}
void BLI_path_frame_strip(char *path, char *r_ext)
{
*r_ext = '\0';
if (*path == '\0') {
return;
}
char *file = (char *)BLI_path_slash_rfind(path);
char *c, *suffix;
int len;
int numdigits = 0;
2019-03-27 13:16:10 +11:00
if (file == NULL) {
file = path;
2019-03-27 13:16:10 +11:00
}
/* first get the extension part */
len = strlen(file);
c = file + len;
/* isolate extension */
while (--c != file) {
if (*c == '.') {
c--;
break;
}
}
suffix = c + 1;
/* find start of number */
while (c != (file - 1) && isdigit(*c)) {
c--;
numdigits++;
}
c++;
int suffix_length = len - (suffix - file);
2019-03-21 00:58:01 +11:00
BLI_strncpy(r_ext, suffix, suffix_length + 1);
/* replace the number with the suffix and terminate the string */
while (numdigits--) {
*c++ = '#';
}
*c = '\0';
}
bool BLI_path_frame_check_chars(const char *path)
{
int ch_sta, ch_end; /* dummy args */
return stringframe_chars(path, &ch_sta, &ch_end);
}
Application Templates: make templates more prominent in the UI. The goal here is to make app templates usable for default templates that we can ship with Blender. These only have a custom startup.blend currently and so are quite limited compared to app templates that fully customize Blender. But still it seems like the same kind of concept where we should be sharing the code and UI. It is useful to be able to save a startup.blend per template, and I can imagine some scripting being useful in the future as well. Changes made: * File > New and Ctrl+N now list the templates, replacing a separate Application Templates menu that was not as easy to discover. * File menu now shows name of active template above Save Startup File and Load Factory Settings to indicate these are saved/loaded per template. * The "Default" template was renamed to "General". * Workspaces can now be added from any of the template startup.blend files when clicking the (+) button in the topbar. * User preferences are now fully shared between app templates, unless the template includes a custom userpref.blend. I think this will be useful in general, not all app templates need their own keymaps for example. * Previously Save User Preferences would save the current app template and then Blender would start using that template by default. I've disabled this, to me it seems it was unintentional, or at least not clear at all that saving user preferences also makes the current Differential Revision: https://developer.blender.org/D3690
2018-08-28 15:12:14 +02:00
void BLI_path_to_display_name(char *display_name, int maxlen, const char *name)
{
/* Strip leading underscores and spaces. */
int strip_offset = 0;
while (ELEM(name[strip_offset], '_', ' ')) {
strip_offset++;
}
Application Templates: make templates more prominent in the UI. The goal here is to make app templates usable for default templates that we can ship with Blender. These only have a custom startup.blend currently and so are quite limited compared to app templates that fully customize Blender. But still it seems like the same kind of concept where we should be sharing the code and UI. It is useful to be able to save a startup.blend per template, and I can imagine some scripting being useful in the future as well. Changes made: * File > New and Ctrl+N now list the templates, replacing a separate Application Templates menu that was not as easy to discover. * File menu now shows name of active template above Save Startup File and Load Factory Settings to indicate these are saved/loaded per template. * The "Default" template was renamed to "General". * Workspaces can now be added from any of the template startup.blend files when clicking the (+) button in the topbar. * User preferences are now fully shared between app templates, unless the template includes a custom userpref.blend. I think this will be useful in general, not all app templates need their own keymaps for example. * Previously Save User Preferences would save the current app template and then Blender would start using that template by default. I've disabled this, to me it seems it was unintentional, or at least not clear at all that saving user preferences also makes the current Differential Revision: https://developer.blender.org/D3690
2018-08-28 15:12:14 +02:00
BLI_strncpy(display_name, name + strip_offset, maxlen);
Application Templates: make templates more prominent in the UI. The goal here is to make app templates usable for default templates that we can ship with Blender. These only have a custom startup.blend currently and so are quite limited compared to app templates that fully customize Blender. But still it seems like the same kind of concept where we should be sharing the code and UI. It is useful to be able to save a startup.blend per template, and I can imagine some scripting being useful in the future as well. Changes made: * File > New and Ctrl+N now list the templates, replacing a separate Application Templates menu that was not as easy to discover. * File menu now shows name of active template above Save Startup File and Load Factory Settings to indicate these are saved/loaded per template. * The "Default" template was renamed to "General". * Workspaces can now be added from any of the template startup.blend files when clicking the (+) button in the topbar. * User preferences are now fully shared between app templates, unless the template includes a custom userpref.blend. I think this will be useful in general, not all app templates need their own keymaps for example. * Previously Save User Preferences would save the current app template and then Blender would start using that template by default. I've disabled this, to me it seems it was unintentional, or at least not clear at all that saving user preferences also makes the current Differential Revision: https://developer.blender.org/D3690
2018-08-28 15:12:14 +02:00
/* Replace underscores with spaces. */
BLI_str_replace_char(display_name, '_', ' ');
Application Templates: make templates more prominent in the UI. The goal here is to make app templates usable for default templates that we can ship with Blender. These only have a custom startup.blend currently and so are quite limited compared to app templates that fully customize Blender. But still it seems like the same kind of concept where we should be sharing the code and UI. It is useful to be able to save a startup.blend per template, and I can imagine some scripting being useful in the future as well. Changes made: * File > New and Ctrl+N now list the templates, replacing a separate Application Templates menu that was not as easy to discover. * File menu now shows name of active template above Save Startup File and Load Factory Settings to indicate these are saved/loaded per template. * The "Default" template was renamed to "General". * Workspaces can now be added from any of the template startup.blend files when clicking the (+) button in the topbar. * User preferences are now fully shared between app templates, unless the template includes a custom userpref.blend. I think this will be useful in general, not all app templates need their own keymaps for example. * Previously Save User Preferences would save the current app template and then Blender would start using that template by default. I've disabled this, to me it seems it was unintentional, or at least not clear at all that saving user preferences also makes the current Differential Revision: https://developer.blender.org/D3690
2018-08-28 15:12:14 +02:00
/* Strip extension. */
BLI_path_extension_replace(display_name, maxlen, "");
Application Templates: make templates more prominent in the UI. The goal here is to make app templates usable for default templates that we can ship with Blender. These only have a custom startup.blend currently and so are quite limited compared to app templates that fully customize Blender. But still it seems like the same kind of concept where we should be sharing the code and UI. It is useful to be able to save a startup.blend per template, and I can imagine some scripting being useful in the future as well. Changes made: * File > New and Ctrl+N now list the templates, replacing a separate Application Templates menu that was not as easy to discover. * File menu now shows name of active template above Save Startup File and Load Factory Settings to indicate these are saved/loaded per template. * The "Default" template was renamed to "General". * Workspaces can now be added from any of the template startup.blend files when clicking the (+) button in the topbar. * User preferences are now fully shared between app templates, unless the template includes a custom userpref.blend. I think this will be useful in general, not all app templates need their own keymaps for example. * Previously Save User Preferences would save the current app template and then Blender would start using that template by default. I've disabled this, to me it seems it was unintentional, or at least not clear at all that saving user preferences also makes the current Differential Revision: https://developer.blender.org/D3690
2018-08-28 15:12:14 +02:00
/* Test if string has any upper case characters. */
bool all_lower = true;
for (int i = 0; display_name[i]; i++) {
if (isupper(display_name[i])) {
all_lower = false;
break;
}
}
Application Templates: make templates more prominent in the UI. The goal here is to make app templates usable for default templates that we can ship with Blender. These only have a custom startup.blend currently and so are quite limited compared to app templates that fully customize Blender. But still it seems like the same kind of concept where we should be sharing the code and UI. It is useful to be able to save a startup.blend per template, and I can imagine some scripting being useful in the future as well. Changes made: * File > New and Ctrl+N now list the templates, replacing a separate Application Templates menu that was not as easy to discover. * File menu now shows name of active template above Save Startup File and Load Factory Settings to indicate these are saved/loaded per template. * The "Default" template was renamed to "General". * Workspaces can now be added from any of the template startup.blend files when clicking the (+) button in the topbar. * User preferences are now fully shared between app templates, unless the template includes a custom userpref.blend. I think this will be useful in general, not all app templates need their own keymaps for example. * Previously Save User Preferences would save the current app template and then Blender would start using that template by default. I've disabled this, to me it seems it was unintentional, or at least not clear at all that saving user preferences also makes the current Differential Revision: https://developer.blender.org/D3690
2018-08-28 15:12:14 +02:00
if (all_lower) {
/* For full lowercase string, use title case. */
bool prevspace = true;
for (int i = 0; display_name[i]; i++) {
if (prevspace) {
display_name[i] = toupper(display_name[i]);
}
Application Templates: make templates more prominent in the UI. The goal here is to make app templates usable for default templates that we can ship with Blender. These only have a custom startup.blend currently and so are quite limited compared to app templates that fully customize Blender. But still it seems like the same kind of concept where we should be sharing the code and UI. It is useful to be able to save a startup.blend per template, and I can imagine some scripting being useful in the future as well. Changes made: * File > New and Ctrl+N now list the templates, replacing a separate Application Templates menu that was not as easy to discover. * File menu now shows name of active template above Save Startup File and Load Factory Settings to indicate these are saved/loaded per template. * The "Default" template was renamed to "General". * Workspaces can now be added from any of the template startup.blend files when clicking the (+) button in the topbar. * User preferences are now fully shared between app templates, unless the template includes a custom userpref.blend. I think this will be useful in general, not all app templates need their own keymaps for example. * Previously Save User Preferences would save the current app template and then Blender would start using that template by default. I've disabled this, to me it seems it was unintentional, or at least not clear at all that saving user preferences also makes the current Differential Revision: https://developer.blender.org/D3690
2018-08-28 15:12:14 +02:00
prevspace = isspace(display_name[i]);
}
}
}
bool BLI_path_abs(char *path, const char *basepath)
2002-10-12 11:37:38 +00:00
{
const bool wasrelative = BLI_path_is_rel(path);
char tmp[FILE_MAX];
char base[FILE_MAX];
#ifdef WIN32
/* without this: "" --> "C:\" */
if (*path == '\0') {
return wasrelative;
}
/* we are checking here if we have an absolute path that is not in the current
2018-06-17 16:32:54 +02:00
* blend file as a lib main - we are basically checking for the case that a
* UNIX root '/' is passed.
*/
if (!wasrelative && !BLI_path_is_abs(path)) {
char *p = path;
BLI_windows_get_default_root_dir(tmp);
/* Get rid of the slashes at the beginning of the path. */
while (ELEM(*p, '\\', '/')) {
p++;
}
strcat(tmp, p);
}
else {
BLI_strncpy(tmp, path, FILE_MAX);
}
#else
BLI_strncpy(tmp, path, sizeof(tmp));
2018-06-17 16:32:54 +02:00
2021-04-01 22:20:53 +11:00
/* Check for loading a MS-Windows path on a POSIX system
* in this case, there is no use in trying `C:/` since it
* will never exist on a Unix system.
2018-06-17 16:32:54 +02:00
*
2021-04-01 22:20:53 +11:00
* Add a `/` prefix and lowercase the drive-letter, remove the `:`.
* `C:\foo.JPG` -> `/c/foo.JPG` */
2018-06-17 16:32:54 +02:00
if (isalpha(tmp[0]) && (tmp[1] == ':') && ELEM(tmp[2], '\\', '/')) {
tmp[1] = tolower(tmp[0]); /* Replace ':' with drive-letter. */
2018-06-17 16:32:54 +02:00
tmp[0] = '/';
2021-04-01 22:20:53 +11:00
/* `\` the slash will be converted later. */
}
2018-06-17 16:32:54 +02:00
#endif
/* push slashes into unix mode - strings entering this part are
* potentially messed up: having both back- and forward slashes.
* Here we push into one conform direction, and at the end we
* push them into the system specific dir. This ensures uniformity
* of paths and solving some problems (and prevent potential future
* ones) -jesterKing.
* For UNC paths the first characters containing the UNC prefix
* shouldn't be switched as we need to distinguish them from
* paths relative to the .blend file -elubie */
BLI_str_replace_char(tmp + BLI_path_unc_prefix_len(tmp), '\\', '/');
2021-07-07 12:55:19 +10:00
/* Paths starting with `//` will get the blend file as their base,
* this isn't standard in any OS but is used in blender all over the place. */
if (wasrelative) {
const char *lslash;
BLI_strncpy(base, basepath, sizeof(base));
/* file component is ignored, so don't bother with the trailing slash */
BLI_path_normalize(NULL, base);
lslash = BLI_path_slash_rfind(base);
BLI_str_replace_char(base + BLI_path_unc_prefix_len(base), '\\', '/');
2002-10-12 11:37:38 +00:00
if (lslash) {
/* length up to and including last "/" */
const int baselen = (int)(lslash - base) + 1;
2010-04-25 15:24:18 +00:00
/* use path for temp storage here, we copy back over it right away */
BLI_strncpy(path, tmp + 2, FILE_MAX); /* strip "//" */
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 */
}
else {
/* 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
}
}
else {
/* base ignored */
BLI_strncpy(path, tmp, FILE_MAX);
2002-10-12 11:37:38 +00:00
}
#ifdef WIN32
/* NOTE(@jesterking): Skip first two chars, which in case of absolute path will
* be `drive:/blabla` and in case of `relpath` `//blabla/`.
* So `relpath` `//` will be retained, rest will be nice and shiny WIN32 backward slashes. */
BLI_str_replace_char(path + 2, '/', '\\');
#endif
/* ensure this is after correcting for path switch */
BLI_path_normalize(NULL, path);
2002-10-12 11:37:38 +00:00
return wasrelative;
}
bool BLI_path_is_abs_from_cwd(const char *path)
{
bool is_abs = false;
const int path_len_clamp = BLI_strnlen(path, 3);
2018-06-17 16:32:54 +02:00
#ifdef WIN32
2021-12-16 10:08:31 +01:00
if ((path_len_clamp >= 3 && BLI_path_is_abs(path)) || BLI_path_is_unc(path)) {
is_abs = true;
2019-03-27 13:16:10 +11:00
}
#else
if (path_len_clamp >= 2 && path[0] == '/') {
is_abs = true;
2019-03-27 13:16:10 +11:00
}
#endif
return is_abs;
}
2018-06-17 16:32:54 +02:00
bool BLI_path_abs_from_cwd(char *path, const size_t maxlen)
{
#ifdef DEBUG_STRSIZE
memset(path, 0xff, sizeof(*path) * maxlen);
#endif
if (!BLI_path_is_abs_from_cwd(path)) {
char cwd[FILE_MAX];
/* in case the full path to the blend isn't used */
if (BLI_current_working_dir(cwd, sizeof(cwd))) {
char origpath[FILE_MAX];
BLI_strncpy(origpath, path, FILE_MAX);
BLI_join_dirfile(path, maxlen, cwd, origpath);
}
else {
printf("Could not get the current working directory - $PWD for an unknown reason.\n");
}
return true;
}
return false;
2002-10-12 11:37:38 +00:00
}
#ifdef _WIN32
/**
* Tries appending each of the semicolon-separated extensions in the PATHEXT
2020-12-14 19:04:43 +11:00
* environment variable (Windows-only) onto `name` in turn until such a file is found.
* Returns success/failure.
*/
bool BLI_path_program_extensions_add_win32(char *name, const size_t maxlen)
{
bool retval = false;
int type;
type = BLI_exists(name);
if ((type == 0) || S_ISDIR(type)) {
/* typically 3-5, ".EXE", ".BAT"... etc */
const int ext_max = 12;
const char *ext = BLI_getenv("PATHEXT");
2015-06-02 15:38:14 +10:00
if (ext) {
const int name_len = strlen(name);
char *filename = alloca(name_len + ext_max);
char *filename_ext;
2015-06-02 15:38:14 +10:00
const char *ext_next;
2015-06-02 15:38:14 +10:00
/* null terminated in the loop */
memcpy(filename, name, name_len);
filename_ext = filename + name_len;
do {
2015-06-02 15:38:14 +10:00
int ext_len;
ext_next = strchr(ext, ';');
ext_len = ext_next ? ((ext_next++) - ext) : strlen(ext);
if (LIKELY(ext_len < ext_max)) {
memcpy(filename_ext, ext, ext_len);
filename_ext[ext_len] = '\0';
2015-06-06 21:02:16 +10:00
type = BLI_exists(filename);
if (type && (!S_ISDIR(type))) {
retval = true;
BLI_strncpy(name, filename, maxlen);
break;
}
}
2015-06-02 15:38:14 +10:00
} while ((ext = ext_next));
}
}
else {
retval = true;
}
return retval;
}
#endif /* WIN32 */
bool BLI_path_program_search(char *fullname, const size_t maxlen, const char *name)
{
#ifdef DEBUG_STRSIZE
memset(fullname, 0xff, sizeof(*fullname) * maxlen);
#endif
const char *path;
bool retval = false;
#ifdef _WIN32
const char separator = ';';
#else
const char separator = ':';
#endif
path = BLI_getenv("PATH");
if (path) {
char filename[FILE_MAX];
const char *temp;
do {
temp = strchr(path, separator);
if (temp) {
memcpy(filename, path, temp - path);
filename[temp - path] = 0;
path = temp + 1;
}
else {
BLI_strncpy(filename, path, sizeof(filename));
}
BLI_path_append(filename, maxlen, name);
if (
#ifdef _WIN32
BLI_path_program_extensions_add_win32(filename, maxlen)
#else
BLI_exists(filename)
#endif
) {
BLI_strncpy(fullname, filename, maxlen);
retval = true;
break;
}
} while (temp);
}
if (retval == false) {
*fullname = '\0';
}
return retval;
}
2012-05-12 15:13:06 +00:00
void BLI_setenv(const char *env, const char *val)
{
2011-09-21 08:40:30 +00:00
/* free windows */
#if (defined(_WIN32) || defined(_WIN64))
uputenv(env, val);
#else
2021-02-05 16:23:34 +11:00
/* Linux/macOS/BSD */
2019-03-27 13:16:10 +11:00
if (val) {
setenv(env, val, 1);
2019-03-27 13:16:10 +11:00
}
else {
unsetenv(env);
2019-03-27 13:16:10 +11:00
}
#endif
}
2012-05-12 15:13:06 +00:00
void BLI_setenv_if_new(const char *env, const char *val)
{
2019-03-27 13:16:10 +11:00
if (BLI_getenv(env) == NULL) {
BLI_setenv(env, val);
2019-03-27 13:16:10 +11:00
}
}
2018-09-05 14:46:54 +10:00
const char *BLI_getenv(const char *env)
{
#ifdef _MSC_VER
const char *result = NULL;
2020-12-14 19:04:43 +11:00
/* 32767 is the maximum size of the environment variable on windows,
* reserve one more character for the zero terminator. */
static wchar_t buffer[32768];
wchar_t *env_16 = alloc_utf16_from_8(env, 0);
if (env_16) {
if (GetEnvironmentVariableW(env_16, buffer, ARRAY_SIZE(buffer))) {
char *res_utf8 = alloc_utf_8_from_16(buffer, 0);
2020-12-14 19:04:43 +11:00
/* Make sure the result is valid, and will fit into our temporary storage buffer. */
if (res_utf8) {
if (strlen(res_utf8) + 1 < sizeof(buffer)) {
2020-12-14 19:04:43 +11:00
/* We are re-using the utf16 buffer here, since allocating a second static buffer to
* contain the UTF-8 version to return would be wasteful. */
memcpy(buffer, res_utf8, strlen(res_utf8) + 1);
result = (const char *)buffer;
}
free(res_utf8);
}
}
2019-03-27 13:16:10 +11:00
}
return result;
#else
return getenv(env);
#endif
}
bool BLI_make_existing_file(const char *name)
Giant commit! A full detailed description of this will be done later... is several days of work. Here's a summary: Render: - Full cleanup of render code, removing *all* globals and bad level calls all over blender. Render module is now not called abusive anymore - API-fied calls to rendering - Full recode of internal render pipeline. Is now rendering tiles by default, prepared for much smarter 'bucket' render later. - Each thread now can render a full part - Renders were tested with 4 threads, goes fine, apart from some lookup tables in softshadow and AO still - Rendering is prepared to do multiple layers and passes - No single 32 bits trick in render code anymore, all 100% floats now. Writing images/movies - moved writing images to blender kernel (bye bye 'schrijfplaatje'!) - made a new Movie handle system, also in kernel. This will enable much easier use of movies in Blender PreviewRender: - Using new render API, previewrender (in buttons) now uses regular render code to generate images. - new datafile 'preview.blend.c' has the preview scenes in it - previews get rendered in exact displayed size (1 pixel = 1 pixel) 3D Preview render - new; press Pkey in 3d window, for a panel that continuously renders (pkey is for games, i know... but we dont do that in orange now!) - this render works nearly identical to buttons-preview render, so it stops rendering on any event (mouse, keyboard, etc) - on moving/scaling the panel, the render code doesn't recreate all geometry - same for shifting/panning view - all other operations (now) regenerate the full render database still. - this is WIP... but big fun, especially for simple scenes! Compositor - Using same node system as now in use for shaders, you can composit images - works pretty straightforward... needs much more options/tools and integration with rendering still - is not threaded yet, nor is so smart to only recalculate changes... will be done soon! - the "Render Result" node will get all layers/passes as output sockets - The "Output" node renders to a builtin image, which you can view in the Image window. (yes, output nodes to render-result, and to files, is on the list!) The Bad News - "Unified Render" is removed. It might come back in some stage, but this system should be built from scratch. I can't really understand this code... I expect it is not much needed, especially with advanced layer/passes control - Panorama render, Field render, Motion blur, is not coded yet... (I had to recode every single feature in render, so...!) - Lens Flare is also not back... needs total revision, might become composit effect though (using zbuffer for visibility) - Part render is gone! (well, thats obvious, its default now). - The render window is only restored with limited functionality... I am going to check first the option to render to a Image window, so Blender can become a true single-window application. :) For example, the 'Spare render buffer' (jkey) doesnt work. - Render with border, now default creates a smaller image - No zbuffers are written yet... on the todo! - Scons files and MSVC will need work to get compiling again OK... thats what I can quickly recall. Now go compiling!
2006-01-23 22:05:47 +00:00
{
char di[FILE_MAX];
BLI_split_dir_part(name, di, sizeof(di));
2014-10-29 14:11:19 +01:00
/* make if the dir doesn't exist */
return BLI_dir_create_recursive(di);
Giant commit! A full detailed description of this will be done later... is several days of work. Here's a summary: Render: - Full cleanup of render code, removing *all* globals and bad level calls all over blender. Render module is now not called abusive anymore - API-fied calls to rendering - Full recode of internal render pipeline. Is now rendering tiles by default, prepared for much smarter 'bucket' render later. - Each thread now can render a full part - Renders were tested with 4 threads, goes fine, apart from some lookup tables in softshadow and AO still - Rendering is prepared to do multiple layers and passes - No single 32 bits trick in render code anymore, all 100% floats now. Writing images/movies - moved writing images to blender kernel (bye bye 'schrijfplaatje'!) - made a new Movie handle system, also in kernel. This will enable much easier use of movies in Blender PreviewRender: - Using new render API, previewrender (in buttons) now uses regular render code to generate images. - new datafile 'preview.blend.c' has the preview scenes in it - previews get rendered in exact displayed size (1 pixel = 1 pixel) 3D Preview render - new; press Pkey in 3d window, for a panel that continuously renders (pkey is for games, i know... but we dont do that in orange now!) - this render works nearly identical to buttons-preview render, so it stops rendering on any event (mouse, keyboard, etc) - on moving/scaling the panel, the render code doesn't recreate all geometry - same for shifting/panning view - all other operations (now) regenerate the full render database still. - this is WIP... but big fun, especially for simple scenes! Compositor - Using same node system as now in use for shaders, you can composit images - works pretty straightforward... needs much more options/tools and integration with rendering still - is not threaded yet, nor is so smart to only recalculate changes... will be done soon! - the "Render Result" node will get all layers/passes as output sockets - The "Output" node renders to a builtin image, which you can view in the Image window. (yes, output nodes to render-result, and to files, is on the list!) The Bad News - "Unified Render" is removed. It might come back in some stage, but this system should be built from scratch. I can't really understand this code... I expect it is not much needed, especially with advanced layer/passes control - Panorama render, Field render, Motion blur, is not coded yet... (I had to recode every single feature in render, so...!) - Lens Flare is also not back... needs total revision, might become composit effect though (using zbuffer for visibility) - Part render is gone! (well, thats obvious, its default now). - The render window is only restored with limited functionality... I am going to check first the option to render to a Image window, so Blender can become a true single-window application. :) For example, the 'Spare render buffer' (jkey) doesnt work. - Render with border, now default creates a smaller image - No zbuffers are written yet... on the todo! - Scons files and MSVC will need work to get compiling again OK... thats what I can quickly recall. Now go compiling!
2006-01-23 22:05:47 +00:00
}
void BLI_make_file_string(const char *relabase, char *string, const char *dir, const char *file)
2002-10-12 11:37:38 +00:00
{
int sl;
if (string) {
/* ensure this is always set even if dir/file are NULL */
2012-05-12 15:13:06 +00:00
string[0] = '\0';
if (ELEM(NULL, dir, file)) {
return; /* We don't want any NULLs */
}
}
else {
return; /* string is NULL, probably shouldn't happen but return anyway */
}
/* Resolve relative references */
2002-10-12 11:37:38 +00:00
if (relabase && dir[0] == '/' && dir[1] == '/') {
char *lslash;
2002-10-12 11:37:38 +00:00
/* Get the file name, chop everything past the last slash (ie. the filename) */
strcpy(string, relabase);
lslash = (char *)BLI_path_slash_rfind(string);
2019-03-27 13:16:10 +11:00
if (lslash) {
*(lslash + 1) = 0;
}
2012-05-12 15:13:06 +00:00
dir += 2; /* Skip over the relative reference */
2002-10-12 11:37:38 +00:00
}
#ifdef WIN32
else {
2012-05-12 15:13:06 +00:00
if (BLI_strnlen(dir, 3) >= 2 && dir[1] == ':') {
BLI_strncpy(string, dir, 3);
dir += 2;
}
else if (BLI_strnlen(dir, 3) >= 2 && BLI_path_is_unc(dir)) {
string[0] = 0;
}
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 && BLI_strnlen(relabase, 3) >= 2 && relabase[1] == ':') {
BLI_strncpy(string, relabase, 3);
string[2] = '\\';
string[3] = '\0';
}
else { /* we're out of luck here, guessing the first valid drive, usually c:\ */
BLI_windows_get_default_root_dir(string);
}
/* ignore leading slashes */
while (ELEM(*dir, '/', '\\')) {
2019-03-27 13:16:10 +11:00
dir++;
}
}
}
#endif
2002-10-12 11:37:38 +00:00
strcat(string, dir);
/* Make sure string ends in one (and only one) slash */
/* first trim all slashes from the end of the string */
sl = strlen(string);
while ((sl > 0) && ELEM(string[sl - 1], '/', '\\')) {
2012-05-12 15:13:06 +00:00
string[sl - 1] = '\0';
sl--;
}
/* since we've now removed all slashes, put back one slash at the end. */
strcat(string, "/");
2018-06-17 16:32:54 +02:00
while (ELEM(*file, '/', '\\')) {
2019-03-27 13:16:10 +11:00
/* Trim slashes from the front of file */
2002-10-12 11:37:38 +00:00
file++;
2019-03-27 13:16:10 +11:00
}
2018-06-17 16:32:54 +02:00
2012-05-12 15:13:06 +00:00
strcat(string, file);
2018-06-17 16:32:54 +02:00
2002-10-12 11:37:38 +00:00
/* Push all slashes to the system preferred direction */
BLI_path_slash_native(string);
2002-10-12 11:37:38 +00:00
}
static bool path_extension_check_ex(const char *str,
const size_t str_len,
const char *ext,
const size_t ext_len)
{
BLI_assert(strlen(str) == str_len);
BLI_assert(strlen(ext) == ext_len);
return (((str_len == 0 || ext_len == 0 || ext_len >= str_len) == 0) &&
(BLI_strcasecmp(ext, str + str_len - ext_len) == 0));
}
bool BLI_path_extension_check(const char *str, const char *ext)
2002-10-12 11:37:38 +00:00
{
return path_extension_check_ex(str, strlen(str), ext, strlen(ext));
}
bool BLI_path_extension_check_n(const char *str, ...)
{
const size_t str_len = strlen(str);
va_list args;
const char *ext;
bool ret = false;
va_start(args, str);
while ((ext = (const char *)va_arg(args, void *))) {
if (path_extension_check_ex(str, str_len, ext, strlen(ext))) {
ret = true;
break;
}
}
va_end(args);
return ret;
2002-10-12 11:37:38 +00:00
}
bool BLI_path_extension_check_array(const char *str, const char **ext_array)
{
const size_t str_len = strlen(str);
2012-05-12 15:13:06 +00:00
int i = 0;
while (ext_array[i]) {
if (path_extension_check_ex(str, str_len, ext_array[i], strlen(ext_array[i]))) {
return true;
}
i++;
}
return false;
}
bool BLI_path_extension_check_glob(const char *str, const char *ext_fnmatch)
{
2012-05-12 15:13:06 +00:00
const char *ext_step = ext_fnmatch;
char pattern[16];
while (ext_step[0]) {
const char *ext_next;
size_t len_ext;
2012-05-12 15:13:06 +00:00
if ((ext_next = strchr(ext_step, ';'))) {
len_ext = ext_next - ext_step + 1;
BLI_strncpy(pattern, ext_step, (len_ext > sizeof(pattern)) ? sizeof(pattern) : len_ext);
}
else {
len_ext = BLI_strncpy_rlen(pattern, ext_step, sizeof(pattern));
}
2012-05-12 15:13:06 +00:00
if (fnmatch(pattern, str, FNM_CASEFOLD) == 0) {
return true;
}
ext_step += len_ext;
}
return false;
}
bool BLI_path_extension_glob_validate(char *ext_fnmatch)
{
bool only_wildcards = false;
for (size_t i = strlen(ext_fnmatch); i-- > 0;) {
if (ext_fnmatch[i] == ';') {
/* Group separator, we truncate here if we only had wildcards so far.
* Otherwise, all is sound and fine. */
if (only_wildcards) {
ext_fnmatch[i] = '\0';
return true;
}
return false;
}
if (!ELEM(ext_fnmatch[i], '?', '*')) {
/* Non-wildcard char, we can break here and consider the pattern valid. */
return false;
}
/* So far, only wildcards in last group of the pattern... */
only_wildcards = true;
}
/* Only one group in the pattern, so even if its only made of wildcard(s),
2019-07-07 15:38:41 +10:00
* it is assumed valid. */
return false;
}
bool BLI_path_extension_replace(char *path, size_t maxlen, const char *ext)
{
#ifdef DEBUG_STRSIZE
memset(path, 0xff, sizeof(*path) * maxlen);
#endif
const size_t path_len = strlen(path);
const size_t ext_len = strlen(ext);
ssize_t a;
2012-05-12 15:13:06 +00:00
for (a = path_len - 1; a >= 0; a--) {
if (ELEM(path[a], '.', '/', '\\')) {
break;
}
}
if ((a < 0) || (path[a] != '.')) {
2012-05-12 15:13:06 +00:00
a = path_len;
}
2019-03-27 13:16:10 +11:00
if (a + ext_len >= maxlen) {
return false;
2019-03-27 13:16:10 +11:00
}
2012-05-12 15:13:06 +00:00
memcpy(path + a, ext, ext_len + 1);
return true;
}
bool BLI_path_extension_ensure(char *path, size_t maxlen, const char *ext)
{
#ifdef DEBUG_STRSIZE
memset(path, 0xff, sizeof(*path) * maxlen);
#endif
const size_t path_len = strlen(path);
const size_t ext_len = strlen(ext);
ssize_t a;
/* first check the extension is already there */
if ((ext_len <= path_len) && (STREQ(path + (path_len - ext_len), ext))) {
return true;
}
2012-05-12 15:13:06 +00:00
for (a = path_len - 1; a >= 0; a--) {
if (path[a] == '.') {
2012-05-12 15:13:06 +00:00
path[a] = '\0';
}
else {
break;
}
}
a++;
2019-03-27 13:16:10 +11:00
if (a + ext_len >= maxlen) {
return false;
2019-03-27 13:16:10 +11:00
}
2012-05-12 15:13:06 +00:00
memcpy(path + a, ext, ext_len + 1);
return true;
}
bool BLI_path_filename_ensure(char *filepath, size_t maxlen, const char *filename)
{
#ifdef DEBUG_STRSIZE
memset(filepath, 0xff, sizeof(*filepath) * maxlen);
#endif
char *c = (char *)BLI_path_slash_rfind(filepath);
if (!c || ((c - filepath) < maxlen - (strlen(filename) + 1))) {
strcpy(c ? &c[1] : filepath, filename);
return true;
}
return false;
}
void BLI_split_dirfile(
const char *string, char *dir, char *file, const size_t dirlen, const size_t filelen)
{
#ifdef DEBUG_STRSIZE
memset(dir, 0xff, sizeof(*dir) * dirlen);
memset(file, 0xff, sizeof(*file) * filelen);
#endif
const char *lslash_str = BLI_path_slash_rfind(string);
const size_t lslash = lslash_str ? (size_t)(lslash_str - string) + 1 : 0;
if (dir) {
if (lslash) {
/* +1 to include the slash and the last char */
BLI_strncpy(dir, string, MIN2(dirlen, lslash + 1));
}
else {
dir[0] = '\0';
}
}
if (file) {
2012-05-12 15:13:06 +00:00
BLI_strncpy(file, string + lslash, filelen);
}
}
void BLI_split_dir_part(const char *string, char *dir, const size_t dirlen)
{
BLI_split_dirfile(string, dir, NULL, dirlen, 0);
}
void BLI_split_file_part(const char *string, char *file, const size_t filelen)
{
BLI_split_dirfile(string, NULL, file, 0, filelen);
}
const char *BLI_path_extension(const char *filepath)
{
const char *extension = strrchr(filepath, '.');
if (extension == NULL) {
return NULL;
}
if (BLI_path_slash_find(extension) != NULL) {
/* There is a path separator in the extension, so the '.' was found in a
* directory component and not in the filename. */
return NULL;
}
return extension;
}
void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__restrict file)
{
size_t dirlen = BLI_strnlen(dst, maxlen);
/* inline BLI_path_slash_ensure */
if ((dirlen > 0) && (dst[dirlen - 1] != SEP)) {
dst[dirlen++] = SEP;
dst[dirlen] = '\0';
}
if (dirlen >= maxlen) {
return; /* fills the path */
}
BLI_strncpy(dst + dirlen, file, maxlen - dirlen);
}
void BLI_join_dirfile(char *__restrict dst,
const size_t maxlen,
const char *__restrict dir,
const char *__restrict file)
{
#ifdef DEBUG_STRSIZE
memset(dst, 0xff, sizeof(*dst) * maxlen);
#endif
2012-05-12 15:13:06 +00:00
size_t dirlen = BLI_strnlen(dir, maxlen);
/* Arguments can't match. */
BLI_assert(!ELEM(dst, dir, file));
/* Files starting with a separator cause a double-slash which could later be interpreted
* as a relative path where: `dir == "/"` and `file == "/file"` would result in "//file". */
BLI_assert(file[0] != SEP);
if (dirlen == maxlen) {
memcpy(dst, dir, dirlen);
dst[dirlen - 1] = '\0';
return; /* dir fills the path */
}
memcpy(dst, dir, dirlen + 1);
if (dirlen + 1 >= maxlen) {
return; /* fills the path */
}
/* inline BLI_path_slash_ensure */
if ((dirlen > 0) && !ELEM(dst[dirlen - 1], SEP, ALTSEP)) {
dst[dirlen++] = SEP;
2012-05-12 15:13:06 +00:00
dst[dirlen] = '\0';
}
if (dirlen >= maxlen) {
return; /* fills the path */
}
BLI_strncpy(dst + dirlen, file, maxlen - dirlen);
}
size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *path, ...)
{
#ifdef DEBUG_STRSIZE
memset(dst, 0xff, sizeof(*dst) * dst_len);
#endif
if (UNLIKELY(dst_len == 0)) {
return 0;
}
const size_t dst_last = dst_len - 1;
size_t ofs = BLI_strncpy_rlen(dst, path, dst_len);
if (ofs == dst_last) {
return ofs;
}
/* remove trailing slashes, unless there are _only_ trailing slashes
* (allow "//" as the first argument). */
bool has_trailing_slash = false;
if (ofs != 0) {
size_t len = ofs;
while ((len != 0) && ELEM(path[len - 1], SEP, ALTSEP)) {
len -= 1;
}
if (len != 0) {
ofs = len;
}
has_trailing_slash = (path[len] != '\0');
}
va_list args;
va_start(args, path);
while ((path = (const char *)va_arg(args, const char *))) {
has_trailing_slash = false;
const char *path_init = path;
while (ELEM(path[0], SEP, ALTSEP)) {
path++;
}
size_t len = strlen(path);
if (len != 0) {
while ((len != 0) && ELEM(path[len - 1], SEP, ALTSEP)) {
len -= 1;
}
if (len != 0) {
/* the very first path may have a slash at the end */
if (ofs && !ELEM(dst[ofs - 1], SEP, ALTSEP)) {
dst[ofs++] = SEP;
if (ofs == dst_last) {
break;
}
}
has_trailing_slash = (path[len] != '\0');
if (ofs + len >= dst_last) {
len = dst_last - ofs;
}
memcpy(&dst[ofs], path, len);
ofs += len;
if (ofs == dst_last) {
break;
}
}
}
else {
has_trailing_slash = (path_init != path);
}
}
va_end(args);
if (has_trailing_slash) {
if ((ofs != dst_last) && (ofs != 0) && (ELEM(dst[ofs - 1], SEP, ALTSEP) == 0)) {
dst[ofs++] = SEP;
}
}
BLI_assert(ofs <= dst_last);
dst[ofs] = '\0';
return ofs;
}
const char *BLI_path_basename(const char *path)
{
const char *const filename = BLI_path_slash_rfind(path);
return filename ? filename + 1 : path;
}
bool BLI_path_name_at_index(const char *__restrict path,
const int index,
int *__restrict r_offset,
int *__restrict r_len)
{
if (index >= 0) {
int index_step = 0;
int prev = -1;
int i = 0;
while (true) {
const char c = path[i];
if (ELEM(c, SEP, ALTSEP, '\0')) {
if (prev + 1 != i) {
prev += 1;
if (index_step == index) {
*r_offset = prev;
*r_len = i - prev;
// printf("!!! %d %d\n", start, end);
return true;
}
index_step += 1;
}
if (c == '\0') {
break;
}
prev = i;
}
i += 1;
}
return false;
}
/* negative number, reverse where -1 is the last element */
int index_step = -1;
int prev = strlen(path);
int i = prev - 1;
while (true) {
const char c = i >= 0 ? path[i] : '\0';
if (ELEM(c, SEP, ALTSEP, '\0')) {
if (prev - 1 != i) {
i += 1;
if (index_step == index) {
*r_offset = i;
*r_len = prev - i;
return true;
}
index_step -= 1;
}
if (c == '\0') {
break;
}
prev = i;
}
i -= 1;
}
return false;
}
bool BLI_path_contains(const char *container_path, const char *containee_path)
{
char container_native[PATH_MAX];
char containee_native[PATH_MAX];
/* Keep space for a trailing slash. If the path is truncated by this, the containee path is
* longer than PATH_MAX and the result is ill-defined. */
BLI_strncpy(container_native, container_path, PATH_MAX - 1);
BLI_strncpy(containee_native, containee_path, PATH_MAX);
BLI_path_slash_native(container_native);
BLI_path_slash_native(containee_native);
BLI_path_normalize(NULL, container_native);
BLI_path_normalize(NULL, containee_native);
#ifdef WIN32
BLI_str_tolower_ascii(container_native, PATH_MAX);
BLI_str_tolower_ascii(containee_native, PATH_MAX);
#endif
if (STREQ(container_native, containee_native)) {
/* The paths are equal, they contain each other. */
return true;
}
/* Add a trailing slash to prevent same-prefix directories from matching.
2021-09-29 07:29:15 +10:00
* e.g. "/some/path" doesn't contain "/some/path_lib". */
BLI_path_slash_ensure(container_native);
return BLI_str_startswith(containee_native, container_native);
}
const char *BLI_path_slash_find(const char *string)
{
const char *const ffslash = strchr(string, '/');
const char *const fbslash = strchr(string, '\\');
2018-06-17 16:32:54 +02:00
2019-03-27 13:16:10 +11:00
if (!ffslash) {
return fbslash;
}
if (!fbslash) {
2019-03-27 13:16:10 +11:00
return ffslash;
}
2018-06-17 16:32:54 +02:00
return (ffslash < fbslash) ? ffslash : fbslash;
}
const char *BLI_path_slash_rfind(const char *string)
{
const char *const lfslash = strrchr(string, '/');
const char *const lbslash = strrchr(string, '\\');
2019-03-27 13:16:10 +11:00
if (!lfslash) {
return lbslash;
}
if (!lbslash) {
2019-03-27 13:16:10 +11:00
return lfslash;
}
2018-06-17 16:32:54 +02:00
return (lfslash > lbslash) ? lfslash : lbslash;
}
int BLI_path_slash_ensure(char *string)
{
int len = strlen(string);
2012-05-12 15:13:06 +00:00
if (len == 0 || string[len - 1] != SEP) {
string[len] = SEP;
2012-05-12 15:13:06 +00:00
string[len + 1] = '\0';
return len + 1;
}
return len;
}
void BLI_path_slash_rstrip(char *string)
{
int len = strlen(string);
while (len) {
2012-05-12 15:13:06 +00:00
if (string[len - 1] == SEP) {
string[len - 1] = '\0';
len--;
}
else {
break;
}
}
}
void BLI_path_slash_native(char *path)
{
#ifdef WIN32
if (path && BLI_strnlen(path, 3) > 2) {
BLI_str_replace_char(path + 2, ALTSEP, SEP);
}
#else
BLI_str_replace_char(path + BLI_path_unc_prefix_len(path), ALTSEP, SEP);
#endif
}