2022-02-11 09:07:11 +11:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
|
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2019-02-18 08:08:12 +11:00
|
|
|
/** \file
|
|
|
|
|
* \ingroup bli
|
2022-02-09 18:31:42 +11:00
|
|
|
* Various string, file, list operations.
|
2011-02-27 20:37:56 +00:00
|
|
|
*/
|
|
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
#include <ctype.h>
|
|
|
|
|
#include <stdlib.h>
|
2020-03-19 09:33:03 +01:00
|
|
|
#include <string.h>
|
2002-10-12 11:37:38 +00:00
|
|
|
|
2011-10-21 17:37:38 +00:00
|
|
|
#include "DNA_listBase.h"
|
2008-02-13 13:55:22 +00:00
|
|
|
|
2009-12-13 17:46:30 +00:00
|
|
|
#include "BLI_fileops.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_fnmatch.h"
|
2009-12-13 17:46:30 +00:00
|
|
|
#include "BLI_path_util.h"
|
|
|
|
|
#include "BLI_string.h"
|
2020-12-11 11:59:14 -07:00
|
|
|
#include "BLI_string_utf8.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
#include "BLI_utildefines.h"
|
2002-10-12 11:37:38 +00:00
|
|
|
|
T39690: Modifications to Blender's 'temp dir' system.
Current temporary data of Blender suffers one major issue - default 'temp' dir on Windows is never
automatically cleaned up, and can end being quite big when used by Blender, especially when we have
to store per-process data (using getpid() in file names).
To address this, this patch:
* Divides tempdir paths in two, one for 'base' temp dir (the same as previous unique tempdir path),
the other is a mkdtemp-generated sub-dir, specific to each Blender instance.
* Only uses base tempdir when we need some shallow persistance accross Blender sessions - and we always
reuse the same filename (quit.blend...) or generate small file (crash reports...).
* Uses temp sub-dir for heavy files like pointcache or renderEXRs (Save Buffer option).
* Erases temp sub-dir on quit or crash.
To get this working it also adds a working 'recursive delete' to BLI_delete() under Windows.
Note that, as in current code, the 'recover render result' hack-feature that was possible
with SaveBuffer option is still removed. A real renderresult cache feature will be added
soon, though.
Reviewers: campbellbarton, brecht, sergey
Reviewed By: campbellbarton, sergey
CC: sergey
Differential Revision: https://developer.blender.org/D531
2014-06-23 13:42:19 +02: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
|
2015-06-04 16:48:56 +10:00
|
|
|
# include "BLI_alloca.h"
|
2020-03-19 09:33:03 +01:00
|
|
|
# include "BLI_winstuff.h"
|
|
|
|
|
# include <shlobj.h>
|
|
|
|
|
# include <windows.h>
|
2015-04-08 21:57:49 +02:00
|
|
|
#else
|
2021-10-04 13:12:38 +11:00
|
|
|
# include <unistd.h>
|
2010-03-20 18:52:03 +00:00
|
|
|
#endif /* WIN32 */
|
|
|
|
|
|
2015-07-14 18:42:22 +02:00
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
|
|
/* Declarations */
|
|
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
|
2019-11-25 01:14:39 +11:00
|
|
|
/**
|
|
|
|
|
* Return true if the path is absolute ie starts with a drive specifier
|
|
|
|
|
* (eg A:\) or is a UNC path.
|
|
|
|
|
*/
|
2015-07-14 18:42:22 +02:00
|
|
|
static bool BLI_path_is_abs(const char *name);
|
|
|
|
|
|
|
|
|
|
#endif /* WIN32 */
|
|
|
|
|
|
2018-06-17 15:35:00 +02:00
|
|
|
// #define DEBUG_STRSIZE
|
|
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
/* implementation */
|
|
|
|
|
|
2022-03-30 17:26:42 +11:00
|
|
|
int BLI_path_sequence_decode(const char *string, char *head, char *tail, ushort *r_digits_len)
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
2017-10-28 17:48:45 +11:00
|
|
|
uint nums = 0, nume = 0;
|
2013-11-24 16:20:04 +11:00
|
|
|
int i;
|
2013-03-04 19:27:51 +00:00
|
|
|
bool found_digit = false;
|
2020-04-07 12:02:21 +10:00
|
|
|
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;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-11 10:43:34 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-11-25 21:13:58 +11:00
|
|
|
for (i = name_end - 1; i >= (int)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 {
|
2019-03-27 13:16:10 +11:00
|
|
|
if (found_digit) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
if (found_digit) {
|
2018-06-11 10:43:34 +02:00
|
|
|
const long long int ret = strtoll(&(string[nums]), NULL, 10);
|
2018-06-01 11:58:50 +02:00
|
|
|
if (ret >= INT_MIN && ret <= INT_MAX) {
|
2018-06-11 10:43:34 +02:00
|
|
|
if (tail) {
|
|
|
|
|
strcpy(tail, &string[nume + 1]);
|
|
|
|
|
}
|
2018-06-01 11:58:50 +02:00
|
|
|
if (head) {
|
|
|
|
|
strcpy(head, string);
|
|
|
|
|
head[nums] = 0;
|
|
|
|
|
}
|
2022-03-30 17:26:42 +11:00
|
|
|
if (r_digits_len) {
|
|
|
|
|
*r_digits_len = nume - nums + 1;
|
2018-06-11 10:43:34 +02:00
|
|
|
}
|
|
|
|
|
return (int)ret;
|
2010-07-04 17:14:06 +00:00
|
|
|
}
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-11 10:43:34 +02:00
|
|
|
if (tail) {
|
|
|
|
|
strcpy(tail, string + name_end);
|
|
|
|
|
}
|
2018-06-01 11:58:50 +02:00
|
|
|
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);
|
2010-07-04 17:14:06 +00:00
|
|
|
}
|
2022-03-30 17:26:42 +11:00
|
|
|
if (r_digits_len) {
|
|
|
|
|
*r_digits_len = 0;
|
2018-06-11 10:43:34 +02:00
|
|
|
}
|
2018-06-01 11:58:50 +02:00
|
|
|
return 0;
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
void BLI_path_sequence_encode(
|
2010-04-23 23:01:50 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2014-04-21 16:49:35 +02:00
|
|
|
static int BLI_path_unc_prefix_len(const char *path); /* defined below in same file */
|
2010-11-07 08:49:07 +00:00
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
void BLI_path_normalize(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') {
|
2021-06-22 10:42:32 -07:00
|
|
|
return; /* path is "//" - can't clean it */
|
2008-06-05 13:02:00 +00:00
|
|
|
}
|
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
|
|
|
}
|
2019-04-17 06:17:24 +02: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
|
2015-02-07 04:33:48 +11:00
|
|
|
* strcpy(start, eind);
|
2008-06-11 09:04:41 +00:00
|
|
|
* 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
|
|
|
*/
|
2018-06-17 16:32:54 +02:00
|
|
|
|
2005-12-14 13:21:32 +00:00
|
|
|
#ifdef WIN32
|
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) {
|
2019-03-27 13:16:10 +11: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
|
|
|
}
|
2019-04-17 06:17:24 +02: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
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-04-21 16:49:35 +02:00
|
|
|
/* remove two consecutive backslashes, but skip the UNC prefix,
|
|
|
|
|
* which needs to be preserved */
|
|
|
|
|
while ((start = strstr(path + BLI_path_unc_prefix_len(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
|
|
|
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 {
|
2021-07-07 12:55:19 +10:00
|
|
|
/* Support for odd paths: eg `/../home/me` --> `/home/me`
|
2021-06-22 10:42:32 -07:00
|
|
|
* this is a valid path in blender but we can't handle this the usual way below
|
2013-03-10 04:43:15 +00:00
|
|
|
* 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. */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-07-03 23:08:40 +10:00
|
|
|
/* 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
|
2013-03-10 04:43:15 +00:00
|
|
|
* 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
|
|
|
}
|
2019-04-17 06:17:24 +02: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
|
|
|
}
|
2019-04-17 06:17:24 +02: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
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
void BLI_path_normalize_dir(const char *relabase, char *dir)
|
2011-03-11 00:30:51 +00:00
|
|
|
{
|
2020-12-08 12:37:46 +01:00
|
|
|
/* Would just create an unexpected "/" path, just early exit entirely. */
|
|
|
|
|
if (dir[0] == '\0') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
BLI_path_normalize(relabase, dir);
|
|
|
|
|
BLI_path_slash_ensure(dir);
|
2011-03-11 00:30:51 +00:00
|
|
|
}
|
|
|
|
|
|
UDIM: Support virtual filenames
This implements the design detailed in T92696 to support virtual
filenames for UDIM textures. Currently, the following 2 substitution
tokens are supported:
| Token | Meaning |
| ----- | ---- |
| <UDIM> | 1001 + u-tile + v-tile * 10 |
| <UVTILE> | Equivalent to u<u-tile + 1>_v<v-tile + 1> |
Example for u-tile of 3 and v-tile of 1:
filename.<UDIM>_ver0023.png --> filename.1014_ver0023.png
filename.<UVTILE>_ver0023.png --> filename.u4_v2_ver0023.png
For image loading, the existing workflow is unchanged. A user can select
one or more image files, belonging to one or more UDIM tile sets, and
have Blender load them all as it does today. Now the <UVTILE> format is
"guessed" just as the <UDIM> format was guessed before.
If guessing fails, the user can simply go into the Image Editor and type
the proper substitution in the filename. Once typing is complete,
Blender will reload the files and correctly fill the tiles. This
workflow is new as attempting to fix the guessing in current versions
did not really work, and the user was often stuck with a confusing
situation.
For image saving, the existing workflow is changed slightly. Currently,
when saving, a user has to be sure to type the filename of the first
tile (e.g. filename.1001.png) to save the entire UDIM set. The number
could differ if they start at a different tile etc. This is confusing.
Now, the user should type a filename containing the appropriate
substitution token. By default Blender will fill in a default name using
the <UDIM> token but the user is free to save out images using <UVTILE>
if they wish.
Differential Revision: https://developer.blender.org/D13057
2021-12-30 22:06:23 -08:00
|
|
|
bool BLI_filename_make_safe_ex(char *fname, bool allow_tokens)
|
2015-01-16 18:48:59 +01:00
|
|
|
{
|
UDIM: Support virtual filenames
This implements the design detailed in T92696 to support virtual
filenames for UDIM textures. Currently, the following 2 substitution
tokens are supported:
| Token | Meaning |
| ----- | ---- |
| <UDIM> | 1001 + u-tile + v-tile * 10 |
| <UVTILE> | Equivalent to u<u-tile + 1>_v<v-tile + 1> |
Example for u-tile of 3 and v-tile of 1:
filename.<UDIM>_ver0023.png --> filename.1014_ver0023.png
filename.<UVTILE>_ver0023.png --> filename.u4_v2_ver0023.png
For image loading, the existing workflow is unchanged. A user can select
one or more image files, belonging to one or more UDIM tile sets, and
have Blender load them all as it does today. Now the <UVTILE> format is
"guessed" just as the <UDIM> format was guessed before.
If guessing fails, the user can simply go into the Image Editor and type
the proper substitution in the filename. Once typing is complete,
Blender will reload the files and correctly fill the tiles. This
workflow is new as attempting to fix the guessing in current versions
did not really work, and the user was often stuck with a confusing
situation.
For image saving, the existing workflow is changed slightly. Currently,
when saving, a user has to be sure to type the filename of the first
tile (e.g. filename.1001.png) to save the entire UDIM set. The number
could differ if they start at a different tile etc. This is confusing.
Now, the user should type a filename containing the appropriate
substitution token. By default Blender will fill in a default name using
the <UDIM> token but the user is free to save out images using <UVTILE>
if they wish.
Differential Revision: https://developer.blender.org/D13057
2021-12-30 22:06:23 -08:00
|
|
|
#define INVALID_CHARS \
|
|
|
|
|
"\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" \
|
|
|
|
|
"/\\?*:|\""
|
|
|
|
|
#define INVALID_TOKENS "<>"
|
|
|
|
|
|
|
|
|
|
const char *invalid = allow_tokens ? INVALID_CHARS : INVALID_CHARS INVALID_TOKENS;
|
|
|
|
|
|
|
|
|
|
#undef INVALID_CHARS
|
|
|
|
|
#undef INVALID_TOKENS
|
|
|
|
|
|
2015-07-14 18:42:22 +02:00
|
|
|
char *fn;
|
|
|
|
|
bool changed = false;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-07-14 18:42:22 +02:00
|
|
|
if (*fname == '\0') {
|
|
|
|
|
return changed;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-07-14 18:42:22 +02:00
|
|
|
for (fn = fname; *fn && (fn = strpbrk(fn, invalid)); fn++) {
|
|
|
|
|
*fn = '_';
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-07-14 18:42:22 +02:00
|
|
|
/* Forbid only dots. */
|
2019-03-27 13:16:10 +11:00
|
|
|
for (fn = fname; *fn == '.'; fn++) {
|
|
|
|
|
/* pass */
|
|
|
|
|
}
|
2015-07-14 18:42:22 +02:00
|
|
|
if (*fn == '\0') {
|
2015-01-16 18:48:59 +01:00
|
|
|
*fname = '_';
|
2015-07-14 18:42:22 +02:00
|
|
|
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;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-07-14 18:42:22 +02:00
|
|
|
/* Forbid trailing dot (trailing space has already been replaced above). */
|
|
|
|
|
if (fname[len - 1] == '.') {
|
|
|
|
|
fname[len - 1] = '_';
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-01-15 23:15:58 +11:00
|
|
|
/* 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). */
|
2015-07-14 18:42:22 +02:00
|
|
|
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);
|
2019-01-15 23:15:58 +11:00
|
|
|
/* 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
|
2015-07-14 18:42:22 +02:00
|
|
|
* good enough for us! */
|
|
|
|
|
if ((iname_len == len) || (lower_fname[iname_len] == '.')) {
|
|
|
|
|
*fname = '_';
|
|
|
|
|
changed = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-07-14 18:42:22 +02:00
|
|
|
MEM_freeN(lower_fname);
|
2015-01-16 18:48:59 +01:00
|
|
|
}
|
2015-07-14 18:42:22 +02:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
return changed;
|
|
|
|
|
}
|
|
|
|
|
|
UDIM: Support virtual filenames
This implements the design detailed in T92696 to support virtual
filenames for UDIM textures. Currently, the following 2 substitution
tokens are supported:
| Token | Meaning |
| ----- | ---- |
| <UDIM> | 1001 + u-tile + v-tile * 10 |
| <UVTILE> | Equivalent to u<u-tile + 1>_v<v-tile + 1> |
Example for u-tile of 3 and v-tile of 1:
filename.<UDIM>_ver0023.png --> filename.1014_ver0023.png
filename.<UVTILE>_ver0023.png --> filename.u4_v2_ver0023.png
For image loading, the existing workflow is unchanged. A user can select
one or more image files, belonging to one or more UDIM tile sets, and
have Blender load them all as it does today. Now the <UVTILE> format is
"guessed" just as the <UDIM> format was guessed before.
If guessing fails, the user can simply go into the Image Editor and type
the proper substitution in the filename. Once typing is complete,
Blender will reload the files and correctly fill the tiles. This
workflow is new as attempting to fix the guessing in current versions
did not really work, and the user was often stuck with a confusing
situation.
For image saving, the existing workflow is changed slightly. Currently,
when saving, a user has to be sure to type the filename of the first
tile (e.g. filename.1001.png) to save the entire UDIM set. The number
could differ if they start at a different tile etc. This is confusing.
Now, the user should type a filename containing the appropriate
substitution token. By default Blender will fill in a default name using
the <UDIM> token but the user is free to save out images using <UVTILE>
if they wish.
Differential Revision: https://developer.blender.org/D13057
2021-12-30 22:06:23 -08:00
|
|
|
bool BLI_filename_make_safe(char *fname)
|
|
|
|
|
{
|
|
|
|
|
return BLI_filename_make_safe_ex(fname, false);
|
|
|
|
|
}
|
|
|
|
|
|
2015-07-14 18:42:22 +02:00
|
|
|
bool BLI_path_make_safe(char *path)
|
|
|
|
|
{
|
2022-03-17 10:04:45 +11:00
|
|
|
/* Simply apply #BLI_filename_make_safe() over each component of the path.
|
|
|
|
|
* Luckily enough, same 'safe' rules applies to file & directory names. */
|
2015-07-14 18:42:22 +02:00
|
|
|
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
|
|
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
for (curr_slash = (char *)BLI_path_slash_find(curr_path); curr_slash;
|
|
|
|
|
curr_slash = (char *)BLI_path_slash_find(curr_path)) {
|
2015-07-14 18:42:22 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-07-14 18:42:22 +02:00
|
|
|
return changed;
|
2015-01-16 18:48:59 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
bool BLI_path_is_rel(const char *path)
|
2012-08-29 10:32:38 +00:00
|
|
|
{
|
|
|
|
|
return path[0] == '/' && path[1] == '/';
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-21 16:49:35 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
|
|
|
|
|
return 2;
|
2014-04-21 16:49:35 +02:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-04-21 16:49:35 +02:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#if defined(WIN32)
|
|
|
|
|
|
2019-11-25 01:14:39 +11:00
|
|
|
/**
|
|
|
|
|
* Return true if the path is absolute ie starts with a drive specifier
|
|
|
|
|
* (eg A:\) or is a UNC path.
|
|
|
|
|
*/
|
2014-04-21 16:49:35 +02:00
|
|
|
static bool BLI_path_is_abs(const char *name)
|
|
|
|
|
{
|
2020-03-07 00:58:48 +11:00
|
|
|
return (name[1] == ':' && ELEM(name[2], '\\', '/')) || BLI_path_is_unc(name);
|
2014-04-21 16:49:35 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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++;
|
|
|
|
|
}
|
2014-04-21 16:49:35 +02:00
|
|
|
return slash;
|
|
|
|
|
}
|
|
|
|
|
|
2021-02-05 16:23:34 +11:00
|
|
|
/* Adds a slash if the UNC path points to a share. */
|
2014-04-21 16:49:35 +02:00
|
|
|
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];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-04-21 16:49:35 +02:00
|
|
|
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'?') &&
|
2020-03-06 12:49:15 -03:00
|
|
|
ELEM(unc[3], L'\\', L'/')) {
|
2014-04-21 16:49:35 +02:00
|
|
|
if ((len > 5) && (unc[5] == L':')) {
|
|
|
|
|
wcsncpy(tmp, unc + 4, len - 4);
|
|
|
|
|
tmp[len - 4] = L'\0';
|
|
|
|
|
wcscpy(unc, tmp);
|
|
|
|
|
}
|
2020-03-07 00:58:48 +11:00
|
|
|
else if ((len > 7) && (wcsncmp(&unc[4], L"UNC", 3) == 0) && ELEM(unc[7], L'\\', L'/')) {
|
2014-04-21 16:49:35 +02:00
|
|
|
tmp[0] = L'\\';
|
|
|
|
|
tmp[1] = L'\\';
|
|
|
|
|
wcsncpy(tmp + 2, unc + 8, len - 8);
|
|
|
|
|
tmp[len - 6] = L'\0';
|
|
|
|
|
wcscpy(unc, tmp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
void BLI_path_normalize_unc(char *path, int maxlen)
|
2014-04-21 16:49:35 +02:00
|
|
|
{
|
|
|
|
|
wchar_t *tmp_16 = alloc_utf16_from_8(path, 1);
|
2020-04-08 16:29:46 +10:00
|
|
|
BLI_path_normalize_unc_16(tmp_16);
|
2014-04-21 16:49:35 +02:00
|
|
|
conv_utf_16_to_8(tmp_16, path, maxlen);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-08 16:29:46 +10:00
|
|
|
void BLI_path_normalize_unc_16(wchar_t *path_16)
|
2014-04-21 16:49:35 +02:00
|
|
|
{
|
|
|
|
|
BLI_path_unc_to_short(path_16);
|
|
|
|
|
BLI_path_add_slash_to_share(path_16);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
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];
|
2018-06-17 16:32:54 +02: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;
|
|
|
|
|
}
|
2018-06-17 16:32:54 +02: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
|
2014-04-21 16:49:35 +02:00
|
|
|
if (BLI_strnlen(relfile, 3) > 2 && !BLI_path_is_abs(relfile)) {
|
2012-05-12 15:13:06 +00:00
|
|
|
char *ptemp;
|
2022-09-17 14:46:50 +10:00
|
|
|
/* Fix missing volume name in relative base,
|
|
|
|
|
* can happen with old `recent-files.txt` files. */
|
2020-08-26 15:50:48 +02:00
|
|
|
BLI_windows_get_default_root_dir(temp);
|
2007-02-21 20:00:03 +00:00
|
|
|
ptemp = &temp[2];
|
2022-03-09 09:35:37 +11:00
|
|
|
if (!ELEM(relfile[0], '\\', '/')) {
|
2007-02-21 20:00:03 +00:00
|
|
|
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
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2010-12-05 23:50:55 +00:00
|
|
|
if (BLI_strnlen(file, 3) > 2) {
|
2014-04-21 16:49:35 +02:00
|
|
|
bool is_unc = BLI_path_is_unc(file);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-04-21 16:49:35 +02:00
|
|
|
/* Ensure paths are both UNC paths or are both drives */
|
|
|
|
|
if (BLI_path_is_unc(temp) != is_unc) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-04-21 16:49:35 +02:00
|
|
|
/* 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]) {
|
2014-04-21 16:49:35 +02:00
|
|
|
return;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (temp[off] == '\\') {
|
2014-04-21 16:49:35 +02:00
|
|
|
slash++;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2014-04-21 16:49:35 +02:00
|
|
|
}
|
|
|
|
|
}
|
2020-02-18 20:47:43 +11:00
|
|
|
else if ((temp[1] == ':' && file[1] == ':') && (tolower(temp[0]) != tolower(file[0]))) {
|
2005-10-24 20:52:51 +00:00
|
|
|
return;
|
2014-04-21 16:49:35 +02:00
|
|
|
}
|
2005-10-24 20:52:51 +00:00
|
|
|
}
|
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
|
|
|
|
2015-06-30 15:18:03 +10:00
|
|
|
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
|
|
|
|
2008-04-28 21:29:15 +00:00
|
|
|
/* remove /./ which confuse the following slash counting... */
|
2020-04-07 12:02:21 +10:00
|
|
|
BLI_path_normalize(NULL, file);
|
|
|
|
|
BLI_path_normalize(NULL, temp);
|
2018-06-17 16:32:54 +02:00
|
|
|
|
2005-10-24 20:52:51 +00:00
|
|
|
/* the last slash in the file indicates where the path part ends */
|
2020-04-07 12:02:21 +10:00
|
|
|
lslash = BLI_path_slash_rfind(temp);
|
2005-10-24 20:52:51 +00:00
|
|
|
|
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 */
|
2014-04-27 00:20:55 +10:00
|
|
|
const char *p = temp;
|
|
|
|
|
const char *q = file;
|
2013-06-27 20:47:59 +00:00
|
|
|
char *r = res;
|
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++;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
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;
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2010-06-03 13:05:45 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-17 16:32:54 +02: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 != '/')) {
|
2019-09-08 00:12:26 +10:00
|
|
|
q--;
|
|
|
|
|
p--;
|
2012-10-21 05:46:41 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
2005-10-24 20:52:51 +00:00
|
|
|
else if (*p != '/') {
|
|
|
|
|
while ((p >= temp) && (*p != '/')) {
|
2019-09-08 00:12:26 +10:00
|
|
|
p--;
|
|
|
|
|
q--;
|
2005-10-24 20:52:51 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
}
|
|
|
|
|
|
2013-06-27 20:47:59 +00:00
|
|
|
r += BLI_strcpy_rlen(r, "//");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2005-10-24 20:52:51 +00:00
|
|
|
/* 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.
|
2012-03-03 20:19:11 +00:00
|
|
|
* 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) {
|
2013-06-27 20:47:59 +00:00
|
|
|
if (*p == '/') {
|
|
|
|
|
r += BLI_strcpy_rlen(r, "../");
|
|
|
|
|
}
|
2012-05-09 09:24:15 +00:00
|
|
|
p++;
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-06-27 20:47:59 +00:00
|
|
|
/* don't copy the slash at the beginning */
|
2020-10-26 16:31:11 +01:00
|
|
|
r += BLI_strncpy_rlen(r, q + 1, FILE_MAX - (r - res));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
#ifdef WIN32
|
2015-06-30 15:18:03 +10:00
|
|
|
BLI_str_replace_char(res + 2, '/', '\\');
|
2005-10-24 20:52:51 +00:00
|
|
|
#endif
|
|
|
|
|
strcpy(file, res);
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2014-04-16 14:25:23 -03:00
|
|
|
bool BLI_path_suffix(char *string, size_t maxlen, const char *suffix, const char *sep)
|
|
|
|
|
{
|
2018-06-17 15:35:00 +02:00
|
|
|
#ifdef DEBUG_STRSIZE
|
|
|
|
|
memset(string, 0xff, sizeof(*string) * maxlen);
|
|
|
|
|
#endif
|
2014-04-16 14:25:23 -03:00
|
|
|
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-04-17 06:17:24 +02:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (string_len + sep_len + suffix_len >= maxlen) {
|
2014-04-16 14:25:23 -03:00
|
|
|
return false;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-04-16 14:25:23 -03:00
|
|
|
for (a = string_len - 1; a >= 0; a--) {
|
|
|
|
|
if (string[a] == '.') {
|
|
|
|
|
has_extension = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
if (ELEM(string[a], '/', '\\')) {
|
2014-04-16 14:25:23 -03:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (!has_extension) {
|
2014-04-16 14:25:23 -03:00
|
|
|
a = string_len;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-04-16 14:25:23 -03:00
|
|
|
BLI_strncpy(extension, string + a, sizeof(extension));
|
|
|
|
|
sprintf(string + a, "%s%s%s", sep, suffix, extension);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
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];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-07-19 10:39:49 +00:00
|
|
|
BLI_join_dirfile(tmp, sizeof(tmp), path, parent_dir);
|
2020-04-07 12:02:21 +10:00
|
|
|
BLI_path_normalize(NULL, tmp); /* does all the work of normalizing the path for us */
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-17 16:13:24 +02:00
|
|
|
if (!BLI_path_extension_check(tmp, parent_dir)) {
|
2022-03-08 14:32:34 +11:00
|
|
|
strcpy(path, tmp); /* We assume the parent directory is always shorter. */
|
2013-03-04 19:27:51 +00:00
|
|
|
return true;
|
2012-03-24 06:18:31 +00:00
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
|
|
|
|
|
return false;
|
2008-09-22 15:37:32 +00:00
|
|
|
}
|
2008-05-04 09:41:15 +00:00
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
bool BLI_path_parent_dir_until_exists(char *dir)
|
2020-02-15 10:33:16 +11:00
|
|
|
{
|
|
|
|
|
bool valid_path = true;
|
|
|
|
|
|
|
|
|
|
/* Loop as long as cur path is not a dir, and we can get a parent path. */
|
2020-04-07 12:02:21 +10:00
|
|
|
while ((BLI_access(dir, R_OK) != 0) && (valid_path = BLI_path_parent_dir(dir))) {
|
2020-02-15 10:33:16 +11:00
|
|
|
/* pass */
|
|
|
|
|
}
|
|
|
|
|
return (valid_path && dir[0]);
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
2020-12-14 19:04:43 +11:00
|
|
|
* Looks for a sequence of "#" characters in the last slash-separated component of `path`,
|
2013-03-04 19:27:51 +00:00
|
|
|
* 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.
|
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
|
|
|
{
|
2017-10-28 17:48:45 +11:00
|
|
|
uint ch_sta, ch_end, i;
|
2008-05-04 09:41:15 +00:00
|
|
|
/* Insert current frame: file### -> file001 */
|
|
|
|
|
ch_sta = ch_end = 0;
|
|
|
|
|
for (i = 0; path[i] != '\0'; i++) {
|
2020-03-07 00:58:48 +11:00
|
|
|
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 */
|
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 */
|
2019-04-17 06:17:24 +02: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
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02: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
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
|
|
|
|
|
*char_start = -1;
|
|
|
|
|
*char_end = -1;
|
|
|
|
|
return false;
|
2010-01-26 21:41:38 +00:00
|
|
|
}
|
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
/**
|
2020-12-14 19:04:43 +11:00
|
|
|
* Ensure `path` contains at least one "#" character in its last slash-separated
|
2013-03-04 19:27:51 +00:00
|
|
|
* component, appending one digits long if not.
|
|
|
|
|
*/
|
2010-01-30 22:33:47 +00:00
|
|
|
static void ensure_digits(char *path, int digits)
|
|
|
|
|
{
|
2020-04-07 12:02:21 +10:00
|
|
|
char *file = (char *)BLI_path_slash_rfind(path);
|
2010-01-30 22:33:47 +00:00
|
|
|
|
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
|
|
|
}
|
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
|
|
|
bool BLI_path_frame(char *path, int frame, int digits)
|
2010-01-26 21:41:38 +00:00
|
|
|
{
|
|
|
|
|
int ch_sta, ch_end;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (digits) {
|
2010-01-30 22:33:47 +00:00
|
|
|
ensure_digits(path, digits);
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
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];
|
2013-07-23 12:49:30 +00:00
|
|
|
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);
|
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
|
|
|
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;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (digits) {
|
2010-01-30 22:33:47 +00:00
|
|
|
ensure_digits(path, digits);
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
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
|
|
|
|
2022-03-30 17:26:42 +11:00
|
|
|
bool BLI_path_frame_get(char *path, int *r_frame, int *r_digits_len)
|
2015-06-04 20:40:11 +02:00
|
|
|
{
|
2015-10-17 16:06:45 +11:00
|
|
|
if (*path) {
|
2020-04-07 12:02:21 +10:00
|
|
|
char *file = (char *)BLI_path_slash_rfind(path);
|
2015-06-04 20:40:11 +02:00
|
|
|
char *c;
|
2022-03-30 17:26:42 +11:00
|
|
|
int len, digits_len;
|
2015-06-04 20:40:11 +02:00
|
|
|
|
2022-03-30 17:26:42 +11:00
|
|
|
digits_len = *r_digits_len = 0;
|
2015-06-04 20:40:11 +02:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (file == NULL) {
|
2015-06-04 20:40:11 +02:00
|
|
|
file = path;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2015-06-04 20:40:11 +02: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--;
|
2022-03-30 17:26:42 +11:00
|
|
|
digits_len++;
|
2015-06-04 20:40:11 +02:00
|
|
|
}
|
|
|
|
|
|
2022-03-30 17:26:42 +11:00
|
|
|
if (digits_len) {
|
2015-06-04 20:40:11 +02:00
|
|
|
char prevchar;
|
|
|
|
|
|
|
|
|
|
c++;
|
2022-03-30 17:26:42 +11:00
|
|
|
prevchar = c[digits_len];
|
|
|
|
|
c[digits_len] = 0;
|
2015-06-04 20:40:11 +02:00
|
|
|
|
|
|
|
|
/* was the number really an extension? */
|
|
|
|
|
*r_frame = atoi(c);
|
2022-03-30 17:26:42 +11:00
|
|
|
c[digits_len] = prevchar;
|
2015-06-04 20:40:11 +02:00
|
|
|
|
2022-03-30 17:26:42 +11:00
|
|
|
*r_digits_len = digits_len;
|
2015-06-04 20:40:11 +02:00
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
Fix BLI_path_frame_strip
The `BLI_path_frame_strip` function was completely broken, unless the
number of digits in the sequence number was the same as the length of
the extension. In other words, it would work fine for `file.0001.abc` (4
digit `0001` and 4 char `.abc`), but other combinations would truncate
to the shortest (`file.001.abc` would become `file.###.ab` and
`file.00001.a` would become `file.##.a`). The dependency between the
sequence number and the file extension is now removed.
The behaviour has changed a little bit in the case where there are no
numbers in the filename. Previously, `path="filename.abc"` would result
in `path="filename.abc"` and `ext=""`, but now it results in
`path="filename"` and `ext=".abc"`. This way `ext` always contains the
extension, and the behaviour is consistent regardless of whether there
were any numbers found.
Furthermore, I've removed the `bool set_frame_char` parameter, because
it was unclear, probably also buggy, and most importantly, never used.
I've also added a unit test for the `BLI_path_frame_strip` function.
2019-03-20 12:59:11 +01:00
|
|
|
void BLI_path_frame_strip(char *path, char *r_ext)
|
2015-06-04 20:40:11 +02:00
|
|
|
{
|
2019-04-16 16:49:55 +02:00
|
|
|
*r_ext = '\0';
|
2019-03-20 13:39:27 +01:00
|
|
|
if (*path == '\0') {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
char *file = (char *)BLI_path_slash_rfind(path);
|
2019-03-20 13:39:27 +01:00
|
|
|
char *c, *suffix;
|
|
|
|
|
int len;
|
2022-03-30 17:26:42 +11:00
|
|
|
int digits_len = 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (file == NULL) {
|
2019-03-20 13:39:27 +01:00
|
|
|
file = path;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-20 13:39:27 +01:00
|
|
|
/* first get the extension part */
|
|
|
|
|
len = strlen(file);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-20 13:39:27 +01:00
|
|
|
c = file + len;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-20 13:39:27 +01:00
|
|
|
/* isolate extension */
|
|
|
|
|
while (--c != file) {
|
|
|
|
|
if (*c == '.') {
|
2015-06-04 20:40:11 +02:00
|
|
|
c--;
|
2019-03-20 13:39:27 +01:00
|
|
|
break;
|
2015-06-04 20:40:11 +02:00
|
|
|
}
|
2019-03-20 13:39:27 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-20 13:39:27 +01:00
|
|
|
suffix = c + 1;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-20 13:39:27 +01:00
|
|
|
/* find start of number */
|
|
|
|
|
while (c != (file - 1) && isdigit(*c)) {
|
|
|
|
|
c--;
|
2022-03-30 17:26:42 +11:00
|
|
|
digits_len++;
|
2019-03-20 13:39:27 +01:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-20 13:39:27 +01:00
|
|
|
c++;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-20 13:39:27 +01:00
|
|
|
int suffix_length = len - (suffix - file);
|
2019-03-21 00:58:01 +11:00
|
|
|
BLI_strncpy(r_ext, suffix, suffix_length + 1);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-20 13:39:27 +01:00
|
|
|
/* replace the number with the suffix and terminate the string */
|
2022-03-30 17:26:42 +11:00
|
|
|
while (digits_len--) {
|
2019-03-20 13:39:27 +01:00
|
|
|
*c++ = '#';
|
2015-06-04 20:40:11 +02:00
|
|
|
}
|
2019-03-20 13:39:27 +01:00
|
|
|
*c = '\0';
|
2015-06-04 20:40:11 +02:00
|
|
|
}
|
|
|
|
|
|
2013-11-25 16:59:10 +11:00
|
|
|
bool BLI_path_frame_check_chars(const char *path)
|
|
|
|
|
{
|
|
|
|
|
int ch_sta, ch_end; /* dummy args */
|
|
|
|
|
return stringframe_chars(path, &ch_sta, &ch_end);
|
|
|
|
|
}
|
|
|
|
|
|
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++;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-28 15:12:14 +02:00
|
|
|
BLI_strncpy(display_name, name + strip_offset, maxlen);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-28 15:12:14 +02:00
|
|
|
/* Replace underscores with spaces. */
|
|
|
|
|
BLI_str_replace_char(display_name, '_', ' ');
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-28 15:12:14 +02:00
|
|
|
/* Strip extension. */
|
|
|
|
|
BLI_path_extension_replace(display_name, maxlen, "");
|
2019-04-17 06:17:24 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
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]);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-08-28 15:12:14 +02:00
|
|
|
prevspace = isspace(display_name[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
|
2014-09-22 14:42:07 +10:00
|
|
|
/* without this: "" --> "C:\" */
|
|
|
|
|
if (*path == '\0') {
|
|
|
|
|
return wasrelative;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2006-09-16 11:42:37 +00:00
|
|
|
/* 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
|
2012-03-03 20:19:11 +00:00
|
|
|
* UNIX root '/' is passed.
|
|
|
|
|
*/
|
2014-04-21 16:49:35 +02:00
|
|
|
if (!wasrelative && !BLI_path_is_abs(path)) {
|
2006-09-16 11:42:37 +00:00
|
|
|
char *p = path;
|
2020-08-26 15:50:48 +02:00
|
|
|
BLI_windows_get_default_root_dir(tmp);
|
2006-08-20 14:41:13 +00:00
|
|
|
/* Get rid of the slashes at the beginning of the path. */
|
2020-03-07 00:58:48 +11:00
|
|
|
while (ELEM(*p, '\\', '/')) {
|
2006-09-16 11:42:37 +00:00
|
|
|
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));
|
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
|
|
|
|
2020-03-07 00:58:48 +11: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. */
|
2008-09-30 04:08:00 +00:00
|
|
|
}
|
2018-06-17 16:32:54 +02:00
|
|
|
|
2006-08-20 14:41:13 +00:00
|
|
|
#endif
|
|
|
|
|
|
2022-01-06 13:54:52 +11:00
|
|
|
/* NOTE(@jesterKing): 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
|
2022-01-06 13:54:52 +11:00
|
|
|
* of paths and solving some problems (and prevent potential future ones).
|
|
|
|
|
*
|
|
|
|
|
* NOTE(@elubie): For UNC paths the first characters containing the UNC prefix
|
2014-04-21 16:49:35 +02:00
|
|
|
* shouldn't be switched as we need to distinguish them from
|
2022-01-06 13:54:52 +11:00
|
|
|
* paths relative to the `.blend` file. */
|
2015-06-30 15:18:03 +10:00
|
|
|
BLI_str_replace_char(tmp + BLI_path_unc_prefix_len(tmp), '\\', '/');
|
2019-04-17 06:17:24 +02:00
|
|
|
|
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. */
|
2008-06-14 16:54:46 +00:00
|
|
|
if (wasrelative) {
|
2014-04-21 16:49:35 +02:00
|
|
|
const char *lslash;
|
|
|
|
|
BLI_strncpy(base, basepath, sizeof(base));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2014-04-21 16:49:35 +02:00
|
|
|
/* file component is ignored, so don't bother with the trailing slash */
|
2020-04-07 12:02:21 +10:00
|
|
|
BLI_path_normalize(NULL, base);
|
|
|
|
|
lslash = BLI_path_slash_rfind(base);
|
2015-06-30 15:18:03 +10:00
|
|
|
BLI_str_replace_char(base + BLI_path_unc_prefix_len(base), '\\', '/');
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
if (lslash) {
|
2019-01-15 23:15:58 +11:00
|
|
|
/* 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 */
|
2013-03-04 19:27:51 +00:00
|
|
|
BLI_strncpy(path, tmp + 2, FILE_MAX); /* strip "//" */
|
2019-04-17 06:17:24 +02: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
|
|
|
|
2005-05-20 12:18:11 +00:00
|
|
|
#ifdef WIN32
|
2021-08-04 13:26:47 +10:00
|
|
|
/* 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. */
|
2015-06-30 15:18:03 +10:00
|
|
|
BLI_str_replace_char(path + 2, '/', '\\');
|
2005-05-20 12:18:11 +00:00
|
|
|
#endif
|
2014-12-01 16:01:08 +01:00
|
|
|
|
|
|
|
|
/* ensure this is after correcting for path switch */
|
2020-04-07 12:02:21 +10:00
|
|
|
BLI_path_normalize(NULL, path);
|
2014-12-01 16:01:08 +01:00
|
|
|
|
2002-10-12 11:37:38 +00:00
|
|
|
return wasrelative;
|
|
|
|
|
}
|
|
|
|
|
|
2021-12-16 16:17:28 +11:00
|
|
|
bool BLI_path_is_abs_from_cwd(const char *path)
|
2008-09-15 01:32:53 +00:00
|
|
|
{
|
2021-12-16 16:17:28 +11:00
|
|
|
bool is_abs = false;
|
|
|
|
|
const int path_len_clamp = BLI_strnlen(path, 3);
|
2018-06-17 16:32:54 +02:00
|
|
|
|
2008-09-15 01:32:53 +00: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)) {
|
2021-12-16 16:17:28 +11:00
|
|
|
is_abs = true;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2008-09-15 01:32:53 +00:00
|
|
|
#else
|
2021-12-16 16:17:28 +11:00
|
|
|
if (path_len_clamp >= 2 && path[0] == '/') {
|
|
|
|
|
is_abs = true;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2008-09-15 01:32:53 +00:00
|
|
|
#endif
|
2021-12-16 16:17:28 +11:00
|
|
|
return is_abs;
|
|
|
|
|
}
|
2018-06-17 16:32:54 +02:00
|
|
|
|
2021-12-16 16:17:28 +11: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)) {
|
2015-10-08 15:05:58 +11:00
|
|
|
char cwd[FILE_MAX];
|
|
|
|
|
/* in case the full path to the blend isn't used */
|
|
|
|
|
if (BLI_current_working_dir(cwd, sizeof(cwd))) {
|
2011-11-26 04:07:38 +00:00
|
|
|
char origpath[FILE_MAX];
|
|
|
|
|
BLI_strncpy(origpath, path, FILE_MAX);
|
2015-10-08 15:05:58 +11:00
|
|
|
BLI_join_dirfile(path, maxlen, cwd, origpath);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
printf("Could not get the current working directory - $PWD for an unknown reason.\n");
|
2008-09-15 01:32:53 +00:00
|
|
|
}
|
2021-12-16 16:17:28 +11:00
|
|
|
return true;
|
2008-09-15 01:32:53 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-12-16 16:17:28 +11:00
|
|
|
return false;
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
2015-05-06 06:05:31 +10: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.
|
2015-05-06 06:05:31 +10:00
|
|
|
* Returns success/failure.
|
|
|
|
|
*/
|
2015-05-06 06:34:19 +10:00
|
|
|
bool BLI_path_program_extensions_add_win32(char *name, const size_t maxlen)
|
2015-05-06 06:05:31 +10:00
|
|
|
{
|
|
|
|
|
bool retval = false;
|
|
|
|
|
int type;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-05-06 06:05:31 +10:00
|
|
|
type = BLI_exists(name);
|
|
|
|
|
if ((type == 0) || S_ISDIR(type)) {
|
2015-06-04 16:48:56 +10:00
|
|
|
/* typically 3-5, ".EXE", ".BAT"... etc */
|
|
|
|
|
const int ext_max = 12;
|
2018-09-05 14:31:10 +10:00
|
|
|
const char *ext = BLI_getenv("PATHEXT");
|
2015-06-02 15:38:14 +10:00
|
|
|
if (ext) {
|
|
|
|
|
const int name_len = strlen(name);
|
2015-06-04 16:48:56 +10:00
|
|
|
char *filename = alloca(name_len + ext_max);
|
|
|
|
|
char *filename_ext;
|
2015-06-02 15:38:14 +10:00
|
|
|
const char *ext_next;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-06-02 15:38:14 +10:00
|
|
|
/* null terminated in the loop */
|
|
|
|
|
memcpy(filename, name, name_len);
|
2015-06-04 16:48:56 +10:00
|
|
|
filename_ext = filename + name_len;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-05-06 06:05:31 +10:00
|
|
|
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);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-06-04 16:48:56 +10:00
|
|
|
if (LIKELY(ext_len < ext_max)) {
|
|
|
|
|
memcpy(filename_ext, ext, ext_len);
|
|
|
|
|
filename_ext[ext_len] = '\0';
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-06-06 21:02:16 +10:00
|
|
|
type = BLI_exists(filename);
|
|
|
|
|
if (type && (!S_ISDIR(type))) {
|
2015-06-04 16:48:56 +10:00
|
|
|
retval = true;
|
|
|
|
|
BLI_strncpy(name, filename, maxlen);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-05-06 06:05:31 +10:00
|
|
|
}
|
2015-06-02 15:38:14 +10:00
|
|
|
} while ((ext = ext_next));
|
2015-05-06 06:05:31 +10:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
retval = true;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-05-06 06:05:31 +10:00
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
#endif /* WIN32 */
|
|
|
|
|
|
|
|
|
|
bool BLI_path_program_search(char *fullname, const size_t maxlen, const char *name)
|
|
|
|
|
{
|
2018-06-17 15:35:00 +02:00
|
|
|
#ifdef DEBUG_STRSIZE
|
|
|
|
|
memset(fullname, 0xff, sizeof(*fullname) * maxlen);
|
|
|
|
|
#endif
|
2015-05-06 06:05:31 +10:00
|
|
|
const char *path;
|
|
|
|
|
bool retval = false;
|
|
|
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
|
const char separator = ';';
|
|
|
|
|
#else
|
|
|
|
|
const char separator = ':';
|
|
|
|
|
#endif
|
|
|
|
|
|
2018-09-05 14:31:10 +10:00
|
|
|
path = BLI_getenv("PATH");
|
2015-05-06 06:05:31 +10:00
|
|
|
if (path) {
|
2022-08-27 14:51:21 +10:00
|
|
|
char filepath_test[FILE_MAX];
|
2015-05-06 06:05:31 +10:00
|
|
|
const char *temp;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-05-06 06:05:31 +10:00
|
|
|
do {
|
|
|
|
|
temp = strchr(path, separator);
|
|
|
|
|
if (temp) {
|
2022-08-27 14:51:21 +10:00
|
|
|
memcpy(filepath_test, path, temp - path);
|
|
|
|
|
filepath_test[temp - path] = 0;
|
2015-05-06 06:05:31 +10:00
|
|
|
path = temp + 1;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2022-08-27 14:51:21 +10:00
|
|
|
BLI_strncpy(filepath_test, path, sizeof(filepath_test));
|
2015-05-06 06:05:31 +10:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-08-27 14:51:21 +10:00
|
|
|
BLI_path_append(filepath_test, maxlen, name);
|
2015-05-06 06:05:31 +10:00
|
|
|
if (
|
|
|
|
|
#ifdef _WIN32
|
2022-08-27 14:51:21 +10:00
|
|
|
BLI_path_program_extensions_add_win32(filepath_test, maxlen)
|
2015-05-06 06:05:31 +10:00
|
|
|
#else
|
2022-08-27 14:51:21 +10:00
|
|
|
BLI_exists(filepath_test)
|
2015-05-06 06:05:31 +10:00
|
|
|
#endif
|
|
|
|
|
) {
|
2022-08-27 14:51:21 +10:00
|
|
|
BLI_strncpy(fullname, filepath_test, maxlen);
|
2015-05-06 06:05:31 +10:00
|
|
|
retval = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} while (temp);
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-05-06 06:05:31 +10:00
|
|
|
if (retval == false) {
|
|
|
|
|
*fullname = '\0';
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2015-05-06 06:05:31 +10:00
|
|
|
return retval;
|
|
|
|
|
}
|
|
|
|
|
|
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 */
|
2013-09-23 14:48:28 +00:00
|
|
|
|
2021-07-12 21:01:18 -07:00
|
|
|
#if (defined(_WIN32) || defined(_WIN64))
|
2012-03-20 02:17:37 +00:00
|
|
|
uputenv(env, val);
|
|
|
|
|
|
2009-07-21 09:26:28 +00:00
|
|
|
#else
|
2021-02-05 16:23:34 +11:00
|
|
|
/* Linux/macOS/BSD */
|
2019-03-27 13:16:10 +11:00
|
|
|
if (val) {
|
2013-09-23 14:48:28 +00:00
|
|
|
setenv(env, val, 1);
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
|
|
|
|
else {
|
2013-09-23 14:48:28 +00:00
|
|
|
unsetenv(env);
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2009-07-21 09:26:28 +00:00
|
|
|
#endif
|
|
|
|
|
}
|
2009-04-11 02:18:24 +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
|
|
|
{
|
2019-03-27 13:16:10 +11:00
|
|
|
if (BLI_getenv(env) == NULL) {
|
2009-10-19 18:44:09 +00:00
|
|
|
BLI_setenv(env, val);
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2009-10-19 18:44:09 +00:00
|
|
|
}
|
|
|
|
|
|
2018-09-05 14:46:54 +10:00
|
|
|
const char *BLI_getenv(const char *env)
|
2018-09-05 14:31:10 +10:00
|
|
|
{
|
|
|
|
|
#ifdef _MSC_VER
|
2020-12-11 11:59:14 -07:00
|
|
|
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];
|
2020-12-11 11:59:14 -07:00
|
|
|
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. */
|
2020-12-11 12:09:18 -07:00
|
|
|
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. */
|
2020-12-11 12:09:18 -07:00
|
|
|
memcpy(buffer, res_utf8, strlen(res_utf8) + 1);
|
|
|
|
|
result = (const char *)buffer;
|
|
|
|
|
}
|
2020-12-11 11:59:14 -07:00
|
|
|
free(res_utf8);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2020-12-11 11:59:14 -07:00
|
|
|
return result;
|
2018-09-05 14:31:10 +10:00
|
|
|
#else
|
|
|
|
|
return getenv(env);
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-17 12:04:38 +02:00
|
|
|
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
|
|
|
{
|
2013-03-05 06:26:10 +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 */
|
2017-04-17 12:04:38 +02:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2018-06-17 16:13:24 +02:00
|
|
|
static bool path_extension_check_ex(const char *str,
|
|
|
|
|
const size_t str_len,
|
2014-01-31 03:09:01 +11:00
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-17 16:13:24 +02:00
|
|
|
bool BLI_path_extension_check(const char *str, const char *ext)
|
2002-10-12 11:37:38 +00:00
|
|
|
{
|
2018-06-17 16:13:24 +02:00
|
|
|
return path_extension_check_ex(str, strlen(str), ext, strlen(ext));
|
2014-01-31 03:09:01 +11:00
|
|
|
}
|
|
|
|
|
|
2018-06-17 16:13:24 +02:00
|
|
|
bool BLI_path_extension_check_n(const char *str, ...)
|
2014-01-31 03:09:01 +11:00
|
|
|
{
|
|
|
|
|
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 *))) {
|
2018-06-17 16:13:24 +02:00
|
|
|
if (path_extension_check_ex(str, str_len, ext, strlen(ext))) {
|
2014-01-31 03:09:01 +11:00
|
|
|
ret = true;
|
2014-12-01 14:33:38 +01:00
|
|
|
break;
|
2014-01-31 03:09:01 +11:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
|
|
return ret;
|
2002-10-12 11:37:38 +00:00
|
|
|
}
|
|
|
|
|
|
2018-06-17 16:13:24 +02:00
|
|
|
bool BLI_path_extension_check_array(const char *str, const char **ext_array)
|
2010-08-03 12:34:42 +00:00
|
|
|
{
|
2014-01-31 03:09:01 +11:00
|
|
|
const size_t str_len = strlen(str);
|
2012-05-12 15:13:06 +00:00
|
|
|
int i = 0;
|
2014-01-31 03:09:01 +11:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
while (ext_array[i]) {
|
2018-06-17 16:13:24 +02:00
|
|
|
if (path_extension_check_ex(str, str_len, ext_array[i], strlen(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
|
|
|
}
|
|
|
|
|
|
2018-06-17 16:13:24 +02:00
|
|
|
bool BLI_path_extension_check_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];
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-03-24 06:18:31 +00:00
|
|
|
while (ext_step[0]) {
|
2014-04-27 00:20:55 +10:00
|
|
|
const char *ext_next;
|
2015-09-07 19:21:12 +02:00
|
|
|
size_t len_ext;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
if ((ext_next = strchr(ext_step, ';'))) {
|
2015-09-07 19:21:12 +02:00
|
|
|
len_ext = ext_next - ext_step + 1;
|
2015-09-09 11:02:32 +02:00
|
|
|
BLI_strncpy(pattern, ext_step, (len_ext > sizeof(pattern)) ? sizeof(pattern) : len_ext);
|
2010-09-24 06:20:43 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2015-09-09 11:02:32 +02:00
|
|
|
len_ext = BLI_strncpy_rlen(pattern, ext_step, sizeof(pattern));
|
2010-09-24 06:20:43 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
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;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-04 18:36:37 +00:00
|
|
|
return false;
|
2010-09-24 06:20:43 +00:00
|
|
|
}
|
|
|
|
|
|
2018-06-18 12:26:47 +02:00
|
|
|
bool BLI_path_extension_glob_validate(char *ext_fnmatch)
|
|
|
|
|
{
|
|
|
|
|
bool only_wildcards = false;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2018-06-18 12:26:47 +02:00
|
|
|
for (size_t i = strlen(ext_fnmatch); i-- > 0;) {
|
|
|
|
|
if (ext_fnmatch[i] == ';') {
|
2019-01-15 23:15:58 +11:00
|
|
|
/* Group separator, we truncate here if we only had wildcards so far.
|
|
|
|
|
* Otherwise, all is sound and fine. */
|
2018-06-18 12:26:47 +02:00
|
|
|
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;
|
|
|
|
|
}
|
2019-01-15 23:15:58 +11:00
|
|
|
/* 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. */
|
2018-06-18 12:26:47 +02:00
|
|
|
return false;
|
|
|
|
|
}
|
2010-09-24 06:20:43 +00:00
|
|
|
|
2018-06-17 16:13:24 +02:00
|
|
|
bool BLI_path_extension_replace(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
|
|
|
{
|
2018-06-17 15:35:00 +02:00
|
|
|
#ifdef DEBUG_STRSIZE
|
|
|
|
|
memset(path, 0xff, sizeof(*path) * maxlen);
|
|
|
|
|
#endif
|
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;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2012-05-12 15:13:06 +00:00
|
|
|
for (a = path_len - 1; a >= 0; a--) {
|
2014-07-20 01:30:29 +10:00
|
|
|
if (ELEM(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
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02: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
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (a + ext_len >= maxlen) {
|
2013-03-05 03:17:46 +00:00
|
|
|
return false;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02: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
|
|
|
}
|
|
|
|
|
|
2018-06-17 16:13:24 +02:00
|
|
|
bool BLI_path_extension_ensure(char *path, size_t maxlen, const char *ext)
|
2011-10-31 00:23:42 +00:00
|
|
|
{
|
2018-06-17 15:35:00 +02:00
|
|
|
#ifdef DEBUG_STRSIZE
|
|
|
|
|
memset(path, 0xff, sizeof(*path) * maxlen);
|
|
|
|
|
#endif
|
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;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2013-03-05 03:17:46 +00:00
|
|
|
/* first check the extension is already there */
|
2015-01-26 16:03:11 +01:00
|
|
|
if ((ext_len <= path_len) && (STREQ(path + (path_len - ext_len), ext))) {
|
2013-03-05 03:17:46 +00:00
|
|
|
return true;
|
2011-10-31 00:23:42 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02: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++;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (a + ext_len >= maxlen) {
|
2013-03-05 03:17:46 +00:00
|
|
|
return false;
|
2019-03-27 13:16:10 +11:00
|
|
|
}
|
2019-04-17 06:17:24 +02: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
|
|
|
}
|
|
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
bool BLI_path_filename_ensure(char *filepath, size_t maxlen, const char *filename)
|
2013-08-29 05:34:58 +00:00
|
|
|
{
|
2018-06-17 15:35:00 +02:00
|
|
|
#ifdef DEBUG_STRSIZE
|
|
|
|
|
memset(filepath, 0xff, sizeof(*filepath) * maxlen);
|
|
|
|
|
#endif
|
2020-04-07 12:02:21 +10:00
|
|
|
char *c = (char *)BLI_path_slash_rfind(filepath);
|
2013-08-29 05:34:58 +00:00
|
|
|
if (!c || ((c - filepath) < maxlen - (strlen(filename) + 1))) {
|
|
|
|
|
strcpy(c ? &c[1] : filepath, filename);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
{
|
2018-06-17 15:35:00 +02:00
|
|
|
#ifdef DEBUG_STRSIZE
|
|
|
|
|
memset(dir, 0xff, sizeof(*dir) * dirlen);
|
|
|
|
|
memset(file, 0xff, sizeof(*file) * filelen);
|
|
|
|
|
#endif
|
2020-04-07 12:02:21 +10:00
|
|
|
const char *lslash_str = BLI_path_slash_rfind(string);
|
2013-03-05 03:17:46 +00:00
|
|
|
const size_t lslash = lslash_str ? (size_t)(lslash_str - string) + 1 : 0;
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2008-04-26 13:08:57 +00:00
|
|
|
if (dir) {
|
|
|
|
|
if (lslash) {
|
2019-01-15 23:15:58 +11:00
|
|
|
/* +1 to include the slash and the last char */
|
|
|
|
|
BLI_strncpy(dir, string, MIN2(dirlen, lslash + 1));
|
2011-10-15 03:56:05 +00:00
|
|
|
}
|
|
|
|
|
else {
|
2008-04-26 13:08:57 +00:00
|
|
|
dir[0] = '\0';
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2008-04-26 13:08:57 +00:00
|
|
|
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
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BLI_split_file_part(const char *string, char *file, const size_t filelen)
|
|
|
|
|
{
|
|
|
|
|
BLI_split_dirfile(string, NULL, file, 0, filelen);
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-13 15:35:48 +02:00
|
|
|
const char *BLI_path_extension(const char *filepath)
|
|
|
|
|
{
|
|
|
|
|
const char *extension = strrchr(filepath, '.');
|
|
|
|
|
if (extension == NULL) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2020-04-07 12:02:21 +10:00
|
|
|
if (BLI_path_slash_find(extension) != NULL) {
|
2019-08-13 15:35:48 +02:00
|
|
|
/* 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;
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-02 11:19:21 +00:00
|
|
|
void BLI_path_append(char *__restrict dst, const size_t maxlen, const char *__restrict file)
|
2013-07-24 21:25:06 +00:00
|
|
|
{
|
|
|
|
|
size_t dirlen = BLI_strnlen(dst, maxlen);
|
|
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
/* inline BLI_path_slash_ensure */
|
2013-07-24 21:25:06 +00:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2013-09-02 11:19:21 +00:00
|
|
|
void BLI_join_dirfile(char *__restrict dst,
|
|
|
|
|
const size_t maxlen,
|
|
|
|
|
const char *__restrict dir,
|
|
|
|
|
const char *__restrict file)
|
2007-02-28 21:37:14 +00:00
|
|
|
{
|
2018-06-17 15:35:00 +02:00
|
|
|
#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);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-11-01 12:38:12 +11:00
|
|
|
/* Arguments can't match. */
|
2013-07-24 21:25:06 +00:00
|
|
|
BLI_assert(!ELEM(dst, dir, file));
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2021-11-01 12:38:12 +11:00
|
|
|
/* 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);
|
|
|
|
|
|
2013-07-24 21:25:06 +00:00
|
|
|
if (dirlen == maxlen) {
|
|
|
|
|
memcpy(dst, dir, dirlen);
|
|
|
|
|
dst[dirlen - 1] = '\0';
|
|
|
|
|
return; /* dir fills the path */
|
|
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
|
|
|
|
|
memcpy(dst, dir, dirlen + 1);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-10-12 11:18:46 +00:00
|
|
|
if (dirlen + 1 >= maxlen) {
|
|
|
|
|
return; /* fills the path */
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
/* inline BLI_path_slash_ensure */
|
2015-11-27 21:12:42 +01:00
|
|
|
if ((dirlen > 0) && !ELEM(dst[dirlen - 1], SEP, ALTSEP)) {
|
2012-03-12 06:53:47 +00:00
|
|
|
dst[dirlen++] = SEP;
|
2012-05-12 15:13:06 +00:00
|
|
|
dst[dirlen] = '\0';
|
2007-02-28 21:37:14 +00:00
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-10-12 11:18:46 +00:00
|
|
|
if (dirlen >= maxlen) {
|
|
|
|
|
return; /* fills the path */
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2011-10-12 11:18:46 +00:00
|
|
|
BLI_strncpy(dst + dirlen, file, maxlen - dirlen);
|
2007-02-28 21:37:14 +00:00
|
|
|
}
|
|
|
|
|
|
2017-03-24 17:29:48 +11:00
|
|
|
size_t BLI_path_join(char *__restrict dst, const size_t dst_len, const char *path, ...)
|
|
|
|
|
{
|
2018-06-17 15:35:00 +02:00
|
|
|
#ifdef DEBUG_STRSIZE
|
|
|
|
|
memset(dst, 0xff, sizeof(*dst) * dst_len);
|
|
|
|
|
#endif
|
2017-03-24 17:29:48 +11:00
|
|
|
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);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-03-24 17:29:48 +11:00
|
|
|
if (ofs == dst_last) {
|
|
|
|
|
return ofs;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2022-09-12 10:39:24 +10:00
|
|
|
/* Remove trailing slashes, unless there are *only* trailing slashes
|
|
|
|
|
* (allow `//` or `//some_path` as the first argument). */
|
2017-03-24 17:29:48 +11:00
|
|
|
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');
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-03-24 17:29:48 +11:00
|
|
|
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;
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-03-24 17:29:48 +11:00
|
|
|
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);
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-03-24 17:29:48 +11:00
|
|
|
if (has_trailing_slash) {
|
|
|
|
|
if ((ofs != dst_last) && (ofs != 0) && (ELEM(dst[ofs - 1], SEP, ALTSEP) == 0)) {
|
|
|
|
|
dst[ofs++] = SEP;
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-03-24 17:29:48 +11:00
|
|
|
BLI_assert(ofs <= dst_last);
|
|
|
|
|
dst[ofs] = '\0';
|
2019-04-17 06:17:24 +02:00
|
|
|
|
2017-03-24 17:29:48 +11:00
|
|
|
return ofs;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-04 19:27:51 +00:00
|
|
|
const char *BLI_path_basename(const char *path)
|
2010-06-05 21:19:59 +00:00
|
|
|
{
|
2020-04-07 12:02:21 +10:00
|
|
|
const char *const filename = BLI_path_slash_rfind(path);
|
2010-06-05 21:19:59 +00:00
|
|
|
return filename ? filename + 1 : path;
|
|
|
|
|
}
|
2010-02-26 11:50:59 +00:00
|
|
|
|
2019-05-25 13:22:25 -06:00
|
|
|
bool BLI_path_name_at_index(const char *__restrict path,
|
|
|
|
|
const int index,
|
|
|
|
|
int *__restrict r_offset,
|
|
|
|
|
int *__restrict r_len)
|
2017-03-22 19:31:34 +11:00
|
|
|
{
|
|
|
|
|
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;
|
2021-08-03 15:27:20 +10:00
|
|
|
// printf("!!! %d %d\n", start, end);
|
2017-03-22 19:31:34 +11:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
index_step += 1;
|
|
|
|
|
}
|
|
|
|
|
if (c == '\0') {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
prev = i;
|
|
|
|
|
}
|
|
|
|
|
i += 1;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
|
|
|
|
|
/* 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;
|
2017-03-22 19:31:34 +11:00
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
index_step -= 1;
|
2017-03-22 19:31:34 +11:00
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
if (c == '\0') {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
prev = i;
|
2017-03-22 19:31:34 +11:00
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
i -= 1;
|
2017-03-22 19:31:34 +11:00
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
return false;
|
2017-03-22 19:31:34 +11:00
|
|
|
}
|
|
|
|
|
|
2021-09-27 15:22:53 +02:00
|
|
|
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
|
2022-01-24 14:29:19 +11:00
|
|
|
* longer than PATH_MAX and the result is ill-defined. */
|
2021-09-27 15:22:53 +02:00
|
|
|
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);
|
|
|
|
|
|
2021-09-28 11:05:45 +02:00
|
|
|
#ifdef WIN32
|
|
|
|
|
BLI_str_tolower_ascii(container_native, PATH_MAX);
|
|
|
|
|
BLI_str_tolower_ascii(containee_native, PATH_MAX);
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-09-27 15:22:53 +02:00
|
|
|
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". */
|
2021-09-27 15:22:53 +02:00
|
|
|
BLI_path_slash_ensure(container_native);
|
|
|
|
|
|
|
|
|
|
return BLI_str_startswith(containee_native, container_native);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
const char *BLI_path_slash_find(const char *string)
|
2011-09-28 05:53:40 +00:00
|
|
|
{
|
2014-04-27 00:20:55 +10:00
|
|
|
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;
|
|
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
if (!fbslash) {
|
2019-03-27 13:16:10 +11:00
|
|
|
return ffslash;
|
|
|
|
|
}
|
2018-06-17 16:32:54 +02:00
|
|
|
|
2016-05-03 18:16:06 +10:00
|
|
|
return (ffslash < fbslash) ? ffslash : fbslash;
|
2010-10-27 06:41:48 +00:00
|
|
|
}
|
|
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
const char *BLI_path_slash_rfind(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
|
|
|
|
2019-03-27 13:16:10 +11:00
|
|
|
if (!lfslash) {
|
|
|
|
|
return lbslash;
|
|
|
|
|
}
|
2020-08-07 11:23:02 +02:00
|
|
|
if (!lbslash) {
|
2019-03-27 13:16:10 +11:00
|
|
|
return lfslash;
|
|
|
|
|
}
|
2018-06-17 16:32:54 +02:00
|
|
|
|
2016-05-03 18:16:06 +10:00
|
|
|
return (lfslash > lbslash) ? lfslash : lbslash;
|
2010-10-27 06:41:48 +00:00
|
|
|
}
|
|
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
int BLI_path_slash_ensure(char *string)
|
2011-09-28 05:53:40 +00:00
|
|
|
{
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
void BLI_path_slash_rstrip(char *string)
|
2011-09-28 05:53:40 +00:00
|
|
|
{
|
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
|
|
|
|
2020-04-07 12:02:21 +10:00
|
|
|
void BLI_path_slash_native(char *path)
|
2014-07-31 01:40:05 +10:00
|
|
|
{
|
|
|
|
|
#ifdef WIN32
|
|
|
|
|
if (path && BLI_strnlen(path, 3) > 2) {
|
2021-06-10 18:35:35 +02:00
|
|
|
BLI_str_replace_char(path + 2, ALTSEP, SEP);
|
2014-07-31 01:40:05 +10:00
|
|
|
}
|
|
|
|
|
#else
|
2021-06-10 18:35:35 +02:00
|
|
|
BLI_str_replace_char(path + BLI_path_unc_prefix_len(path), ALTSEP, SEP);
|
2014-07-31 01:40:05 +10:00
|
|
|
#endif
|
|
|
|
|
}
|
USD Preview Surface material export.
Add `USD Preview Surface From Nodes` export option, to convert a
Principled BSDF material node network to an approximate USD Preview
Surface shader representation. If this option is disabled, the original
material export behavior is maintained, where viewport setting are saved
to the Preview Surface shader.
Also added the following options for texture export.
- `Export Textures`: If converting Preview Surface, export textures
referenced by shader nodes to a 'textures' directory which is a
sibling of the USD file.
- `Overwrite Textures`: Allow overwriting existing texture files when
exporting textures (this option is off by default).
- `Relative Texture Paths`: Make texture asset paths relative to the
USD.
The entry point for the new functionality is
`create_usd_preview_surface_material()`, called from
`USDAbstractWriter::ensure_usd_material()`. The material conversion
currently handles a small subset of Blender shading nodes,
`BSDF_DIFFUSE`, `BSDF_PRINCIPLED`, `TEX_IMAGE` and `UVMAP`.
Texture export is handled by copying texture files from their original
location to a `textures` folder in the same directory as the USD.
In-memory and packed textures are saved directly to the textures folder.
This patch is based, in part, on code in Tangent Animation's USD
exporter branch.
Reviewed By: sybren, HooglyBoogly
Differential Revision: https://developer.blender.org/D13647
2022-01-27 15:43:14 +01:00
|
|
|
|
2022-01-28 13:59:06 +11:00
|
|
|
int BLI_path_cmp_normalized(const char *p1, const char *p2)
|
USD Preview Surface material export.
Add `USD Preview Surface From Nodes` export option, to convert a
Principled BSDF material node network to an approximate USD Preview
Surface shader representation. If this option is disabled, the original
material export behavior is maintained, where viewport setting are saved
to the Preview Surface shader.
Also added the following options for texture export.
- `Export Textures`: If converting Preview Surface, export textures
referenced by shader nodes to a 'textures' directory which is a
sibling of the USD file.
- `Overwrite Textures`: Allow overwriting existing texture files when
exporting textures (this option is off by default).
- `Relative Texture Paths`: Make texture asset paths relative to the
USD.
The entry point for the new functionality is
`create_usd_preview_surface_material()`, called from
`USDAbstractWriter::ensure_usd_material()`. The material conversion
currently handles a small subset of Blender shading nodes,
`BSDF_DIFFUSE`, `BSDF_PRINCIPLED`, `TEX_IMAGE` and `UVMAP`.
Texture export is handled by copying texture files from their original
location to a `textures` folder in the same directory as the USD.
In-memory and packed textures are saved directly to the textures folder.
This patch is based, in part, on code in Tangent Animation's USD
exporter branch.
Reviewed By: sybren, HooglyBoogly
Differential Revision: https://developer.blender.org/D13647
2022-01-27 15:43:14 +01:00
|
|
|
{
|
2022-01-28 15:02:13 +11:00
|
|
|
BLI_assert_msg(!BLI_path_is_rel(p1) && !BLI_path_is_rel(p2), "Paths arguments must be absolute");
|
|
|
|
|
|
USD Preview Surface material export.
Add `USD Preview Surface From Nodes` export option, to convert a
Principled BSDF material node network to an approximate USD Preview
Surface shader representation. If this option is disabled, the original
material export behavior is maintained, where viewport setting are saved
to the Preview Surface shader.
Also added the following options for texture export.
- `Export Textures`: If converting Preview Surface, export textures
referenced by shader nodes to a 'textures' directory which is a
sibling of the USD file.
- `Overwrite Textures`: Allow overwriting existing texture files when
exporting textures (this option is off by default).
- `Relative Texture Paths`: Make texture asset paths relative to the
USD.
The entry point for the new functionality is
`create_usd_preview_surface_material()`, called from
`USDAbstractWriter::ensure_usd_material()`. The material conversion
currently handles a small subset of Blender shading nodes,
`BSDF_DIFFUSE`, `BSDF_PRINCIPLED`, `TEX_IMAGE` and `UVMAP`.
Texture export is handled by copying texture files from their original
location to a `textures` folder in the same directory as the USD.
In-memory and packed textures are saved directly to the textures folder.
This patch is based, in part, on code in Tangent Animation's USD
exporter branch.
Reviewed By: sybren, HooglyBoogly
Differential Revision: https://developer.blender.org/D13647
2022-01-27 15:43:14 +01:00
|
|
|
/* Normalize the paths so we can compare them. */
|
|
|
|
|
char norm_p1[FILE_MAX];
|
|
|
|
|
char norm_p2[FILE_MAX];
|
|
|
|
|
|
|
|
|
|
BLI_strncpy(norm_p1, p1, sizeof(norm_p1));
|
|
|
|
|
BLI_strncpy(norm_p2, p2, sizeof(norm_p2));
|
|
|
|
|
|
|
|
|
|
BLI_path_slash_native(norm_p1);
|
|
|
|
|
BLI_path_slash_native(norm_p2);
|
|
|
|
|
|
|
|
|
|
BLI_path_normalize(NULL, norm_p1);
|
|
|
|
|
BLI_path_normalize(NULL, norm_p2);
|
|
|
|
|
|
2022-01-28 13:59:06 +11:00
|
|
|
return BLI_path_cmp(norm_p1, norm_p2);
|
USD Preview Surface material export.
Add `USD Preview Surface From Nodes` export option, to convert a
Principled BSDF material node network to an approximate USD Preview
Surface shader representation. If this option is disabled, the original
material export behavior is maintained, where viewport setting are saved
to the Preview Surface shader.
Also added the following options for texture export.
- `Export Textures`: If converting Preview Surface, export textures
referenced by shader nodes to a 'textures' directory which is a
sibling of the USD file.
- `Overwrite Textures`: Allow overwriting existing texture files when
exporting textures (this option is off by default).
- `Relative Texture Paths`: Make texture asset paths relative to the
USD.
The entry point for the new functionality is
`create_usd_preview_surface_material()`, called from
`USDAbstractWriter::ensure_usd_material()`. The material conversion
currently handles a small subset of Blender shading nodes,
`BSDF_DIFFUSE`, `BSDF_PRINCIPLED`, `TEX_IMAGE` and `UVMAP`.
Texture export is handled by copying texture files from their original
location to a `textures` folder in the same directory as the USD.
In-memory and packed textures are saved directly to the textures folder.
This patch is based, in part, on code in Tangent Animation's USD
exporter branch.
Reviewed By: sybren, HooglyBoogly
Differential Revision: https://developer.blender.org/D13647
2022-01-27 15:43:14 +01:00
|
|
|
}
|