git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 1/2] abspath: add a function to resolve paths with missing components

From
brian m. carlson <sandals@crustytoothpaste.net>
Date
Oct 9, 2020, 19:15 UTC
Message-ID
<20201009191511.267461-2-sandals@crustytoothpaste.net>
In-Reply-To
<20201009191511.267461-1-sandals@crustytoothpaste.net>

We'd like to canonicalize paths such that we can preserve any number of trailing components that may be missing. Let's add a function to do that that calls strbuf_realpath to find the canonical path for the portion we do have and then append the missing part. We adjust strip_last_component to return us the component it has stripped and use that to help us accumulate the missing part.

Note that it is intentional that we invoke strbuf_realpath here, repeatedly if necessary, because on Windows that function is replaced with a version that uses the proper system semantics for canonicalization. Trying to adjust strbuf_realpath to perform this kind of canonicalization with an additional option would fail to work properly on Windows. The present approach is equivalent to strbuf_realpath for cases where the path exists, and the only other cases where we will use this function the additional overhead of multiple invocations is not significant.

Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>
---
 abspath.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++----
 cache.h   |  1 +
 2 files changed, 47 insertions(+), 4 deletions(-)
diff --git a/abspath.c b/abspath.c
index 6f15a418bb..092bb33b64 100644
--- a/abspath.c
+++ b/abspath.c
@@ -11,8 +11,12 @@ int is_directory(const char *path)
 	return (!stat(path, &st) && S_ISDIR(st.st_mode));
 }
 
-/* removes the last path component from 'path' except if 'path' is root */
-static void strip_last_component(struct strbuf *path)
+/*
+ * Removes the last path component from 'path' except if 'path' is root.
+ *
+ * If last is not NULL, the last path component is copied to last.
+ */
+static void strip_last_component(struct strbuf *path, struct strbuf *last)
 {
 	size_t offset = offset_1st_component(path->buf);
 	size_t len = path->len;
@@ -20,6 +24,10 @@ static void strip_last_component(struct strbuf *path)
 	/* Find start of the last component */
 	while (offset < len && !is_dir_sep(path->buf[len - 1]))
 		len--;
+
+	if (last)
+		strbuf_addstr(last, path->buf + len);
+
 	/* Skip sequences of multiple path-separators */
 	while (offset < len && is_dir_sep(path->buf[len - 1]))
 		len--;
@@ -118,7 +126,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,
 			continue; /* '.' component */
 		} else if (next.len == 2 && !strcmp(next.buf, "..")) {
 			/* '..' component; strip the last path component */
-			strip_last_component(resolved);
+			strip_last_component(resolved, NULL);
 			continue;
 		}
 
@@ -169,7 +177,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,
 				 * strip off the last component since it will
 				 * be replaced with the contents of the symlink
 				 */
-				strip_last_component(resolved);
+				strip_last_component(resolved, NULL);
 			}
 
 			/*
@@ -202,6 +210,40 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path,
 	return retval;
 }
 
+/*
+ * Like strbuf_realpath, but trailing components which do not exist are copied
+ * through.
+ */
+char *strbuf_realpath_missing(struct strbuf *resolved, const char *path)
+{
+	struct strbuf remaining = STRBUF_INIT;
+	struct strbuf trailing = STRBUF_INIT;
+	struct strbuf component = STRBUF_INIT;
+
+	strbuf_addstr(&remaining, path);
+
+	while (remaining.len) {
+		if (strbuf_realpath(resolved, remaining.buf, 0)) {
+			strbuf_addbuf(resolved, &trailing);
+
+			strbuf_release(&component);
+			strbuf_release(&remaining);
+			strbuf_release(&trailing);
+
+			return resolved->buf;
+		}
+		strip_last_component(&remaining, &component);
+		strbuf_insertstr(&trailing, 0, "/");
+		strbuf_insertstr(&trailing, 1, component.buf);
+		strbuf_reset(&component);
+	}
+
+	strbuf_release(&component);
+	strbuf_release(&remaining);
+	strbuf_release(&trailing);
+	return NULL;
+}
+
 char *real_pathdup(const char *path, int die_on_error)
 {
 	struct strbuf realpath = STRBUF_INIT;
diff --git a/cache.h b/cache.h
index c0072d43b1..e1e17e108e 100644
--- a/cache.h
+++ b/cache.h
@@ -1320,6 +1320,7 @@ static inline int is_absolute_path(const char *path)
 int is_directory(const char *);
 char *strbuf_realpath(struct strbuf *resolved, const char *path,
 		      int die_on_error);
+char *strbuf_realpath_missing(struct strbuf *resolved, const char *path);
 char *real_pathdup(const char *path, int die_on_error);
 const char *absolute_path(const char *path);
 char *absolute_pathdup(const char *path);
Previous: brian m. carlsonNext: Junio C Hamano
Message 5 of 13 in “rev-parse options for absolute or relative paths”
  1. 0/2 rev-parse options for absolute or relative pathsbrian m. carlson, Oct 9, 2020
  2. 2/2 rev-parse: add option for absolute or relative path formattingbrian m. carlson, Oct 9, 2020
  3. Johannes SchindelinNov 9, 2020
  4. brian m. carlsonNov 16, 2020
  5. 1/2 abspath: add a function to resolve paths with missing componentsbrian m. carlson, Oct 9, 2020
  6. Junio C HamanoOct 9, 2020
  7. brian m. carlsonOct 10, 2020
  8. Johannes SchindelinNov 9, 2020
  9. Johannes SchindelinNov 9, 2020
  10. brian m. carlsonNov 16, 2020
  11. Emily ShafferNov 4, 2020
  12. brian m. carlsonNov 5, 2020
  13. Johannes SchindelinNov 9, 2020

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.