rebase -i: rewrite init_basic_state() in C
[gitweb.git] / path.c
diff --git a/path.c b/path.c
index 9018aa0ac08b4178411f00b6215d17a27d997206..34f0f98349a6eccd462614cdf143bb031ca348c8 100644 (file)
--- a/path.c
+++ b/path.c
@@ -2,11 +2,15 @@
  * Utilities for paths and pathnames
  */
 #include "cache.h"
+#include "repository.h"
 #include "strbuf.h"
 #include "string-list.h"
 #include "dir.h"
 #include "worktree.h"
 #include "submodule-config.h"
+#include "path.h"
+#include "packfile.h"
+#include "object-store.h"
 
 static int get_st_mode_bits(const char *path, int *mode)
 {
@@ -31,11 +35,10 @@ static struct strbuf *get_pathname(void)
        return sb;
 }
 
-static char *cleanup_path(char *path)
+static const char *cleanup_path(const char *path)
 {
        /* Clean it up */
-       if (!memcmp(path, "./", 2)) {
-               path += 2;
+       if (skip_prefix(path, "./", &path)) {
                while (*path == '/')
                        path++;
        }
@@ -44,7 +47,7 @@ static char *cleanup_path(char *path)
 
 static void strbuf_cleanup_path(struct strbuf *sb)
 {
-       char *path = cleanup_path(sb->buf);
+       const char *path = cleanup_path(sb->buf);
        if (path > sb->buf)
                strbuf_remove(sb, 0, path - sb->buf);
 }
@@ -61,7 +64,7 @@ char *mksnpath(char *buf, size_t n, const char *fmt, ...)
                strlcpy(buf, bad_path, n);
                return buf;
        }
-       return cleanup_path(buf);
+       return (char *)cleanup_path(buf);
 }
 
 static int dir_prefix(const char *buf, const char *dir)
@@ -190,7 +193,7 @@ static void *add_to_trie(struct trie *root, const char *key, void *value)
                 * Split this node: child will contain this node's
                 * existing children.
                 */
-               child = malloc(sizeof(*child));
+               child = xmalloc(sizeof(*child));
                memcpy(child->children, root->children, sizeof(root->children));
 
                child->len = root->len - i - 1;
@@ -343,8 +346,6 @@ static void update_common_dir(struct strbuf *buf, int git_dir_len,
 {
        char *base = buf->buf + git_dir_len;
        init_common_trie();
-       if (!common_dir)
-               common_dir = get_git_common_dir();
        if (trie_find(&common_trie, base, check_common, NULL) > 0)
                replace_dir(buf, git_dir_len, common_dir);
 }
@@ -355,7 +356,7 @@ void report_linked_checkout_garbage(void)
        const struct common_dir *p;
        int len;
 
-       if (!git_common_dir_env)
+       if (!the_repository->different_commondir)
                return;
        strbuf_addf(&sb, "%s/", get_git_dir());
        len = sb.len;
@@ -371,42 +372,78 @@ void report_linked_checkout_garbage(void)
        strbuf_release(&sb);
 }
 
-static void adjust_git_path(struct strbuf *buf, int git_dir_len)
+static void adjust_git_path(const struct repository *repo,
+                           struct strbuf *buf, int git_dir_len)
 {
        const char *base = buf->buf + git_dir_len;
-       if (git_graft_env && is_dir_file(base, "info", "grafts"))
+       if (is_dir_file(base, "info", "grafts"))
                strbuf_splice(buf, 0, buf->len,
-                             get_graft_file(), strlen(get_graft_file()));
-       else if (git_index_env && !strcmp(base, "index"))
+                             repo->graft_file, strlen(repo->graft_file));
+       else if (!strcmp(base, "index"))
                strbuf_splice(buf, 0, buf->len,
-                             get_index_file(), strlen(get_index_file()));
-       else if (git_db_env && dir_prefix(base, "objects"))
-               replace_dir(buf, git_dir_len + 7, get_object_directory());
+                             repo->index_file, strlen(repo->index_file));
+       else if (dir_prefix(base, "objects"))
+               replace_dir(buf, git_dir_len + 7, repo->objects->objectdir);
        else if (git_hooks_path && dir_prefix(base, "hooks"))
                replace_dir(buf, git_dir_len + 5, git_hooks_path);
-       else if (git_common_dir_env)
-               update_common_dir(buf, git_dir_len, NULL);
+       else if (repo->different_commondir)
+               update_common_dir(buf, git_dir_len, repo->commondir);
 }
 
-static void do_git_path(const struct worktree *wt, struct strbuf *buf,
+static void strbuf_worktree_gitdir(struct strbuf *buf,
+                                  const struct repository *repo,
+                                  const struct worktree *wt)
+{
+       if (!wt)
+               strbuf_addstr(buf, repo->gitdir);
+       else if (!wt->id)
+               strbuf_addstr(buf, repo->commondir);
+       else
+               strbuf_git_common_path(buf, repo, "worktrees/%s", wt->id);
+}
+
+static void do_git_path(const struct repository *repo,
+                       const struct worktree *wt, struct strbuf *buf,
                        const char *fmt, va_list args)
 {
        int gitdir_len;
-       strbuf_addstr(buf, get_worktree_git_dir(wt));
+       strbuf_worktree_gitdir(buf, repo, wt);
        if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
                strbuf_addch(buf, '/');
        gitdir_len = buf->len;
        strbuf_vaddf(buf, fmt, args);
-       adjust_git_path(buf, gitdir_len);
+       if (!wt)
+               adjust_git_path(repo, buf, gitdir_len);
        strbuf_cleanup_path(buf);
 }
 
+char *repo_git_path(const struct repository *repo,
+                   const char *fmt, ...)
+{
+       struct strbuf path = STRBUF_INIT;
+       va_list args;
+       va_start(args, fmt);
+       do_git_path(repo, NULL, &path, fmt, args);
+       va_end(args);
+       return strbuf_detach(&path, NULL);
+}
+
+void strbuf_repo_git_path(struct strbuf *sb,
+                         const struct repository *repo,
+                         const char *fmt, ...)
+{
+       va_list args;
+       va_start(args, fmt);
+       do_git_path(repo, NULL, sb, fmt, args);
+       va_end(args);
+}
+
 char *git_path_buf(struct strbuf *buf, const char *fmt, ...)
 {
        va_list args;
        strbuf_reset(buf);
        va_start(args, fmt);
-       do_git_path(NULL, buf, fmt, args);
+       do_git_path(the_repository, NULL, buf, fmt, args);
        va_end(args);
        return buf->buf;
 }
@@ -415,7 +452,7 @@ void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
 {
        va_list args;
        va_start(args, fmt);
-       do_git_path(NULL, sb, fmt, args);
+       do_git_path(the_repository, NULL, sb, fmt, args);
        va_end(args);
 }
 
@@ -424,7 +461,7 @@ const char *git_path(const char *fmt, ...)
        struct strbuf *pathname = get_pathname();
        va_list args;
        va_start(args, fmt);
-       do_git_path(NULL, pathname, fmt, args);
+       do_git_path(the_repository, NULL, pathname, fmt, args);
        va_end(args);
        return pathname->buf;
 }
@@ -434,7 +471,7 @@ char *git_pathdup(const char *fmt, ...)
        struct strbuf path = STRBUF_INIT;
        va_list args;
        va_start(args, fmt);
-       do_git_path(NULL, &path, fmt, args);
+       do_git_path(the_repository, NULL, &path, fmt, args);
        va_end(args);
        return strbuf_detach(&path, NULL);
 }
@@ -465,11 +502,52 @@ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)
        struct strbuf *pathname = get_pathname();
        va_list args;
        va_start(args, fmt);
-       do_git_path(wt, pathname, fmt, args);
+       do_git_path(the_repository, wt, pathname, fmt, args);
        va_end(args);
        return pathname->buf;
 }
 
+static void do_worktree_path(const struct repository *repo,
+                            struct strbuf *buf,
+                            const char *fmt, va_list args)
+{
+       strbuf_addstr(buf, repo->worktree);
+       if(buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
+               strbuf_addch(buf, '/');
+
+       strbuf_vaddf(buf, fmt, args);
+       strbuf_cleanup_path(buf);
+}
+
+char *repo_worktree_path(const struct repository *repo, const char *fmt, ...)
+{
+       struct strbuf path = STRBUF_INIT;
+       va_list args;
+
+       if (!repo->worktree)
+               return NULL;
+
+       va_start(args, fmt);
+       do_worktree_path(repo, &path, fmt, args);
+       va_end(args);
+
+       return strbuf_detach(&path, NULL);
+}
+
+void strbuf_repo_worktree_path(struct strbuf *sb,
+                              const struct repository *repo,
+                              const char *fmt, ...)
+{
+       va_list args;
+
+       if (!repo->worktree)
+               return;
+
+       va_start(args, fmt);
+       do_worktree_path(repo, sb, fmt, args);
+       va_end(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)
@@ -524,11 +602,12 @@ int strbuf_git_path_submodule(struct strbuf *buf, const char *path,
        return err;
 }
 
-static void do_git_common_path(struct strbuf *buf,
+static void do_git_common_path(const struct repository *repo,
+                              struct strbuf *buf,
                               const char *fmt,
                               va_list args)
 {
-       strbuf_addstr(buf, get_git_common_dir());
+       strbuf_addstr(buf, repo->commondir);
        if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
                strbuf_addch(buf, '/');
        strbuf_vaddf(buf, fmt, args);
@@ -540,24 +619,27 @@ const char *git_common_path(const char *fmt, ...)
        struct strbuf *pathname = get_pathname();
        va_list args;
        va_start(args, fmt);
-       do_git_common_path(pathname, fmt, args);
+       do_git_common_path(the_repository, pathname, fmt, args);
        va_end(args);
        return pathname->buf;
 }
 
-void strbuf_git_common_path(struct strbuf *sb, const char *fmt, ...)
+void strbuf_git_common_path(struct strbuf *sb,
+                           const struct repository *repo,
+                           const char *fmt, ...)
 {
        va_list args;
        va_start(args, fmt);
-       do_git_common_path(sb, fmt, args);
+       do_git_common_path(repo, sb, fmt, args);
        va_end(args);
 }
 
 int validate_headref(const char *path)
 {
        struct stat st;
-       char *buf, buffer[256];
-       unsigned char sha1[20];
+       char buffer[256];
+       const char *refname;
+       struct object_id oid;
        int fd;
        ssize_t len;
 
@@ -581,24 +663,24 @@ int validate_headref(const char *path)
        len = read_in_full(fd, buffer, sizeof(buffer)-1);
        close(fd);
 
+       if (len < 0)
+               return -1;
+       buffer[len] = '\0';
+
        /*
         * Is it a symbolic ref?
         */
-       if (len < 4)
-               return -1;
-       if (!memcmp("ref:", buffer, 4)) {
-               buf = buffer + 4;
-               len -= 4;
-               while (len && isspace(*buf))
-                       buf++, len--;
-               if (len >= 5 && !memcmp("refs/", buf, 5))
+       if (skip_prefix(buffer, "ref:", &refname)) {
+               while (isspace(*refname))
+                       refname++;
+               if (starts_with(refname, "refs/"))
                        return 0;
        }
 
        /*
         * Is this a detached HEAD?
         */
-       if (!get_sha1_hex(buffer, sha1))
+       if (!get_oid_hex(buffer, &oid))
                return 0;
 
        return -1;
@@ -636,7 +718,7 @@ char *expand_user_path(const char *path, int real_home)
                        if (!home)
                                goto return_null;
                        if (real_home)
-                               strbuf_addstr(&user_path, real_path(home));
+                               strbuf_add_real_path(&user_path, home);
                        else
                                strbuf_addstr(&user_path, home);
 #ifdef GIT_WINDOWS_NATIVE
@@ -1241,6 +1323,90 @@ int is_ntfs_dotgit(const char *name)
                }
 }
 
+static int is_ntfs_dot_generic(const char *name,
+                              const char *dotgit_name,
+                              size_t len,
+                              const char *dotgit_ntfs_shortname_prefix)
+{
+       int saw_tilde;
+       size_t i;
+
+       if ((name[0] == '.' && !strncasecmp(name + 1, dotgit_name, len))) {
+               i = len + 1;
+only_spaces_and_periods:
+               for (;;) {
+                       char c = name[i++];
+                       if (!c)
+                               return 1;
+                       if (c != ' ' && c != '.')
+                               return 0;
+               }
+       }
+
+       /*
+        * Is it a regular NTFS short name, i.e. shortened to 6 characters,
+        * followed by ~1, ... ~4?
+        */
+       if (!strncasecmp(name, dotgit_name, 6) && name[6] == '~' &&
+           name[7] >= '1' && name[7] <= '4') {
+               i = 8;
+               goto only_spaces_and_periods;
+       }
+
+       /*
+        * Is it a fall-back NTFS short name (for details, see
+        * https://en.wikipedia.org/wiki/8.3_filename?
+        */
+       for (i = 0, saw_tilde = 0; i < 8; i++)
+               if (name[i] == '\0')
+                       return 0;
+               else if (saw_tilde) {
+                       if (name[i] < '0' || name[i] > '9')
+                               return 0;
+               } else if (name[i] == '~') {
+                       if (name[++i] < '1' || name[i] > '9')
+                               return 0;
+                       saw_tilde = 1;
+               } else if (i >= 6)
+                       return 0;
+               else if (name[i] < 0) {
+                       /*
+                        * We know our needles contain only ASCII, so we clamp
+                        * here to make the results of tolower() sane.
+                        */
+                       return 0;
+               } else if (tolower(name[i]) != dotgit_ntfs_shortname_prefix[i])
+                       return 0;
+
+       goto only_spaces_and_periods;
+}
+
+/*
+ * Inline helper to make sure compiler resolves strlen() on literals at
+ * compile time.
+ */
+static inline int is_ntfs_dot_str(const char *name, const char *dotgit_name,
+                                 const char *dotgit_ntfs_shortname_prefix)
+{
+       return is_ntfs_dot_generic(name, dotgit_name, strlen(dotgit_name),
+                                  dotgit_ntfs_shortname_prefix);
+}
+
+int is_ntfs_dotgitmodules(const char *name)
+{
+       return is_ntfs_dot_str(name, "gitmodules", "gi7eba");
+}
+
+int is_ntfs_dotgitignore(const char *name)
+{
+       return is_ntfs_dot_str(name, "gitignore", "gi250a");
+}
+
+int is_ntfs_dotgitattributes(const char *name)
+{
+       return is_ntfs_dot_str(name, "gitattributes", "gi7d29");
+}
+
 int looks_like_command_line_option(const char *str)
 {
        return str && str[0] == '-';
@@ -1276,12 +1442,12 @@ char *xdg_cache_home(const char *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")
-GIT_PATH_FUNC(git_path_merge_msg, "MERGE_MSG")
-GIT_PATH_FUNC(git_path_merge_rr, "MERGE_RR")
-GIT_PATH_FUNC(git_path_merge_mode, "MERGE_MODE")
-GIT_PATH_FUNC(git_path_merge_head, "MERGE_HEAD")
-GIT_PATH_FUNC(git_path_fetch_head, "FETCH_HEAD")
-GIT_PATH_FUNC(git_path_shallow, "shallow")
+REPO_GIT_PATH_FUNC(cherry_pick_head, "CHERRY_PICK_HEAD")
+REPO_GIT_PATH_FUNC(revert_head, "REVERT_HEAD")
+REPO_GIT_PATH_FUNC(squash_msg, "SQUASH_MSG")
+REPO_GIT_PATH_FUNC(merge_msg, "MERGE_MSG")
+REPO_GIT_PATH_FUNC(merge_rr, "MERGE_RR")
+REPO_GIT_PATH_FUNC(merge_mode, "MERGE_MODE")
+REPO_GIT_PATH_FUNC(merge_head, "MERGE_HEAD")
+REPO_GIT_PATH_FUNC(fetch_head, "FETCH_HEAD")
+REPO_GIT_PATH_FUNC(shallow, "shallow")