Merge commit 'junio/next' into next
[git/platforms/storm.git] / path.c
blob6049ee1436c6e90b5a3ec82f6f3e00005a332585
1 /*
2 * I'm tired of doing "vsnprintf()" etc just to open a
3 * file, so here's a "return static buffer with printf"
4 * interface for paths.
6 * It's obviously not thread-safe. Sue me. But it's quite
7 * useful for doing things like
9 * f = open(mkpath("%s/%s.git", base, name), O_RDONLY);
11 * which is what it's designed for.
13 #include "cache.h"
15 static char bad_path[] = "/bad-path/";
17 static char *get_pathname(void)
19 static char pathname_array[4][PATH_MAX];
20 static int index;
21 return pathname_array[3 & ++index];
24 static char *cleanup_path(char *path)
26 /* Clean it up */
27 if (!memcmp(path, "./", 2)) {
28 path += 2;
29 while (*path == '/')
30 path++;
32 return path;
35 char *mkpath(const char *fmt, ...)
37 va_list args;
38 unsigned len;
39 char *pathname = get_pathname();
41 va_start(args, fmt);
42 len = vsnprintf(pathname, PATH_MAX, fmt, args);
43 va_end(args);
44 if (len >= PATH_MAX)
45 return bad_path;
46 return cleanup_path(pathname);
49 char *git_path(const char *fmt, ...)
51 const char *git_dir = get_git_dir();
52 char *pathname = get_pathname();
53 va_list args;
54 unsigned len;
56 len = strlen(git_dir);
57 if (len > PATH_MAX-100)
58 return bad_path;
59 memcpy(pathname, git_dir, len);
60 if (len && git_dir[len-1] != '/')
61 pathname[len++] = '/';
62 va_start(args, fmt);
63 len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args);
64 va_end(args);
65 if (len >= PATH_MAX)
66 return bad_path;
67 return cleanup_path(pathname);
71 /* git_mkstemp() - create tmp file honoring TMPDIR variable */
72 int git_mkstemp(char *path, size_t len, const char *template)
74 const char *tmp;
75 size_t n;
77 tmp = getenv("TMPDIR");
78 #ifdef __MINGW32__
79 /* on Windows it is TMP and TEMP */
80 if (!tmp)
81 tmp = getenv("TMP");
82 if (!tmp)
83 tmp = getenv("TEMP");
84 #endif
85 if (!tmp)
86 tmp = "/tmp";
87 n = snprintf(path, len, "%s/%s", tmp, template);
88 if (len <= n) {
89 errno = ENAMETOOLONG;
90 return -1;
92 return mkstemp(path);
96 int validate_headref(const char *path)
98 struct stat st;
99 char *buf, buffer[256];
100 unsigned char sha1[20];
101 int fd;
102 ssize_t len;
104 if (lstat(path, &st) < 0)
105 return -1;
107 /* Make sure it is a "refs/.." symlink */
108 if (S_ISLNK(st.st_mode)) {
109 len = readlink(path, buffer, sizeof(buffer)-1);
110 if (len >= 5 && !memcmp("refs/", buffer, 5))
111 return 0;
112 return -1;
116 * Anything else, just open it and try to see if it is a symbolic ref.
118 fd = open(path, O_RDONLY);
119 if (fd < 0)
120 return -1;
121 len = read_in_full(fd, buffer, sizeof(buffer)-1);
122 close(fd);
125 * Is it a symbolic ref?
127 if (len < 4)
128 return -1;
129 if (!memcmp("ref:", buffer, 4)) {
130 buf = buffer + 4;
131 len -= 4;
132 while (len && isspace(*buf))
133 buf++, len--;
134 if (len >= 5 && !memcmp("refs/", buf, 5))
135 return 0;
139 * Is this a detached HEAD?
141 if (!get_sha1_hex(buffer, sha1))
142 return 0;
144 return -1;
147 static char *user_path(char *buf, char *path, int sz)
149 struct passwd *pw;
150 char *slash;
151 int len, baselen;
153 if (!path || path[0] != '~')
154 return NULL;
155 path++;
156 slash = strchr(path, '/');
157 if (path[0] == '/' || !path[0]) {
158 pw = getpwuid(getuid());
160 else {
161 if (slash) {
162 *slash = 0;
163 pw = getpwnam(path);
164 *slash = '/';
166 else
167 pw = getpwnam(path);
169 if (!pw || !pw->pw_dir || sz <= strlen(pw->pw_dir))
170 return NULL;
171 baselen = strlen(pw->pw_dir);
172 memcpy(buf, pw->pw_dir, baselen);
173 while ((1 < baselen) && (buf[baselen-1] == '/')) {
174 buf[baselen-1] = 0;
175 baselen--;
177 if (slash && slash[1]) {
178 len = strlen(slash);
179 if (sz <= baselen + len)
180 return NULL;
181 memcpy(buf + baselen, slash, len + 1);
183 return buf;
187 * First, one directory to try is determined by the following algorithm.
189 * (0) If "strict" is given, the path is used as given and no DWIM is
190 * done. Otherwise:
191 * (1) "~/path" to mean path under the running user's home directory;
192 * (2) "~user/path" to mean path under named user's home directory;
193 * (3) "relative/path" to mean cwd relative directory; or
194 * (4) "/absolute/path" to mean absolute directory.
196 * Unless "strict" is given, we try access() for existence of "%s.git/.git",
197 * "%s/.git", "%s.git", "%s" in this order. The first one that exists is
198 * what we try.
200 * Second, we try chdir() to that. Upon failure, we return NULL.
202 * Then, we try if the current directory is a valid git repository.
203 * Upon failure, we return NULL.
205 * If all goes well, we return the directory we used to chdir() (but
206 * before ~user is expanded), avoiding getcwd() resolving symbolic
207 * links. User relative paths are also returned as they are given,
208 * except DWIM suffixing.
210 char *enter_repo(char *path, int strict)
212 static char used_path[PATH_MAX];
213 static char validated_path[PATH_MAX];
215 if (!path)
216 return NULL;
218 if (!strict) {
219 static const char *suffix[] = {
220 ".git/.git", "/.git", ".git", "", NULL,
222 int len = strlen(path);
223 int i;
224 while ((1 < len) && (path[len-1] == '/')) {
225 path[len-1] = 0;
226 len--;
228 if (PATH_MAX <= len)
229 return NULL;
230 if (path[0] == '~') {
231 if (!user_path(used_path, path, PATH_MAX))
232 return NULL;
233 strcpy(validated_path, path);
234 path = used_path;
236 else if (PATH_MAX - 10 < len)
237 return NULL;
238 else {
239 path = strcpy(used_path, path);
240 strcpy(validated_path, path);
242 len = strlen(path);
243 for (i = 0; suffix[i]; i++) {
244 strcpy(path + len, suffix[i]);
245 if (!access(path, F_OK)) {
246 strcat(validated_path, suffix[i]);
247 break;
250 if (!suffix[i] || chdir(path))
251 return NULL;
252 path = validated_path;
254 else if (chdir(path))
255 return NULL;
257 if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
258 validate_headref("HEAD") == 0) {
259 setenv(GIT_DIR_ENVIRONMENT, ".", 1);
260 check_repository_format();
261 return path;
264 return NULL;
267 int adjust_shared_perm(const char *path)
269 struct stat st;
270 int mode;
272 if (!shared_repository)
273 return 0;
274 if (lstat(path, &st) < 0)
275 return -1;
276 mode = st.st_mode;
278 if (shared_repository) {
279 int tweak = shared_repository;
280 if (!(mode & S_IWUSR))
281 tweak &= ~0222;
282 mode = (mode & ~0777) | tweak;
283 } else {
284 /* Preserve old PERM_UMASK behaviour */
285 if (mode & S_IWUSR)
286 mode |= S_IWGRP;
289 if (S_ISDIR(mode)) {
290 mode |= FORCE_DIR_SET_GID;
292 /* Copy read bits to execute bits */
293 mode |= (shared_repository & 0444) >> 2;
296 if ((mode & st.st_mode) != mode && chmod(path, mode) < 0)
297 return -2;
298 return 0;
301 /* We allow "recursive" symbolic links. Only within reason, though. */
302 #define MAXDEPTH 5
304 const char *make_absolute_path(const char *path)
306 static char bufs[2][PATH_MAX + 1], *buf = bufs[0], *next_buf = bufs[1];
307 char cwd[1024] = "";
308 int buf_index = 1, len;
310 int depth = MAXDEPTH;
311 char *last_elem = NULL;
312 struct stat st;
314 if (strlcpy(buf, path, PATH_MAX) >= PATH_MAX)
315 die ("Too long path: %.*s", 60, path);
317 while (depth--) {
318 if (stat(buf, &st) || !S_ISDIR(st.st_mode)) {
319 char *last_slash = strrchr(buf, '/');
320 if (last_slash) {
321 *last_slash = '\0';
322 last_elem = xstrdup(last_slash + 1);
323 } else {
324 last_elem = xstrdup(buf);
325 *buf = '\0';
329 if (*buf) {
330 if (!*cwd && !getcwd(cwd, sizeof(cwd)))
331 die ("Could not get current working directory");
333 if (chdir(buf))
334 die ("Could not switch to '%s'", buf);
336 if (!getcwd(buf, PATH_MAX))
337 die ("Could not get current working directory");
339 if (last_elem) {
340 int len = strlen(buf);
341 if (len + strlen(last_elem) + 2 > PATH_MAX)
342 die ("Too long path name: '%s/%s'",
343 buf, last_elem);
344 buf[len] = '/';
345 strcpy(buf + len + 1, last_elem);
346 free(last_elem);
347 last_elem = NULL;
350 if (!lstat(buf, &st) && S_ISLNK(st.st_mode)) {
351 len = readlink(buf, next_buf, PATH_MAX);
352 if (len < 0)
353 die ("Invalid symlink: %s", buf);
354 next_buf[len] = '\0';
355 buf = next_buf;
356 buf_index = 1 - buf_index;
357 next_buf = bufs[buf_index];
358 } else
359 break;
362 if (*cwd && chdir(cwd))
363 die ("Could not change back to '%s'", cwd);
365 return buf;
368 char *make_native_separator(char* path) {
369 #ifdef __MINGW32__
370 char* c;
371 for (c = path; *c; c++) {
372 if (*c == '/')
373 *c = '\\';
375 return path;
376 #else
377 return path;
378 #endif