[PATCH] git-local-fetch: Fix error checking and leak in setup_indices()
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index 9973d1fc21e9d14f4cf1d30cb59f55cdfd7fc1e7..161018097def05f717ae20e59e3df6cc1b01ab77 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -3,6 +3,84 @@
 
 #include <errno.h>
 
+static int read_ref(const char *refname, unsigned char *sha1)
+{
+       int ret = -1;
+       int fd = open(git_path("%s", refname), O_RDONLY);
+
+       if (fd >= 0) {
+               char buffer[60];
+               if (read(fd, buffer, sizeof(buffer)) >= 40)
+                       ret = get_sha1_hex(buffer, sha1);
+               close(fd);
+       }
+       return ret;
+}
+
+static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1))
+{
+       int retval = 0;
+       DIR *dir = opendir(git_path("%s", base));
+
+       if (dir) {
+               struct dirent *de;
+               int baselen = strlen(base);
+               char *path = xmalloc(baselen + 257);
+
+               if (!strncmp(base, "./", 2)) {
+                       base += 2;
+                       baselen -= 2;
+               }
+               memcpy(path, base, baselen);
+               if (baselen && base[baselen-1] != '/')
+                       path[baselen++] = '/';
+
+               while ((de = readdir(dir)) != NULL) {
+                       unsigned char sha1[20];
+                       struct stat st;
+                       int namelen;
+
+                       if (de->d_name[0] == '.')
+                               continue;
+                       namelen = strlen(de->d_name);
+                       if (namelen > 255)
+                               continue;
+                       memcpy(path + baselen, de->d_name, namelen+1);
+                       if (stat(git_path("%s", path), &st) < 0)
+                               continue;
+                       if (S_ISDIR(st.st_mode)) {
+                               retval = do_for_each_ref(path, fn);
+                               if (retval)
+                                       break;
+                               continue;
+                       }
+                       if (read_ref(path, sha1) < 0)
+                               continue;
+                       if (!has_sha1_file(sha1))
+                               continue;
+                       retval = fn(path, sha1);
+                       if (retval)
+                               break;
+               }
+               free(path);
+               closedir(dir);
+       }
+       return retval;
+}
+
+int head_ref(int (*fn)(const char *path, const unsigned char *sha1))
+{
+       unsigned char sha1[20];
+       if (!read_ref("HEAD", sha1))
+               return fn("HEAD", sha1);
+       return 0;
+}
+
+int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1))
+{
+       return do_for_each_ref("refs", fn);
+}
+
 static char *ref_file_name(const char *ref)
 {
        char *base = get_refs_directory();