Code

clean up pathspec matching
[git.git] / symlinks.c
1 #include "cache.h"
3 struct pathname {
4         int len;
5         char path[PATH_MAX];
6 };
8 /* Return matching pathname prefix length, or zero if not matching */
9 static inline int match_pathname(int len, const char *name, struct pathname *match)
10 {
11         int match_len = match->len;
12         return (len > match_len &&
13                 name[match_len] == '/' &&
14                 !memcmp(name, match->path, match_len)) ? match_len : 0;
15 }
17 static inline void set_pathname(int len, const char *name, struct pathname *match)
18 {
19         if (len < PATH_MAX) {
20                 match->len = len;
21                 memcpy(match->path, name, len);
22                 match->path[len] = 0;
23         }
24 }
26 int has_symlink_leading_path(int len, const char *name)
27 {
28         static struct pathname link, nonlink;
29         char path[PATH_MAX];
30         struct stat st;
31         char *sp;
32         int known_dir;
34         /*
35          * See if the last known symlink cache matches.
36          */
37         if (match_pathname(len, name, &link))
38                 return 1;
40         /*
41          * Get rid of the last known directory part
42          */
43         known_dir = match_pathname(len, name, &nonlink);
45         while ((sp = strchr(name + known_dir + 1, '/')) != NULL) {
46                 int thislen = sp - name ;
47                 memcpy(path, name, thislen);
48                 path[thislen] = 0;
50                 if (lstat(path, &st))
51                         return 0;
52                 if (S_ISDIR(st.st_mode)) {
53                         set_pathname(thislen, path, &nonlink);
54                         known_dir = thislen;
55                         continue;
56                 }
57                 if (S_ISLNK(st.st_mode)) {
58                         set_pathname(thislen, path, &link);
59                         return 1;
60                 }
61                 break;
62         }
63         return 0;
64 }