Changes in v3: * Rewrite of `strip_last_component()` as the v2 verison didn't properly handle inputs like '/foo'. Thanks to Johannes for pointing this out and suggesting a solution. * Small style changes * Revert the call in `get_common_dir_noenv()` to maintain proper functionality. Brandon Williams (4): real_path: resolve symlinks by hand real_path: convert real_path_internal to strbuf_realpath real_path: create real_pathdup real_path: have callers use real_pathdup and strbuf_realpath abspath.c | 222 ++++++++++++++++++++++++++++++++++++------------------ builtin/init-db.c | 6 +- cache.h | 3 + environment.c | 2 +- setup.c | 13 ++-- sha1_file.c | 2 +- submodule.c | 2 +- transport.c | 2 +- worktree.c | 2 +- 9 files changed, 169 insertions(+), 85 deletions(-) --- interdiff from v2 diff --git a/abspath.c b/abspath.c index df37356..79ee310 100644 --- a/abspath.c +++ b/abspath.c @@ -14,10 +14,17 @@ int is_directory(const char *path) /* removes the last path component from 'path' except if 'path' is root */ static void strip_last_component(struct strbuf *path) { - if (path->len > offset_1st_component(path->buf)) { - char *last_slash = find_last_dir_sep(path->buf); - strbuf_setlen(path, last_slash - path->buf); - } + size_t offset = offset_1st_component(path->buf); + size_t len = path->len; + + /* Find start of the last component */ + while (offset < len && !is_dir_sep(path->buf[len - 1])) + len--; + /* Skip sequences of multiple path-separators */ + while (offset < len && is_dir_sep(path->buf[len - 1])) + len--; + + strbuf_setlen(path, len); } /* get (and remove) the next component in 'remaining' and place it in 'next' */ @@ -112,7 +119,7 @@ char *strbuf_realpath(struct strbuf *resolved, const char *path, if (lstat(resolved->buf, &st)) { /* error out unless this was the last component */ - if (!(errno == ENOENT && !remaining.len)) { + if (errno != ENOENT || remaining.len) { if (die_on_error) die_errno("Invalid path '%s'", resolved->buf); @@ -203,7 +210,7 @@ char *real_pathdup(const char *path) struct strbuf realpath = STRBUF_INIT; char *retval = NULL; - if(strbuf_realpath(&realpath, path, 0)) + if (strbuf_realpath(&realpath, path, 0)) retval = strbuf_detach(&realpath, NULL); strbuf_release(&realpath); diff --git a/setup.c b/setup.c index 0d9fdd0..1b534a7 100644 --- a/setup.c +++ b/setup.c @@ -254,7 +254,7 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir) if (!is_absolute_path(data.buf)) strbuf_addf(&path, "%s/", gitdir); strbuf_addbuf(&path, &data); - strbuf_realpath(sb, path.buf, 1); + strbuf_addstr(sb, real_path(path.buf)); ret = 1; } else { strbuf_addstr(sb, gitdir); -- 2.8.0.rc3.226.g39d4020