Library code for user-relative paths, take three.
authorAndreas Ericsson <exon@op5.se>
Thu, 17 Nov 2005 19:37:14 +0000 (20:37 +0100)
committerJunio C Hamano <junkio@cox.net>
Sun, 20 Nov 2005 04:50:37 +0000 (20:50 -0800)
This patch provides the work-horse of the user-relative paths feature,
using Linus' idea of a blind chdir() and getcwd() which makes it
remarkably simple.

Signed-off-by: Andreas Ericsson <ae@op5.se>
Signed-off-by: Junio C Hamano <junkio@cox.net>
cache.h
path.c

diff --git a/cache.h b/cache.h
index f93f516..e2be3e7 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -192,6 +192,7 @@ extern int diff_rename_limit_default;
 
 /* Return a statically allocated filename matching the sha1 signature */
 extern char *mkpath(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
+extern char *enter_repo(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..5b61709 100644 (file)
--- 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 char *src, size_t n)
 
        return dest;
 }
+
+static 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(char *path, int strict)
+{
+       char *dir = path;
+
+       if(strict && *dir != '/')
+               return NULL;
+
+       if(*dir == '~') {               /* user-relative path */
+               struct passwd *pw;
+               char *slash = strchr(dir, '/');
+
+               dir++;
+               /* '~/' and '~' (no slash) means users own home-dir */
+               if(!*dir || *dir == '/')
+                       pw = getpwuid(getuid());
+               else {
+                       if (slash) {
+                               *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]) /* no path following username */
+                       return current_dir();
+
+               dir = slash + 1;
+       }
+
+       /* ~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();
+}
+
+char *enter_repo(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;
+}