setup.con commit Merge branch 'master' (52b6536)
   1#include "cache.h"
   2
   3const char *prefix_path(const char *prefix, int len, const char *path)
   4{
   5        const char *orig = path;
   6        for (;;) {
   7                char c;
   8                if (*path != '.')
   9                        break;
  10                c = path[1];
  11                /* "." */
  12                if (!c) {
  13                        path++;
  14                        break;
  15                }
  16                /* "./" */
  17                if (c == '/') {
  18                        path += 2;
  19                        continue;
  20                }
  21                if (c != '.')
  22                        break;
  23                c = path[2];
  24                if (!c)
  25                        path += 2;
  26                else if (c == '/')
  27                        path += 3;
  28                else
  29                        break;
  30                /* ".." and "../" */
  31                /* Remove last component of the prefix */
  32                do {
  33                        if (!len)
  34                                die("'%s' is outside repository", orig);
  35                        len--;
  36                } while (len && prefix[len-1] != '/');
  37                continue;
  38        }
  39        if (len) {
  40                int speclen = strlen(path);
  41                char *n = xmalloc(speclen + len + 1);
  42        
  43                memcpy(n, prefix, len);
  44                memcpy(n + len, path, speclen+1);
  45                path = n;
  46        }
  47        return path;
  48}
  49
  50const char **get_pathspec(const char *prefix, const char **pathspec)
  51{
  52        const char *entry = *pathspec;
  53        const char **p;
  54        int prefixlen;
  55
  56        if (!prefix && !entry)
  57                return NULL;
  58
  59        if (!entry) {
  60                static const char *spec[2];
  61                spec[0] = prefix;
  62                spec[1] = NULL;
  63                return spec;
  64        }
  65
  66        /* Otherwise we have to re-write the entries.. */
  67        p = pathspec;
  68        prefixlen = prefix ? strlen(prefix) : 0;
  69        do {
  70                *p = prefix_path(prefix, prefixlen, entry);
  71        } while ((entry = *++p) != NULL);
  72        return (const char **) pathspec;
  73}
  74
  75/*
  76 * Test it it looks like we're at the top
  77 * level git directory. We want to see a
  78 *
  79 *  - either a .git/objects/ directory _or_ the proper
  80 *    GIT_OBJECT_DIRECTORY environment variable
  81 *  - a refs/ directory under ".git"
  82 *  - either a HEAD symlink or a HEAD file that is formatted as
  83 *    a proper "ref:".
  84 */
  85static int is_toplevel_directory(void)
  86{
  87        if (access(".git/refs/", X_OK) ||
  88            access(getenv(DB_ENVIRONMENT) ?
  89                   getenv(DB_ENVIRONMENT) : ".git/objects/", X_OK) ||
  90            validate_symref(".git/HEAD"))
  91                return 0;
  92        return 1;
  93}
  94
  95const char *setup_git_directory(void)
  96{
  97        static char cwd[PATH_MAX+1];
  98        int len, offset;
  99
 100        /*
 101         * If GIT_DIR is set explicitly, we're not going
 102         * to do any discovery
 103         */
 104        if (getenv(GIT_DIR_ENVIRONMENT))
 105                return NULL;
 106
 107        if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/')
 108                die("Unable to read current working directory");
 109
 110        offset = len = strlen(cwd);
 111        for (;;) {
 112                if (is_toplevel_directory())
 113                        break;
 114                chdir("..");
 115                do {
 116                        if (!offset)
 117                                die("Not a git repository");
 118                } while (cwd[--offset] != '/');
 119        }
 120
 121        if (offset == len)
 122                return NULL;
 123
 124        /* Make "offset" point to past the '/', and add a '/' at the end */
 125        offset++;
 126        cwd[len++] = '/';
 127        cwd[len] = 0;
 128        return cwd + offset;
 129}