]>
Commit | Line | Data |
---|---|---|
d288a700 LT |
1 | #include "cache.h" |
2 | ||
6b5ee137 | 3 | const char *prefix_path(const char *prefix, int len, const char *path) |
f332726e | 4 | { |
6b5ee137 | 5 | const char *orig = path; |
f332726e LT |
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 | ||
6b5ee137 | 50 | const char **get_pathspec(const char *prefix, const char **pathspec) |
d288a700 | 51 | { |
6b5ee137 JH |
52 | const char *entry = *pathspec; |
53 | const char **p; | |
d288a700 LT |
54 | int prefixlen; |
55 | ||
f332726e LT |
56 | if (!prefix && !entry) |
57 | return NULL; | |
d288a700 LT |
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.. */ | |
d288a700 | 67 | p = pathspec; |
f332726e | 68 | prefixlen = prefix ? strlen(prefix) : 0; |
d288a700 | 69 | do { |
f332726e | 70 | *p = prefix_path(prefix, prefixlen, entry); |
d288a700 LT |
71 | } while ((entry = *++p) != NULL); |
72 | return (const char **) pathspec; | |
73 | } | |
74 | ||
5f5608bc LT |
75 | /* |
76 | * Test it it looks like we're at the top | |
77 | * level git directory. We want to see a | |
78 | * | |
5f5608bc LT |
79 | * - either a .git/objects/ directory _or_ the proper |
80 | * GIT_OBJECT_DIRECTORY environment variable | |
8098a178 JH |
81 | * - a refs/ directory under ".git" |
82 | * - either a HEAD symlink or a HEAD file that is formatted as | |
83 | * a proper "ref:". | |
5f5608bc LT |
84 | */ |
85 | static int is_toplevel_directory(void) | |
86 | { | |
8098a178 JH |
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; | |
5f5608bc LT |
93 | } |
94 | ||
d288a700 LT |
95 | const 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 | */ | |
a9ab586a | 104 | if (getenv(GIT_DIR_ENVIRONMENT)) |
d288a700 LT |
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 (;;) { | |
5f5608bc LT |
112 | if (is_toplevel_directory()) |
113 | break; | |
d288a700 LT |
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 | } |