[Bf-blender-cvs] [d0253b2ea4] master: BLI_path_util: add BLI_path_name_at_index

Campbell Barton noreply at git.blender.org
Wed Mar 22 09:37:16 CET 2017


Commit: d0253b2ea4f3d196d76d3dd7ce6725abd2edc26b
Author: Campbell Barton
Date:   Wed Mar 22 19:31:34 2017 +1100
Branches: master
https://developer.blender.org/rBd0253b2ea4f3d196d76d3dd7ce6725abd2edc26b

BLI_path_util: add BLI_path_name_at_index

Utility to get a file/dir in the path by index,
supporting negative indices to start from the end of the path.

Without this it wasn't straightforward to get
the a files parent directory name from a filepath.

===================================================================

M	source/blender/blenlib/BLI_path_util.h
M	source/blender/blenlib/intern/path_util.c
M	tests/gtests/blenlib/BLI_path_util_test.cc

===================================================================

diff --git a/source/blender/blenlib/BLI_path_util.h b/source/blender/blenlib/BLI_path_util.h
index baa1f79201..7d971fbfc3 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 6644e6605a..83aa01d3d1 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 d017ab18b4..ccfa59503c 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)
 {




More information about the Bf-blender-cvs mailing list