setup.con commit fetch-clone progress: finishing touches. (21fcd1b)
   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
  50/* 
  51 * Unlike prefix_path, this should be used if the named file does
  52 * not have to interact with index entry; i.e. name of a random file
  53 * on the filesystem.
  54 */
  55const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
  56{
  57        static char path[PATH_MAX];
  58        if (!pfx || !*pfx || arg[0] == '/')
  59                return arg;
  60        memcpy(path, pfx, pfx_len);
  61        strcpy(path + pfx_len, arg);
  62        return path;
  63}
  64
  65const char **get_pathspec(const char *prefix, const char **pathspec)
  66{
  67        const char *entry = *pathspec;
  68        const char **p;
  69        int prefixlen;
  70
  71        if (!prefix && !entry)
  72                return NULL;
  73
  74        if (!entry) {
  75                static const char *spec[2];
  76                spec[0] = prefix;
  77                spec[1] = NULL;
  78                return spec;
  79        }
  80
  81        /* Otherwise we have to re-write the entries.. */
  82        p = pathspec;
  83        prefixlen = prefix ? strlen(prefix) : 0;
  84        do {
  85                *p = prefix_path(prefix, prefixlen, entry);
  86        } while ((entry = *++p) != NULL);
  87        return (const char **) pathspec;
  88}
  89
  90/*
  91 * Test if it looks like we're at the top level git directory.
  92 * We want to see:
  93 *
  94 *  - either a .git/objects/ directory _or_ the proper
  95 *    GIT_OBJECT_DIRECTORY environment variable
  96 *  - a refs/ directory under ".git"
  97 *  - either a HEAD symlink or a HEAD file that is formatted as
  98 *    a proper "ref:".
  99 */
 100static int is_toplevel_directory(void)
 101{
 102        if (access(".git/refs/", X_OK) ||
 103            access(getenv(DB_ENVIRONMENT) ?
 104                   getenv(DB_ENVIRONMENT) : ".git/objects/", X_OK) ||
 105            validate_symref(".git/HEAD"))
 106                return 0;
 107        return 1;
 108}
 109
 110const char *setup_git_directory_gently(int *nongit_ok)
 111{
 112        static char cwd[PATH_MAX+1];
 113        int len, offset;
 114
 115        /*
 116         * If GIT_DIR is set explicitly, we're not going
 117         * to do any discovery, but we still do repository
 118         * validation.
 119         */
 120        if (getenv(GIT_DIR_ENVIRONMENT)) {
 121                char path[PATH_MAX];
 122                int len = strlen(getenv(GIT_DIR_ENVIRONMENT));
 123                if (sizeof(path) - 40 < len)
 124                        die("'$%s' too big", GIT_DIR_ENVIRONMENT);
 125                memcpy(path, getenv(GIT_DIR_ENVIRONMENT), len);
 126                
 127                strcpy(path + len, "/refs");
 128                if (access(path, X_OK))
 129                        goto bad_dir_environ;
 130                strcpy(path + len, "/HEAD");
 131                if (validate_symref(path))
 132                        goto bad_dir_environ;
 133                if (getenv(DB_ENVIRONMENT)) {
 134                        if (access(getenv(DB_ENVIRONMENT), X_OK))
 135                                goto bad_dir_environ;
 136                }
 137                else {
 138                        strcpy(path + len, "/objects");
 139                        if (access(path, X_OK))
 140                                goto bad_dir_environ;
 141                }
 142                return NULL;
 143        bad_dir_environ:
 144                path[len] = 0;
 145                die("Not a git repository: '%s'", path);
 146        }
 147
 148        if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/')
 149                die("Unable to read current working directory");
 150
 151        offset = len = strlen(cwd);
 152        for (;;) {
 153                if (is_toplevel_directory())
 154                        break;
 155                chdir("..");
 156                do {
 157                        if (!offset) {
 158                                if (nongit_ok) {
 159                                        if (chdir(cwd))
 160                                                die("Cannot come back to cwd");
 161                                        *nongit_ok = 1;
 162                                        return NULL;
 163                                }
 164                                die("Not a git repository");
 165                        }
 166                } while (cwd[--offset] != '/');
 167        }
 168
 169        if (offset == len)
 170                return NULL;
 171
 172        /* Make "offset" point to past the '/', and add a '/' at the end */
 173        offset++;
 174        cwd[len++] = '/';
 175        cwd[len] = 0;
 176        return cwd + offset;
 177}
 178
 179int check_repository_format_version(const char *var, const char *value)
 180{
 181       if (strcmp(var, "core.repositoryformatversion") == 0)
 182               repository_format_version = git_config_int(var, value);
 183        else if (strcmp(var, "core.sharedrepository") == 0)
 184                shared_repository = git_config_bool(var, value);
 185       return 0;
 186}
 187
 188int check_repository_format(void)
 189{
 190        git_config(check_repository_format_version);
 191        if (GIT_REPO_VERSION < repository_format_version)
 192                die ("Expected git repo version <= %d, found %d",
 193                     GIT_REPO_VERSION, repository_format_version);
 194        return 0;
 195}
 196
 197const char *setup_git_directory(void)
 198{
 199        const char *retval = setup_git_directory_gently(NULL);
 200        check_repository_format();
 201        return retval;
 202}