setup.con commit Fix "git diff --stat" with long filenames (5d6a9f4)
   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
  65/*
  66 * Verify a filename that we got as an argument for a pathspec
  67 * entry. Note that a filename that begins with "-" never verifies
  68 * as true, because even if such a filename were to exist, we want
  69 * it to be preceded by the "--" marker (or we want the user to
  70 * use a format like "./-filename")
  71 */
  72void verify_filename(const char *prefix, const char *arg)
  73{
  74        const char *name;
  75        struct stat st;
  76
  77        if (*arg == '-')
  78                die("bad flag '%s' used after filename", arg);
  79        name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
  80        if (!lstat(name, &st))
  81                return;
  82        if (errno == ENOENT)
  83                die("ambiguous argument '%s': unknown revision or filename\n"
  84                    "Use '--' to separate filenames from revisions", arg);
  85        die("'%s': %s", arg, strerror(errno));
  86}
  87
  88const char **get_pathspec(const char *prefix, const char **pathspec)
  89{
  90        const char *entry = *pathspec;
  91        const char **p;
  92        int prefixlen;
  93
  94        if (!prefix && !entry)
  95                return NULL;
  96
  97        if (!entry) {
  98                static const char *spec[2];
  99                spec[0] = prefix;
 100                spec[1] = NULL;
 101                return spec;
 102        }
 103
 104        /* Otherwise we have to re-write the entries.. */
 105        p = pathspec;
 106        prefixlen = prefix ? strlen(prefix) : 0;
 107        do {
 108                *p = prefix_path(prefix, prefixlen, entry);
 109        } while ((entry = *++p) != NULL);
 110        return (const char **) pathspec;
 111}
 112
 113/*
 114 * Test if it looks like we're at the top level git directory.
 115 * We want to see:
 116 *
 117 *  - either a .git/objects/ directory _or_ the proper
 118 *    GIT_OBJECT_DIRECTORY environment variable
 119 *  - a refs/ directory under ".git"
 120 *  - either a HEAD symlink or a HEAD file that is formatted as
 121 *    a proper "ref:".
 122 */
 123static int is_toplevel_directory(void)
 124{
 125        if (access(".git/refs/", X_OK) ||
 126            access(getenv(DB_ENVIRONMENT) ?
 127                   getenv(DB_ENVIRONMENT) : ".git/objects/", X_OK) ||
 128            validate_symref(".git/HEAD"))
 129                return 0;
 130        return 1;
 131}
 132
 133const char *setup_git_directory_gently(int *nongit_ok)
 134{
 135        static char cwd[PATH_MAX+1];
 136        int len, offset;
 137
 138        /*
 139         * If GIT_DIR is set explicitly, we're not going
 140         * to do any discovery, but we still do repository
 141         * validation.
 142         */
 143        if (getenv(GIT_DIR_ENVIRONMENT)) {
 144                char path[PATH_MAX];
 145                int len = strlen(getenv(GIT_DIR_ENVIRONMENT));
 146                if (sizeof(path) - 40 < len)
 147                        die("'$%s' too big", GIT_DIR_ENVIRONMENT);
 148                memcpy(path, getenv(GIT_DIR_ENVIRONMENT), len);
 149                
 150                strcpy(path + len, "/refs");
 151                if (access(path, X_OK))
 152                        goto bad_dir_environ;
 153                strcpy(path + len, "/HEAD");
 154                if (validate_symref(path))
 155                        goto bad_dir_environ;
 156                if (getenv(DB_ENVIRONMENT)) {
 157                        if (access(getenv(DB_ENVIRONMENT), X_OK))
 158                                goto bad_dir_environ;
 159                }
 160                else {
 161                        strcpy(path + len, "/objects");
 162                        if (access(path, X_OK))
 163                                goto bad_dir_environ;
 164                }
 165                return NULL;
 166        bad_dir_environ:
 167                path[len] = 0;
 168                die("Not a git repository: '%s'", path);
 169        }
 170
 171        if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/')
 172                die("Unable to read current working directory");
 173
 174        offset = len = strlen(cwd);
 175        for (;;) {
 176                if (is_toplevel_directory())
 177                        break;
 178                chdir("..");
 179                do {
 180                        if (!offset) {
 181                                if (nongit_ok) {
 182                                        if (chdir(cwd))
 183                                                die("Cannot come back to cwd");
 184                                        *nongit_ok = 1;
 185                                        return NULL;
 186                                }
 187                                die("Not a git repository");
 188                        }
 189                } while (cwd[--offset] != '/');
 190        }
 191
 192        if (offset == len)
 193                return NULL;
 194
 195        /* Make "offset" point to past the '/', and add a '/' at the end */
 196        offset++;
 197        cwd[len++] = '/';
 198        cwd[len] = 0;
 199        return cwd + offset;
 200}
 201
 202int check_repository_format_version(const char *var, const char *value)
 203{
 204       if (strcmp(var, "core.repositoryformatversion") == 0)
 205               repository_format_version = git_config_int(var, value);
 206        else if (strcmp(var, "core.sharedrepository") == 0)
 207                shared_repository = git_config_bool(var, value);
 208       return 0;
 209}
 210
 211int check_repository_format(void)
 212{
 213        git_config(check_repository_format_version);
 214        if (GIT_REPO_VERSION < repository_format_version)
 215                die ("Expected git repo version <= %d, found %d",
 216                     GIT_REPO_VERSION, repository_format_version);
 217        return 0;
 218}
 219
 220const char *setup_git_directory(void)
 221{
 222        const char *retval = setup_git_directory_gently(NULL);
 223        check_repository_format();
 224        return retval;
 225}