Imported Upstream version 2.5.0
[platform/upstream/git.git] / path.c
1 /*
2  * Utilities for paths and pathnames
3  */
4 #include "cache.h"
5 #include "strbuf.h"
6 #include "string-list.h"
7 #include "dir.h"
8
9 static int get_st_mode_bits(const char *path, int *mode)
10 {
11         struct stat st;
12         if (lstat(path, &st) < 0)
13                 return -1;
14         *mode = st.st_mode;
15         return 0;
16 }
17
18 static char bad_path[] = "/bad-path/";
19
20 static struct strbuf *get_pathname(void)
21 {
22         static struct strbuf pathname_array[4] = {
23                 STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
24         };
25         static int index;
26         struct strbuf *sb = &pathname_array[3 & ++index];
27         strbuf_reset(sb);
28         return sb;
29 }
30
31 static char *cleanup_path(char *path)
32 {
33         /* Clean it up */
34         if (!memcmp(path, "./", 2)) {
35                 path += 2;
36                 while (*path == '/')
37                         path++;
38         }
39         return path;
40 }
41
42 static void strbuf_cleanup_path(struct strbuf *sb)
43 {
44         char *path = cleanup_path(sb->buf);
45         if (path > sb->buf)
46                 strbuf_remove(sb, 0, path - sb->buf);
47 }
48
49 char *mksnpath(char *buf, size_t n, const char *fmt, ...)
50 {
51         va_list args;
52         unsigned len;
53
54         va_start(args, fmt);
55         len = vsnprintf(buf, n, fmt, args);
56         va_end(args);
57         if (len >= n) {
58                 strlcpy(buf, bad_path, n);
59                 return buf;
60         }
61         return cleanup_path(buf);
62 }
63
64 static int dir_prefix(const char *buf, const char *dir)
65 {
66         int len = strlen(dir);
67         return !strncmp(buf, dir, len) &&
68                 (is_dir_sep(buf[len]) || buf[len] == '\0');
69 }
70
71 /* $buf =~ m|$dir/+$file| but without regex */
72 static int is_dir_file(const char *buf, const char *dir, const char *file)
73 {
74         int len = strlen(dir);
75         if (strncmp(buf, dir, len) || !is_dir_sep(buf[len]))
76                 return 0;
77         while (is_dir_sep(buf[len]))
78                 len++;
79         return !strcmp(buf + len, file);
80 }
81
82 static void replace_dir(struct strbuf *buf, int len, const char *newdir)
83 {
84         int newlen = strlen(newdir);
85         int need_sep = (buf->buf[len] && !is_dir_sep(buf->buf[len])) &&
86                 !is_dir_sep(newdir[newlen - 1]);
87         if (need_sep)
88                 len--;   /* keep one char, to be replaced with '/'  */
89         strbuf_splice(buf, 0, len, newdir, newlen);
90         if (need_sep)
91                 buf->buf[newlen] = '/';
92 }
93
94 static const char *common_list[] = {
95         "/branches", "/hooks", "/info", "!/logs", "/lost-found",
96         "/objects", "/refs", "/remotes", "/worktrees", "/rr-cache", "/svn",
97         "config", "!gc.pid", "packed-refs", "shallow",
98         NULL
99 };
100
101 static void update_common_dir(struct strbuf *buf, int git_dir_len)
102 {
103         char *base = buf->buf + git_dir_len;
104         const char **p;
105
106         if (is_dir_file(base, "logs", "HEAD") ||
107             is_dir_file(base, "info", "sparse-checkout"))
108                 return; /* keep this in $GIT_DIR */
109         for (p = common_list; *p; p++) {
110                 const char *path = *p;
111                 int is_dir = 0;
112                 if (*path == '!')
113                         path++;
114                 if (*path == '/') {
115                         path++;
116                         is_dir = 1;
117                 }
118                 if (is_dir && dir_prefix(base, path)) {
119                         replace_dir(buf, git_dir_len, get_git_common_dir());
120                         return;
121                 }
122                 if (!is_dir && !strcmp(base, path)) {
123                         replace_dir(buf, git_dir_len, get_git_common_dir());
124                         return;
125                 }
126         }
127 }
128
129 void report_linked_checkout_garbage(void)
130 {
131         struct strbuf sb = STRBUF_INIT;
132         const char **p;
133         int len;
134
135         if (!git_common_dir_env)
136                 return;
137         strbuf_addf(&sb, "%s/", get_git_dir());
138         len = sb.len;
139         for (p = common_list; *p; p++) {
140                 const char *path = *p;
141                 if (*path == '!')
142                         continue;
143                 strbuf_setlen(&sb, len);
144                 strbuf_addstr(&sb, path);
145                 if (file_exists(sb.buf))
146                         report_garbage("unused in linked checkout", sb.buf);
147         }
148         strbuf_release(&sb);
149 }
150
151 static void adjust_git_path(struct strbuf *buf, int git_dir_len)
152 {
153         const char *base = buf->buf + git_dir_len;
154         if (git_graft_env && is_dir_file(base, "info", "grafts"))
155                 strbuf_splice(buf, 0, buf->len,
156                               get_graft_file(), strlen(get_graft_file()));
157         else if (git_index_env && !strcmp(base, "index"))
158                 strbuf_splice(buf, 0, buf->len,
159                               get_index_file(), strlen(get_index_file()));
160         else if (git_db_env && dir_prefix(base, "objects"))
161                 replace_dir(buf, git_dir_len + 7, get_object_directory());
162         else if (git_common_dir_env)
163                 update_common_dir(buf, git_dir_len);
164 }
165
166 static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)
167 {
168         int gitdir_len;
169         strbuf_addstr(buf, get_git_dir());
170         if (buf->len && !is_dir_sep(buf->buf[buf->len - 1]))
171                 strbuf_addch(buf, '/');
172         gitdir_len = buf->len;
173         strbuf_vaddf(buf, fmt, args);
174         adjust_git_path(buf, gitdir_len);
175         strbuf_cleanup_path(buf);
176 }
177
178 void strbuf_git_path(struct strbuf *sb, const char *fmt, ...)
179 {
180         va_list args;
181         va_start(args, fmt);
182         do_git_path(sb, fmt, args);
183         va_end(args);
184 }
185
186 const char *git_path(const char *fmt, ...)
187 {
188         struct strbuf *pathname = get_pathname();
189         va_list args;
190         va_start(args, fmt);
191         do_git_path(pathname, fmt, args);
192         va_end(args);
193         return pathname->buf;
194 }
195
196 char *git_pathdup(const char *fmt, ...)
197 {
198         struct strbuf path = STRBUF_INIT;
199         va_list args;
200         va_start(args, fmt);
201         do_git_path(&path, fmt, args);
202         va_end(args);
203         return strbuf_detach(&path, NULL);
204 }
205
206 char *mkpathdup(const char *fmt, ...)
207 {
208         struct strbuf sb = STRBUF_INIT;
209         va_list args;
210         va_start(args, fmt);
211         strbuf_vaddf(&sb, fmt, args);
212         va_end(args);
213         strbuf_cleanup_path(&sb);
214         return strbuf_detach(&sb, NULL);
215 }
216
217 const char *mkpath(const char *fmt, ...)
218 {
219         va_list args;
220         struct strbuf *pathname = get_pathname();
221         va_start(args, fmt);
222         strbuf_vaddf(pathname, fmt, args);
223         va_end(args);
224         return cleanup_path(pathname->buf);
225 }
226
227 const char *git_path_submodule(const char *path, const char *fmt, ...)
228 {
229         struct strbuf *buf = get_pathname();
230         const char *git_dir;
231         va_list args;
232
233         strbuf_addstr(buf, path);
234         if (buf->len && buf->buf[buf->len - 1] != '/')
235                 strbuf_addch(buf, '/');
236         strbuf_addstr(buf, ".git");
237
238         git_dir = read_gitfile(buf->buf);
239         if (git_dir) {
240                 strbuf_reset(buf);
241                 strbuf_addstr(buf, git_dir);
242         }
243         strbuf_addch(buf, '/');
244
245         va_start(args, fmt);
246         strbuf_vaddf(buf, fmt, args);
247         va_end(args);
248         strbuf_cleanup_path(buf);
249         return buf->buf;
250 }
251
252 int validate_headref(const char *path)
253 {
254         struct stat st;
255         char *buf, buffer[256];
256         unsigned char sha1[20];
257         int fd;
258         ssize_t len;
259
260         if (lstat(path, &st) < 0)
261                 return -1;
262
263         /* Make sure it is a "refs/.." symlink */
264         if (S_ISLNK(st.st_mode)) {
265                 len = readlink(path, buffer, sizeof(buffer)-1);
266                 if (len >= 5 && !memcmp("refs/", buffer, 5))
267                         return 0;
268                 return -1;
269         }
270
271         /*
272          * Anything else, just open it and try to see if it is a symbolic ref.
273          */
274         fd = open(path, O_RDONLY);
275         if (fd < 0)
276                 return -1;
277         len = read_in_full(fd, buffer, sizeof(buffer)-1);
278         close(fd);
279
280         /*
281          * Is it a symbolic ref?
282          */
283         if (len < 4)
284                 return -1;
285         if (!memcmp("ref:", buffer, 4)) {
286                 buf = buffer + 4;
287                 len -= 4;
288                 while (len && isspace(*buf))
289                         buf++, len--;
290                 if (len >= 5 && !memcmp("refs/", buf, 5))
291                         return 0;
292         }
293
294         /*
295          * Is this a detached HEAD?
296          */
297         if (!get_sha1_hex(buffer, sha1))
298                 return 0;
299
300         return -1;
301 }
302
303 static struct passwd *getpw_str(const char *username, size_t len)
304 {
305         struct passwd *pw;
306         char *username_z = xmemdupz(username, len);
307         pw = getpwnam(username_z);
308         free(username_z);
309         return pw;
310 }
311
312 /*
313  * Return a string with ~ and ~user expanded via getpw*.  If buf != NULL,
314  * then it is a newly allocated string. Returns NULL on getpw failure or
315  * if path is NULL.
316  */
317 char *expand_user_path(const char *path)
318 {
319         struct strbuf user_path = STRBUF_INIT;
320         const char *to_copy = path;
321
322         if (path == NULL)
323                 goto return_null;
324         if (path[0] == '~') {
325                 const char *first_slash = strchrnul(path, '/');
326                 const char *username = path + 1;
327                 size_t username_len = first_slash - username;
328                 if (username_len == 0) {
329                         const char *home = getenv("HOME");
330                         if (!home)
331                                 goto return_null;
332                         strbuf_addstr(&user_path, home);
333                 } else {
334                         struct passwd *pw = getpw_str(username, username_len);
335                         if (!pw)
336                                 goto return_null;
337                         strbuf_addstr(&user_path, pw->pw_dir);
338                 }
339                 to_copy = first_slash;
340         }
341         strbuf_addstr(&user_path, to_copy);
342         return strbuf_detach(&user_path, NULL);
343 return_null:
344         strbuf_release(&user_path);
345         return NULL;
346 }
347
348 /*
349  * First, one directory to try is determined by the following algorithm.
350  *
351  * (0) If "strict" is given, the path is used as given and no DWIM is
352  *     done. Otherwise:
353  * (1) "~/path" to mean path under the running user's home directory;
354  * (2) "~user/path" to mean path under named user's home directory;
355  * (3) "relative/path" to mean cwd relative directory; or
356  * (4) "/absolute/path" to mean absolute directory.
357  *
358  * Unless "strict" is given, we check "%s/.git", "%s", "%s.git/.git", "%s.git"
359  * in this order. We select the first one that is a valid git repository, and
360  * chdir() to it. If none match, or we fail to chdir, we return NULL.
361  *
362  * If all goes well, we return the directory we used to chdir() (but
363  * before ~user is expanded), avoiding getcwd() resolving symbolic
364  * links.  User relative paths are also returned as they are given,
365  * except DWIM suffixing.
366  */
367 const char *enter_repo(const char *path, int strict)
368 {
369         static char used_path[PATH_MAX];
370         static char validated_path[PATH_MAX];
371
372         if (!path)
373                 return NULL;
374
375         if (!strict) {
376                 static const char *suffix[] = {
377                         "/.git", "", ".git/.git", ".git", NULL,
378                 };
379                 const char *gitfile;
380                 int len = strlen(path);
381                 int i;
382                 while ((1 < len) && (path[len-1] == '/'))
383                         len--;
384
385                 if (PATH_MAX <= len)
386                         return NULL;
387                 strncpy(used_path, path, len); used_path[len] = 0 ;
388                 strcpy(validated_path, used_path);
389
390                 if (used_path[0] == '~') {
391                         char *newpath = expand_user_path(used_path);
392                         if (!newpath || (PATH_MAX - 10 < strlen(newpath))) {
393                                 free(newpath);
394                                 return NULL;
395                         }
396                         /*
397                          * Copy back into the static buffer. A pity
398                          * since newpath was not bounded, but other
399                          * branches of the if are limited by PATH_MAX
400                          * anyway.
401                          */
402                         strcpy(used_path, newpath); free(newpath);
403                 }
404                 else if (PATH_MAX - 10 < len)
405                         return NULL;
406                 len = strlen(used_path);
407                 for (i = 0; suffix[i]; i++) {
408                         struct stat st;
409                         strcpy(used_path + len, suffix[i]);
410                         if (!stat(used_path, &st) &&
411                             (S_ISREG(st.st_mode) ||
412                             (S_ISDIR(st.st_mode) && is_git_directory(used_path)))) {
413                                 strcat(validated_path, suffix[i]);
414                                 break;
415                         }
416                 }
417                 if (!suffix[i])
418                         return NULL;
419                 gitfile = read_gitfile(used_path) ;
420                 if (gitfile)
421                         strcpy(used_path, gitfile);
422                 if (chdir(used_path))
423                         return NULL;
424                 path = validated_path;
425         }
426         else if (chdir(path))
427                 return NULL;
428
429         if (access("objects", X_OK) == 0 && access("refs", X_OK) == 0 &&
430             validate_headref("HEAD") == 0) {
431                 set_git_dir(".");
432                 check_repository_format();
433                 return path;
434         }
435
436         return NULL;
437 }
438
439 static int calc_shared_perm(int mode)
440 {
441         int tweak;
442
443         if (shared_repository < 0)
444                 tweak = -shared_repository;
445         else
446                 tweak = shared_repository;
447
448         if (!(mode & S_IWUSR))
449                 tweak &= ~0222;
450         if (mode & S_IXUSR)
451                 /* Copy read bits to execute bits */
452                 tweak |= (tweak & 0444) >> 2;
453         if (shared_repository < 0)
454                 mode = (mode & ~0777) | tweak;
455         else
456                 mode |= tweak;
457
458         return mode;
459 }
460
461
462 int adjust_shared_perm(const char *path)
463 {
464         int old_mode, new_mode;
465
466         if (!shared_repository)
467                 return 0;
468         if (get_st_mode_bits(path, &old_mode) < 0)
469                 return -1;
470
471         new_mode = calc_shared_perm(old_mode);
472         if (S_ISDIR(old_mode)) {
473                 /* Copy read bits to execute bits */
474                 new_mode |= (new_mode & 0444) >> 2;
475                 new_mode |= FORCE_DIR_SET_GID;
476         }
477
478         if (((old_mode ^ new_mode) & ~S_IFMT) &&
479                         chmod(path, (new_mode & ~S_IFMT)) < 0)
480                 return -2;
481         return 0;
482 }
483
484 static int have_same_root(const char *path1, const char *path2)
485 {
486         int is_abs1, is_abs2;
487
488         is_abs1 = is_absolute_path(path1);
489         is_abs2 = is_absolute_path(path2);
490         return (is_abs1 && is_abs2 && tolower(path1[0]) == tolower(path2[0])) ||
491                (!is_abs1 && !is_abs2);
492 }
493
494 /*
495  * Give path as relative to prefix.
496  *
497  * The strbuf may or may not be used, so do not assume it contains the
498  * returned path.
499  */
500 const char *relative_path(const char *in, const char *prefix,
501                           struct strbuf *sb)
502 {
503         int in_len = in ? strlen(in) : 0;
504         int prefix_len = prefix ? strlen(prefix) : 0;
505         int in_off = 0;
506         int prefix_off = 0;
507         int i = 0, j = 0;
508
509         if (!in_len)
510                 return "./";
511         else if (!prefix_len)
512                 return in;
513
514         if (have_same_root(in, prefix)) {
515                 /* bypass dos_drive, for "c:" is identical to "C:" */
516                 if (has_dos_drive_prefix(in)) {
517                         i = 2;
518                         j = 2;
519                 }
520         } else {
521                 return in;
522         }
523
524         while (i < prefix_len && j < in_len && prefix[i] == in[j]) {
525                 if (is_dir_sep(prefix[i])) {
526                         while (is_dir_sep(prefix[i]))
527                                 i++;
528                         while (is_dir_sep(in[j]))
529                                 j++;
530                         prefix_off = i;
531                         in_off = j;
532                 } else {
533                         i++;
534                         j++;
535                 }
536         }
537
538         if (
539             /* "prefix" seems like prefix of "in" */
540             i >= prefix_len &&
541             /*
542              * but "/foo" is not a prefix of "/foobar"
543              * (i.e. prefix not end with '/')
544              */
545             prefix_off < prefix_len) {
546                 if (j >= in_len) {
547                         /* in="/a/b", prefix="/a/b" */
548                         in_off = in_len;
549                 } else if (is_dir_sep(in[j])) {
550                         /* in="/a/b/c", prefix="/a/b" */
551                         while (is_dir_sep(in[j]))
552                                 j++;
553                         in_off = j;
554                 } else {
555                         /* in="/a/bbb/c", prefix="/a/b" */
556                         i = prefix_off;
557                 }
558         } else if (
559                    /* "in" is short than "prefix" */
560                    j >= in_len &&
561                    /* "in" not end with '/' */
562                    in_off < in_len) {
563                 if (is_dir_sep(prefix[i])) {
564                         /* in="/a/b", prefix="/a/b/c/" */
565                         while (is_dir_sep(prefix[i]))
566                                 i++;
567                         in_off = in_len;
568                 }
569         }
570         in += in_off;
571         in_len -= in_off;
572
573         if (i >= prefix_len) {
574                 if (!in_len)
575                         return "./";
576                 else
577                         return in;
578         }
579
580         strbuf_reset(sb);
581         strbuf_grow(sb, in_len);
582
583         while (i < prefix_len) {
584                 if (is_dir_sep(prefix[i])) {
585                         strbuf_addstr(sb, "../");
586                         while (is_dir_sep(prefix[i]))
587                                 i++;
588                         continue;
589                 }
590                 i++;
591         }
592         if (!is_dir_sep(prefix[prefix_len - 1]))
593                 strbuf_addstr(sb, "../");
594
595         strbuf_addstr(sb, in);
596
597         return sb->buf;
598 }
599
600 /*
601  * A simpler implementation of relative_path
602  *
603  * Get relative path by removing "prefix" from "in". This function
604  * first appears in v1.5.6-1-g044bbbc, and makes git_dir shorter
605  * to increase performance when traversing the path to work_tree.
606  */
607 const char *remove_leading_path(const char *in, const char *prefix)
608 {
609         static char buf[PATH_MAX + 1];
610         int i = 0, j = 0;
611
612         if (!prefix || !prefix[0])
613                 return in;
614         while (prefix[i]) {
615                 if (is_dir_sep(prefix[i])) {
616                         if (!is_dir_sep(in[j]))
617                                 return in;
618                         while (is_dir_sep(prefix[i]))
619                                 i++;
620                         while (is_dir_sep(in[j]))
621                                 j++;
622                         continue;
623                 } else if (in[j] != prefix[i]) {
624                         return in;
625                 }
626                 i++;
627                 j++;
628         }
629         if (
630             /* "/foo" is a prefix of "/foo" */
631             in[j] &&
632             /* "/foo" is not a prefix of "/foobar" */
633             !is_dir_sep(prefix[i-1]) && !is_dir_sep(in[j])
634            )
635                 return in;
636         while (is_dir_sep(in[j]))
637                 j++;
638         if (!in[j])
639                 strcpy(buf, ".");
640         else
641                 strcpy(buf, in + j);
642         return buf;
643 }
644
645 /*
646  * It is okay if dst == src, but they should not overlap otherwise.
647  *
648  * Performs the following normalizations on src, storing the result in dst:
649  * - Ensures that components are separated by '/' (Windows only)
650  * - Squashes sequences of '/'.
651  * - Removes "." components.
652  * - Removes ".." components, and the components the precede them.
653  * Returns failure (non-zero) if a ".." component appears as first path
654  * component anytime during the normalization. Otherwise, returns success (0).
655  *
656  * Note that this function is purely textual.  It does not follow symlinks,
657  * verify the existence of the path, or make any system calls.
658  *
659  * prefix_len != NULL is for a specific case of prefix_pathspec():
660  * assume that src == dst and src[0..prefix_len-1] is already
661  * normalized, any time "../" eats up to the prefix_len part,
662  * prefix_len is reduced. In the end prefix_len is the remaining
663  * prefix that has not been overridden by user pathspec.
664  */
665 int normalize_path_copy_len(char *dst, const char *src, int *prefix_len)
666 {
667         char *dst0;
668
669         if (has_dos_drive_prefix(src)) {
670                 *dst++ = *src++;
671                 *dst++ = *src++;
672         }
673         dst0 = dst;
674
675         if (is_dir_sep(*src)) {
676                 *dst++ = '/';
677                 while (is_dir_sep(*src))
678                         src++;
679         }
680
681         for (;;) {
682                 char c = *src;
683
684                 /*
685                  * A path component that begins with . could be
686                  * special:
687                  * (1) "." and ends   -- ignore and terminate.
688                  * (2) "./"           -- ignore them, eat slash and continue.
689                  * (3) ".." and ends  -- strip one and terminate.
690                  * (4) "../"          -- strip one, eat slash and continue.
691                  */
692                 if (c == '.') {
693                         if (!src[1]) {
694                                 /* (1) */
695                                 src++;
696                         } else if (is_dir_sep(src[1])) {
697                                 /* (2) */
698                                 src += 2;
699                                 while (is_dir_sep(*src))
700                                         src++;
701                                 continue;
702                         } else if (src[1] == '.') {
703                                 if (!src[2]) {
704                                         /* (3) */
705                                         src += 2;
706                                         goto up_one;
707                                 } else if (is_dir_sep(src[2])) {
708                                         /* (4) */
709                                         src += 3;
710                                         while (is_dir_sep(*src))
711                                                 src++;
712                                         goto up_one;
713                                 }
714                         }
715                 }
716
717                 /* copy up to the next '/', and eat all '/' */
718                 while ((c = *src++) != '\0' && !is_dir_sep(c))
719                         *dst++ = c;
720                 if (is_dir_sep(c)) {
721                         *dst++ = '/';
722                         while (is_dir_sep(c))
723                                 c = *src++;
724                         src--;
725                 } else if (!c)
726                         break;
727                 continue;
728
729         up_one:
730                 /*
731                  * dst0..dst is prefix portion, and dst[-1] is '/';
732                  * go up one level.
733                  */
734                 dst--;  /* go to trailing '/' */
735                 if (dst <= dst0)
736                         return -1;
737                 /* Windows: dst[-1] cannot be backslash anymore */
738                 while (dst0 < dst && dst[-1] != '/')
739                         dst--;
740                 if (prefix_len && *prefix_len > dst - dst0)
741                         *prefix_len = dst - dst0;
742         }
743         *dst = '\0';
744         return 0;
745 }
746
747 int normalize_path_copy(char *dst, const char *src)
748 {
749         return normalize_path_copy_len(dst, src, NULL);
750 }
751
752 /*
753  * path = Canonical absolute path
754  * prefixes = string_list containing normalized, absolute paths without
755  * trailing slashes (except for the root directory, which is denoted by "/").
756  *
757  * Determines, for each path in prefixes, whether the "prefix"
758  * is an ancestor directory of path.  Returns the length of the longest
759  * ancestor directory, excluding any trailing slashes, or -1 if no prefix
760  * is an ancestor.  (Note that this means 0 is returned if prefixes is
761  * ["/"].) "/foo" is not considered an ancestor of "/foobar".  Directories
762  * are not considered to be their own ancestors.  path must be in a
763  * canonical form: empty components, or "." or ".." components are not
764  * allowed.
765  */
766 int longest_ancestor_length(const char *path, struct string_list *prefixes)
767 {
768         int i, max_len = -1;
769
770         if (!strcmp(path, "/"))
771                 return -1;
772
773         for (i = 0; i < prefixes->nr; i++) {
774                 const char *ceil = prefixes->items[i].string;
775                 int len = strlen(ceil);
776
777                 if (len == 1 && ceil[0] == '/')
778                         len = 0; /* root matches anything, with length 0 */
779                 else if (!strncmp(path, ceil, len) && path[len] == '/')
780                         ; /* match of length len */
781                 else
782                         continue; /* no match */
783
784                 if (len > max_len)
785                         max_len = len;
786         }
787
788         return max_len;
789 }
790
791 /* strip arbitrary amount of directory separators at end of path */
792 static inline int chomp_trailing_dir_sep(const char *path, int len)
793 {
794         while (len && is_dir_sep(path[len - 1]))
795                 len--;
796         return len;
797 }
798
799 /*
800  * If path ends with suffix (complete path components), returns the
801  * part before suffix (sans trailing directory separators).
802  * Otherwise returns NULL.
803  */
804 char *strip_path_suffix(const char *path, const char *suffix)
805 {
806         int path_len = strlen(path), suffix_len = strlen(suffix);
807
808         while (suffix_len) {
809                 if (!path_len)
810                         return NULL;
811
812                 if (is_dir_sep(path[path_len - 1])) {
813                         if (!is_dir_sep(suffix[suffix_len - 1]))
814                                 return NULL;
815                         path_len = chomp_trailing_dir_sep(path, path_len);
816                         suffix_len = chomp_trailing_dir_sep(suffix, suffix_len);
817                 }
818                 else if (path[--path_len] != suffix[--suffix_len])
819                         return NULL;
820         }
821
822         if (path_len && !is_dir_sep(path[path_len - 1]))
823                 return NULL;
824         return xstrndup(path, chomp_trailing_dir_sep(path, path_len));
825 }
826
827 int daemon_avoid_alias(const char *p)
828 {
829         int sl, ndot;
830
831         /*
832          * This resurrects the belts and suspenders paranoia check by HPA
833          * done in <435560F7.4080006@zytor.com> thread, now enter_repo()
834          * does not do getcwd() based path canonicalization.
835          *
836          * sl becomes true immediately after seeing '/' and continues to
837          * be true as long as dots continue after that without intervening
838          * non-dot character.
839          */
840         if (!p || (*p != '/' && *p != '~'))
841                 return -1;
842         sl = 1; ndot = 0;
843         p++;
844
845         while (1) {
846                 char ch = *p++;
847                 if (sl) {
848                         if (ch == '.')
849                                 ndot++;
850                         else if (ch == '/') {
851                                 if (ndot < 3)
852                                         /* reject //, /./ and /../ */
853                                         return -1;
854                                 ndot = 0;
855                         }
856                         else if (ch == 0) {
857                                 if (0 < ndot && ndot < 3)
858                                         /* reject /.$ and /..$ */
859                                         return -1;
860                                 return 0;
861                         }
862                         else
863                                 sl = ndot = 0;
864                 }
865                 else if (ch == 0)
866                         return 0;
867                 else if (ch == '/') {
868                         sl = 1;
869                         ndot = 0;
870                 }
871         }
872 }
873
874 static int only_spaces_and_periods(const char *path, size_t len, size_t skip)
875 {
876         if (len < skip)
877                 return 0;
878         len -= skip;
879         path += skip;
880         while (len-- > 0) {
881                 char c = *(path++);
882                 if (c != ' ' && c != '.')
883                         return 0;
884         }
885         return 1;
886 }
887
888 int is_ntfs_dotgit(const char *name)
889 {
890         int len;
891
892         for (len = 0; ; len++)
893                 if (!name[len] || name[len] == '\\' || is_dir_sep(name[len])) {
894                         if (only_spaces_and_periods(name, len, 4) &&
895                                         !strncasecmp(name, ".git", 4))
896                                 return 1;
897                         if (only_spaces_and_periods(name, len, 5) &&
898                                         !strncasecmp(name, "git~1", 5))
899                                 return 1;
900                         if (name[len] != '\\')
901                                 return 0;
902                         name += len + 1;
903                         len = -1;
904                 }
905 }
906
907 char *xdg_config_home(const char *filename)
908 {
909         const char *home, *config_home;
910
911         assert(filename);
912         config_home = getenv("XDG_CONFIG_HOME");
913         if (config_home && *config_home)
914                 return mkpathdup("%s/git/%s", config_home, filename);
915
916         home = getenv("HOME");
917         if (home)
918                 return mkpathdup("%s/.config/git/%s", home, filename);
919         return NULL;
920 }