diff options
-rw-r--r-- | source/blender/blenlib/BLI_path_util.h | 3 | ||||
-rw-r--r-- | source/blender/blenlib/intern/path_util.c | 65 | ||||
-rw-r--r-- | tests/gtests/blenlib/BLI_path_util_test.cc | 133 |
3 files changed, 201 insertions, 0 deletions
diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h index baa1f792018..7d971fbfc3d 100644 --- a/source/blender/blenlib/BLI_path_util.h +++ b/source/blender/blenlib/BLI_path_util.h @@ -61,6 +61,9 @@ void BLI_path_append(char *__restrict dst, const size_t maxlen, void BLI_join_dirfile(char *__restrict string, const size_t maxlen, const char *__restrict dir, const char *__restrict file) ATTR_NONNULL(); const char *BLI_path_basename(const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; +bool BLI_path_name_at_index( + const char *__restrict path, const int index, + int *__restrict r_offset, int *__restrict r_len) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT; #if 0 typedef enum bli_rebase_state { diff --git a/source/blender/blenlib/intern/path_util.c b/source/blender/blenlib/intern/path_util.c index 6644e6605a1..83aa01d3d15 100644 --- a/source/blender/blenlib/intern/path_util.c +++ b/source/blender/blenlib/intern/path_util.c @@ -1626,6 +1626,71 @@ const char *BLI_path_basename(const char *path) return filename ? filename + 1 : path; } +/** + * Get an element of the path at an index, eg: + * "/some/path/file.txt" where an index of... + * - 0 or -3: "some" + * - 1 or -2: "path" + * - 2 or -1: "file.txt" + * + * Ignores multiple slashes at any point in the path (including start/end). + */ +bool BLI_path_name_at_index(const char *path, const int index, int *r_offset, int *r_len) +{ + if (index >= 0) { + int index_step = 0; + int prev = -1; + int i = 0; + while (true) { + const char c = path[i]; + if (ELEM(c, SEP, ALTSEP, '\0')) { + if (prev + 1 != i) { + prev += 1; + if (index_step == index) { + *r_offset = prev; + *r_len = i - prev; + /* printf("!!! %d %d\n", start, end); */ + return true; + } + index_step += 1; + } + if (c == '\0') { + break; + } + prev = i; + } + i += 1; + } + return false; + } + else { + /* negative number, reverse where -1 is the last element */ + int index_step = -1; + int prev = strlen(path); + int i = prev - 1; + while (true) { + const char c = i >= 0 ? path[i] : '\0'; + if (ELEM(c, SEP, ALTSEP, '\0')) { + if (prev - 1 != i) { + i += 1; + if (index_step == index) { + *r_offset = i; + *r_len = prev - i; + return true; + } + index_step -= 1; + } + if (c == '\0') { + break; + } + prev = i; + } + i -= 1; + } + return false; + } +} + /* UNUSED */ #if 0 /** diff --git a/tests/gtests/blenlib/BLI_path_util_test.cc b/tests/gtests/blenlib/BLI_path_util_test.cc index d017ab18b4d..ccfa59503c2 100644 --- a/tests/gtests/blenlib/BLI_path_util_test.cc +++ b/tests/gtests/blenlib/BLI_path_util_test.cc @@ -113,6 +113,139 @@ TEST(path_util, PathUtilClean) } #endif + +#define AT_INDEX(str_input, index_input, str_expect) \ + { \ + char path[] = str_input; \ + const char *expect = str_expect; \ + int index_output, len_output; \ + const bool ret = BLI_path_name_at_index(path, index_input, &index_output, &len_output); \ + if (expect == NULL) { \ + EXPECT_EQ(ret, false); \ + } \ + else { \ + EXPECT_EQ(ret, true); \ + EXPECT_EQ(strlen(expect), len_output); \ + path[index_output + len_output] = '\0'; \ + EXPECT_STREQ(expect, &path[index_output]); \ + } \ + }((void)0) + +/* BLI_path_name_at_index */ +TEST(path_util, NameAtIndex_Single) +{ + AT_INDEX("/a", 0, "a"); + AT_INDEX("/a/", 0, "a"); + AT_INDEX("a/", 0, "a"); + AT_INDEX("//a//", 0, "a"); + AT_INDEX("a/b", 0, "a"); + + AT_INDEX("/a", 1, NULL); + AT_INDEX("/a/", 1, NULL); + AT_INDEX("a/", 1, NULL); + AT_INDEX("//a//", 1, NULL); +} +TEST(path_util, NameAtIndex_SingleNeg) +{ + AT_INDEX("/a", -1, "a"); + AT_INDEX("/a/", -1, "a"); + AT_INDEX("a/", -1, "a"); + AT_INDEX("//a//", -1, "a"); + AT_INDEX("a/b", -1, "b"); + + AT_INDEX("/a", -2, NULL); + AT_INDEX("/a/", -2, NULL); + AT_INDEX("a/", -2, NULL); + AT_INDEX("//a//", -2, NULL); +} + +TEST(path_util, NameAtIndex_Double) +{ + AT_INDEX("/ab", 0, "ab"); + AT_INDEX("/ab/", 0, "ab"); + AT_INDEX("ab/", 0, "ab"); + AT_INDEX("//ab//", 0, "ab"); + AT_INDEX("ab/c", 0, "ab"); + + AT_INDEX("/ab", 1, NULL); + AT_INDEX("/ab/", 1, NULL); + AT_INDEX("ab/", 1, NULL); + AT_INDEX("//ab//", 1, NULL); +} + +TEST(path_util, NameAtIndex_DoublNeg) +{ + AT_INDEX("/ab", -1, "ab"); + AT_INDEX("/ab/", -1, "ab"); + AT_INDEX("ab/", -1, "ab"); + AT_INDEX("//ab//", -1, "ab"); + AT_INDEX("ab/c", -1, "c"); + + AT_INDEX("/ab", -2, NULL); + AT_INDEX("/ab/", -2, NULL); + AT_INDEX("ab/", -2, NULL); + AT_INDEX("//ab//", -2, NULL); +} + +TEST(path_util, NameAtIndex_Misc) +{ + AT_INDEX("/how/now/brown/cow", 0, "how"); + AT_INDEX("/how/now/brown/cow", 1, "now"); + AT_INDEX("/how/now/brown/cow", 2, "brown"); + AT_INDEX("/how/now/brown/cow", 3, "cow"); + AT_INDEX("/how/now/brown/cow", 4, NULL); + AT_INDEX("/how/now/brown/cow/", 4, NULL); +} + +TEST(path_util, NameAtIndex_MiscNeg) +{ + AT_INDEX("/how/now/brown/cow", 0, "how"); + AT_INDEX("/how/now/brown/cow", 1, "now"); + AT_INDEX("/how/now/brown/cow", 2, "brown"); + AT_INDEX("/how/now/brown/cow", 3, "cow"); + AT_INDEX("/how/now/brown/cow", 4, NULL); + AT_INDEX("/how/now/brown/cow/", 4, NULL); +} + +TEST(path_util, NameAtIndex_MiscComplex) +{ + AT_INDEX("how//now/brown/cow", 0, "how"); + AT_INDEX("//how///now\\/brown/cow", 1, "now"); + AT_INDEX("/how/now\\//brown\\/cow", 2, "brown"); + AT_INDEX("/how/now/brown/cow//\\", 3, "cow"); + AT_INDEX("/how/now/brown/\\cow", 4, NULL); + AT_INDEX("how/now/brown/\\cow\\", 4, NULL); +} + +TEST(path_util, NameAtIndex_MiscComplexNeg) +{ + AT_INDEX("how//now/brown/cow", -4, "how"); + AT_INDEX("//how///now\\/brown/cow", -3, "now"); + AT_INDEX("/how/now\\//brown\\/cow", -2, "brown"); + AT_INDEX("/how/now/brown/cow//\\", -1, "cow"); + AT_INDEX("/how/now/brown/\\cow", -5, NULL); + AT_INDEX("how/now/brown/\\cow\\", -5, NULL); +} + +TEST(path_util, NameAtIndex_NoneComplex) +{ + AT_INDEX("", 0, NULL); + AT_INDEX("/", 0, NULL); + AT_INDEX("//", 0, NULL); + AT_INDEX("///", 0, NULL); +} + +TEST(path_util, NameAtIndex_NoneComplexNeg) +{ + AT_INDEX("", -1, NULL); + AT_INDEX("/", -1, NULL); + AT_INDEX("//", -1, NULL); + AT_INDEX("///", -1, NULL); +} + +#undef AT_INDEX + + /* BLI_path_frame */ TEST(path_util, PathUtilFrame) { |