[PATCH 2/4] Library code for user-relative paths.
From: Andreas Ericsson <hidden>
Date: 2016-06-15 22:42:10
See this discussion, "[RFC] GIT paths", on the git-list: http://www.gelato.unsw.edu.au/archives/git/0510/10924.html This patch provides the work-horse of the user-relative paths, using Linus' idea of a blind chdir() and getcwd(), which makes it remarkably simple. Signed-off-by: Andreas Ericsson <redacted> --- cache.h | 1 + path.c | 72 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 0 deletions(-) applies-to: bf640ae3d5bb5f5f283c62d4f5198a3c5e5086f0 1c4e7bb2383e2b67f0e877a46ea1eba4ec7e45e7
diff --git a/cache.h b/cache.h
index 677c6ac..aea2097 100644
--- a/cache.h
+++ b/cache.h@@ -190,6 +190,7 @@ extern int trust_executable_bit; /* Return a statically allocated filename matching the sha1 signature */ extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2))); +extern const char *is_git_repo(const char *path, int strict); extern char *git_path(const char *fmt, ...) __attribute__((format (printf, 1, 2))); extern char *sha1_file_name(const unsigned char *sha1); extern char *sha1_pack_name(const unsigned char *sha1);
diff --git a/path.c b/path.c
index 495d17c..4f4018d 100644
--- a/path.c
+++ b/path.c@@ -11,6 +11,7 @@ * which is what it's designed for. */ #include "cache.h" +#include <pwd.h> static char pathname[PATH_MAX]; static char bad_path[] = "/bad-path/";
@@ -89,3 +90,74 @@ char *safe_strncpy(char *dest, const cha return dest; } + +static const char *current_dir() +{ + return getcwd(pathname, sizeof(pathname)); +} + +/* Take a raw path from is_git_repo() and canonicalize it using Linus' + * idea of a blind chdir() and getcwd(). */ +static const char *canonical_path(const char *path, int strict) +{ + const char *dir = path; + + if(strict && *dir != '/') + return NULL; + + if(*dir == '~') { /* user-relative path */ + struct passwd *pw; + char *slash = NULL; + + dir++; + /* '~/' and '~' (no slash) means users own home-dir */ + if(!*dir || *dir == '/') + pw = getpwuid(getuid()); + else { + if((slash = strchr(dir, '/'))) { + *slash = '\0'; + pw = getpwnam(dir); + *slash = '/'; + } + else + pw = getpwnam(dir); + } + + /* make sure we got something back that we can chdir() to */ + if(!pw || chdir(pw->pw_dir) < 0) + return NULL; + + if(slash && *slash + 1) + dir = slash + 1; + else + dir = current_dir(); + } + + /* ~foo/path/to/repo is now path/to/repo and we're in foo's homedir */ + if(chdir(dir) < 0) + return NULL; + + return current_dir(); +} + +const char *is_git_repo(const char *path, int strict) +{ + if(!path) + return NULL; + + if(!canonical_path(path, strict)) { + if(strict || !canonical_path(mkpath("%s.git", path), strict)) + return NULL; + } + + /* This is perfectly safe, and people tend to think of the directory + * where they ran git-init-db as their repository, so humour them. */ + (void)chdir(".git"); + + if(access("objects", X_OK) == 0 && access("refs", X_OK) == 0) { + putenv("GIT_DIR=."); + return current_dir(); + } + + return NULL; +}
--- 0.99.9.GIT