Merge branch 'ab/conditional-config-with-symlinks'
[gitweb.git] / path.c
diff --git a/path.c b/path.c
index 9bfaeda2071636ec8a5a5ee32491494f5f83211c..c1cb1cf6273f4a5a7ed91d495c55a7d283497460 100644 (file)
--- a/path.c
+++ b/path.c
@@ -6,6 +6,7 @@
 #include "string-list.h"
 #include "dir.h"
 #include "worktree.h"
+#include "submodule-config.h"
 
 static int get_st_mode_bits(const char *path, int *mode)
 {
@@ -469,25 +470,20 @@ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)
        return pathname->buf;
 }
 
-static void do_submodule_path(struct strbuf *buf, const char *path,
-                             const char *fmt, va_list args)
+/* Returns 0 on success, negative on failure. */
+static int do_submodule_path(struct strbuf *buf, const char *path,
+                            const char *fmt, va_list args)
 {
-       const char *git_dir;
        struct strbuf git_submodule_common_dir = STRBUF_INIT;
        struct strbuf git_submodule_dir = STRBUF_INIT;
+       int ret;
 
-       strbuf_addstr(buf, path);
-       strbuf_complete(buf, '/');
-       strbuf_addstr(buf, ".git");
-
-       git_dir = read_gitfile(buf->buf);
-       if (git_dir) {
-               strbuf_reset(buf);
-               strbuf_addstr(buf, git_dir);
-       }
-       strbuf_addch(buf, '/');
-       strbuf_addbuf(&git_submodule_dir, buf);
+       ret = submodule_to_gitdir(&git_submodule_dir, path);
+       if (ret)
+               goto cleanup;
 
+       strbuf_complete(&git_submodule_dir, '/');
+       strbuf_addbuf(buf, &git_submodule_dir);
        strbuf_vaddf(buf, fmt, args);
 
        if (get_common_dir_noenv(&git_submodule_common_dir, git_submodule_dir.buf))
@@ -495,27 +491,37 @@ static void do_submodule_path(struct strbuf *buf, const char *path,
 
        strbuf_cleanup_path(buf);
 
+cleanup:
        strbuf_release(&git_submodule_dir);
        strbuf_release(&git_submodule_common_dir);
+       return ret;
 }
 
 char *git_pathdup_submodule(const char *path, const char *fmt, ...)
 {
+       int err;
        va_list args;
        struct strbuf buf = STRBUF_INIT;
        va_start(args, fmt);
-       do_submodule_path(&buf, path, fmt, args);
+       err = do_submodule_path(&buf, path, fmt, args);
        va_end(args);
+       if (err) {
+               strbuf_release(&buf);
+               return NULL;
+       }
        return strbuf_detach(&buf, NULL);
 }
 
-void strbuf_git_path_submodule(struct strbuf *buf, const char *path,
-                              const char *fmt, ...)
+int strbuf_git_path_submodule(struct strbuf *buf, const char *path,
+                             const char *fmt, ...)
 {
+       int err;
        va_list args;
        va_start(args, fmt);
-       do_submodule_path(buf, path, fmt, args);
+       err = do_submodule_path(buf, path, fmt, args);
        va_end(args);
+
+       return err;
 }
 
 static void do_git_common_path(struct strbuf *buf,
@@ -611,8 +617,10 @@ static struct passwd *getpw_str(const char *username, size_t len)
  * Return a string with ~ and ~user expanded via getpw*.  If buf != NULL,
  * then it is a newly allocated string. Returns NULL on getpw failure or
  * if path is NULL.
+ *
+ * If real_home is true, real_path($HOME) is used in the expansion.
  */
-char *expand_user_path(const char *path)
+char *expand_user_path(const char *path, int real_home)
 {
        struct strbuf user_path = STRBUF_INIT;
        const char *to_copy = path;
@@ -627,7 +635,10 @@ char *expand_user_path(const char *path)
                        const char *home = getenv("HOME");
                        if (!home)
                                goto return_null;
-                       strbuf_addstr(&user_path, home);
+                       if (real_home)
+                               strbuf_addstr(&user_path, real_path(home));
+                       else
+                               strbuf_addstr(&user_path, home);
 #ifdef GIT_WINDOWS_NATIVE
                        convert_slashes(user_path.buf);
 #endif
@@ -696,7 +707,7 @@ const char *enter_repo(const char *path, int strict)
                strbuf_add(&validated_path, path, len);
 
                if (used_path.buf[0] == '~') {
-                       char *newpath = expand_user_path(used_path.buf);
+                       char *newpath = expand_user_path(used_path.buf, 0);
                        if (!newpath)
                                return NULL;
                        strbuf_attach(&used_path, newpath, strlen(newpath),
@@ -964,7 +975,7 @@ const char *remove_leading_path(const char *in, const char *prefix)
  *
  * Performs the following normalizations on src, storing the result in dst:
  * - Ensures that components are separated by '/' (Windows only)
- * - Squashes sequences of '/'.
+ * - Squashes sequences of '/' except "//server/share" on Windows
  * - Removes "." components.
  * - Removes ".." components, and the components the precede them.
  * Returns failure (non-zero) if a ".." component appears as first path
@@ -987,17 +998,22 @@ const char *remove_leading_path(const char *in, const char *prefix)
 int normalize_path_copy_len(char *dst, const char *src, int *prefix_len)
 {
        char *dst0;
-       int i;
+       const char *end;
 
-       for (i = has_dos_drive_prefix(src); i > 0; i--)
-               *dst++ = *src++;
+       /*
+        * Copy initial part of absolute path: "/", "C:/", "//server/share/".
+        */
+       end = src + offset_1st_component(src);
+       while (src < end) {
+               char c = *src++;
+               if (is_dir_sep(c))
+                       c = '/';
+               *dst++ = c;
+       }
        dst0 = dst;
 
-       if (is_dir_sep(*src)) {
-               *dst++ = '/';
-               while (is_dir_sep(*src))
-                       src++;
-       }
+       while (is_dir_sep(*src))
+               src++;
 
        for (;;) {
                char c = *src;
@@ -1240,6 +1256,21 @@ char *xdg_config_home(const char *filename)
        return NULL;
 }
 
+char *xdg_cache_home(const char *filename)
+{
+       const char *home, *cache_home;
+
+       assert(filename);
+       cache_home = getenv("XDG_CACHE_HOME");
+       if (cache_home && *cache_home)
+               return mkpathdup("%s/git/%s", cache_home, filename);
+
+       home = getenv("HOME");
+       if (home)
+               return mkpathdup("%s/.cache/git/%s", home, filename);
+       return NULL;
+}
+
 GIT_PATH_FUNC(git_path_cherry_pick_head, "CHERRY_PICK_HEAD")
 GIT_PATH_FUNC(git_path_revert_head, "REVERT_HEAD")
 GIT_PATH_FUNC(git_path_squash_msg, "SQUASH_MSG")