BLI_path: skip "/./" and "/." in BLI_path_name_at_index

This avoids having to run BLI_path_normalize before calling
BLI_path_name_at_index.
This commit is contained in:
Campbell Barton 2022-11-03 15:28:01 +11:00
parent 31d43cb0e7
commit 65e4d169ba
3 changed files with 108 additions and 27 deletions

View File

@ -202,7 +202,10 @@ const char *BLI_path_basename(const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_
* - 1 or -2: `path`
* - 2 or -1: `file.txt`
*
* Ignores multiple slashes at any point in the path (including start/end).
* Ignored elements in the path:
* - Multiple slashes at any point in the path (including start/end).
* - Single '.' in the path: `/./` except for the beginning of the path
* where it's used to signify a $PWD relative path.
*/
bool BLI_path_name_at_index(const char *__restrict path,
int index,

View File

@ -1597,39 +1597,47 @@ const char *BLI_path_basename(const char *path)
return filename ? filename + 1 : path;
}
bool BLI_path_name_at_index(const char *__restrict path,
const int index,
int *__restrict r_offset,
int *__restrict r_len)
static bool path_name_at_index_forward(const char *__restrict path,
const int index,
int *__restrict r_offset,
int *__restrict r_len)
{
if (index >= 0) {
int index_step = 0;
int prev = -1;
int i = 0;
while (true) {
const char c = path[i];
if (ELEM(c, SEP, '\0')) {
if (prev + 1 != i) {
prev += 1;
BLI_assert(index >= 0);
int index_step = 0;
int prev = -1;
int i = 0;
while (true) {
const char c = path[i];
if (ELEM(c, SEP, '\0')) {
if (prev + 1 != i) {
prev += 1;
/* Skip '/./' (behave as if they don't exist). */
if (!((i - prev == 1) && (prev != 0) && (path[prev] == '.'))) {
if (index_step == index) {
*r_offset = prev;
*r_len = i - prev;
// printf("!!! %d %d\n", start, end);
return true;
}
index_step += 1;
}
if (c == '\0') {
break;
}
prev = i;
}
i += 1;
if (c == '\0') {
break;
}
prev = i;
}
return false;
i += 1;
}
return false;
}
/* negative number, reverse where -1 is the last element */
static bool path_name_at_index_backward(const char *__restrict path,
const int index,
int *__restrict r_offset,
int *__restrict r_len)
{
/* Negative number, reverse where -1 is the last element. */
BLI_assert(index < 0);
int index_step = -1;
int prev = strlen(path);
int i = prev - 1;
@ -1638,12 +1646,15 @@ bool BLI_path_name_at_index(const char *__restrict path,
if (ELEM(c, SEP, '\0')) {
if (prev - 1 != i) {
i += 1;
if (index_step == index) {
*r_offset = i;
*r_len = prev - i;
return true;
/* Skip '/./' (behave as if they don't exist). */
if (!((prev - i == 1) && (i != 0) && (path[i] == '.'))) {
if (index_step == index) {
*r_offset = i;
*r_len = prev - i;
return true;
}
index_step -= 1;
}
index_step -= 1;
}
if (c == '\0') {
break;
@ -1655,6 +1666,15 @@ bool BLI_path_name_at_index(const char *__restrict path,
return false;
}
bool BLI_path_name_at_index(const char *__restrict path,
const int index,
int *__restrict r_offset,
int *__restrict r_len)
{
return (index >= 0) ? path_name_at_index_forward(path, index, r_offset, r_len) :
path_name_at_index_backward(path, index, r_offset, r_len);
}
bool BLI_path_contains(const char *container_path, const char *containee_path)
{
char container_native[PATH_MAX];

View File

@ -259,6 +259,64 @@ TEST(path_util, NameAtIndex_MiscNeg)
AT_INDEX("/how/now/brown/cow/", 4, nullptr);
}
#define TEST_STR "./a/./b/./c/."
TEST(path_util, NameAtIndex_SingleDot)
{
AT_INDEX(TEST_STR, 0, ".");
AT_INDEX(TEST_STR, 1, "a");
AT_INDEX(TEST_STR, 2, "b");
AT_INDEX(TEST_STR, 3, "c");
AT_INDEX(TEST_STR, 4, nullptr);
}
TEST(path_util, NameAtIndex_SingleDotNeg)
{
AT_INDEX(TEST_STR, -5, nullptr);
AT_INDEX(TEST_STR, -4, ".");
AT_INDEX(TEST_STR, -3, "a");
AT_INDEX(TEST_STR, -2, "b");
AT_INDEX(TEST_STR, -1, "c");
}
#undef TEST_STR
#define TEST_STR ".//a//.//b//.//c//.//"
TEST(path_util, NameAtIndex_SingleDotDoubleSlash)
{
AT_INDEX(TEST_STR, 0, ".");
AT_INDEX(TEST_STR, 1, "a");
AT_INDEX(TEST_STR, 2, "b");
AT_INDEX(TEST_STR, 3, "c");
AT_INDEX(TEST_STR, 4, nullptr);
}
TEST(path_util, NameAtIndex_SingleDotDoubleSlashNeg)
{
AT_INDEX(TEST_STR, -5, nullptr);
AT_INDEX(TEST_STR, -4, ".");
AT_INDEX(TEST_STR, -3, "a");
AT_INDEX(TEST_STR, -2, "b");
AT_INDEX(TEST_STR, -1, "c");
}
#undef TEST_STR
TEST(path_util, NameAtIndex_SingleDotSeries)
{
AT_INDEX("abc/././/././xyz", 0, "abc");
AT_INDEX("abc/././/././xyz", 1, "xyz");
AT_INDEX("abc/././/././xyz", 2, nullptr);
}
TEST(path_util, NameAtIndex_SingleDotSeriesNeg)
{
AT_INDEX("abc/././/././xyz", -3, nullptr);
AT_INDEX("abc/././/././xyz", -2, "abc");
AT_INDEX("abc/././/././xyz", -1, "xyz");
}
TEST(path_util, NameAtIndex_MiscComplex)
{
AT_INDEX("how//now/brown/cow", 0, "how");