Merge branch 'rf/web'
[git] / dir.c
1 /*
2  * This handles recursive filename detection with exclude
3  * files, index knowledge etc..
4  *
5  * Copyright (C) Linus Torvalds, 2005-2006
6  *               Junio Hamano, 2005-2006
7  */
8 #include "cache.h"
9 #include "dir.h"
10
11 int common_prefix(const char **pathspec)
12 {
13         const char *path, *slash, *next;
14         int prefix;
15
16         if (!pathspec)
17                 return 0;
18
19         path = *pathspec;
20         slash = strrchr(path, '/');
21         if (!slash)
22                 return 0;
23
24         prefix = slash - path + 1;
25         while ((next = *++pathspec) != NULL) {
26                 int len = strlen(next);
27                 if (len >= prefix && !memcmp(path, next, len))
28                         continue;
29                 for (;;) {
30                         if (!len)
31                                 return 0;
32                         if (next[--len] != '/')
33                                 continue;
34                         if (memcmp(path, next, len+1))
35                                 continue;
36                         prefix = len + 1;
37                         break;
38                 }
39         }
40         return prefix;
41 }
42
43 /*
44  * Does 'match' matches the given name?
45  * A match is found if
46  *
47  * (1) the 'match' string is leading directory of 'name', or
48  * (2) the 'match' string is a wildcard and matches 'name', or
49  * (3) the 'match' string is exactly the same as 'name'.
50  *
51  * and the return value tells which case it was.
52  *
53  * It returns 0 when there is no match.
54  */
55 static int match_one(const char *match, const char *name, int namelen)
56 {
57         int matchlen;
58
59         /* If the match was just the prefix, we matched */
60         matchlen = strlen(match);
61         if (!matchlen)
62                 return MATCHED_RECURSIVELY;
63
64         /*
65          * If we don't match the matchstring exactly,
66          * we need to match by fnmatch
67          */
68         if (strncmp(match, name, matchlen))
69                 return !fnmatch(match, name, 0) ? MATCHED_FNMATCH : 0;
70
71         if (!name[matchlen])
72                 return MATCHED_EXACTLY;
73         if (match[matchlen-1] == '/' || name[matchlen] == '/')
74                 return MATCHED_RECURSIVELY;
75         return 0;
76 }
77
78 /*
79  * Given a name and a list of pathspecs, see if the name matches
80  * any of the pathspecs.  The caller is also interested in seeing
81  * all pathspec matches some names it calls this function with
82  * (otherwise the user could have mistyped the unmatched pathspec),
83  * and a mark is left in seen[] array for pathspec element that
84  * actually matched anything.
85  */
86 int match_pathspec(const char **pathspec, const char *name, int namelen, int prefix, char *seen)
87 {
88         int retval;
89         const char *match;
90
91         name += prefix;
92         namelen -= prefix;
93
94         for (retval = 0; (match = *pathspec++) != NULL; seen++) {
95                 int how;
96                 if (retval && *seen == MATCHED_EXACTLY)
97                         continue;
98                 match += prefix;
99                 how = match_one(match, name, namelen);
100                 if (how) {
101                         if (retval < how)
102                                 retval = how;
103                         if (*seen < how)
104                                 *seen = how;
105                 }
106         }
107         return retval;
108 }
109
110 void add_exclude(const char *string, const char *base,
111                  int baselen, struct exclude_list *which)
112 {
113         struct exclude *x = xmalloc(sizeof (*x));
114
115         x->pattern = string;
116         x->base = base;
117         x->baselen = baselen;
118         if (which->nr == which->alloc) {
119                 which->alloc = alloc_nr(which->alloc);
120                 which->excludes = xrealloc(which->excludes,
121                                            which->alloc * sizeof(x));
122         }
123         which->excludes[which->nr++] = x;
124 }
125
126 static int add_excludes_from_file_1(const char *fname,
127                                     const char *base,
128                                     int baselen,
129                                     struct exclude_list *which)
130 {
131         struct stat st;
132         int fd, i;
133         long size;
134         char *buf, *entry;
135
136         fd = open(fname, O_RDONLY);
137         if (fd < 0 || fstat(fd, &st) < 0)
138                 goto err;
139         size = st.st_size;
140         if (size == 0) {
141                 close(fd);
142                 return 0;
143         }
144         buf = xmalloc(size+1);
145         if (read(fd, buf, size) != size)
146                 goto err;
147         close(fd);
148
149         buf[size++] = '\n';
150         entry = buf;
151         for (i = 0; i < size; i++) {
152                 if (buf[i] == '\n') {
153                         if (entry != buf + i && entry[0] != '#') {
154                                 buf[i - (i && buf[i-1] == '\r')] = 0;
155                                 add_exclude(entry, base, baselen, which);
156                         }
157                         entry = buf + i + 1;
158                 }
159         }
160         return 0;
161
162  err:
163         if (0 <= fd)
164                 close(fd);
165         return -1;
166 }
167
168 void add_excludes_from_file(struct dir_struct *dir, const char *fname)
169 {
170         if (add_excludes_from_file_1(fname, "", 0,
171                                      &dir->exclude_list[EXC_FILE]) < 0)
172                 die("cannot use %s as an exclude file", fname);
173 }
174
175 int push_exclude_per_directory(struct dir_struct *dir, const char *base, int baselen)
176 {
177         char exclude_file[PATH_MAX];
178         struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
179         int current_nr = el->nr;
180
181         if (dir->exclude_per_dir) {
182                 memcpy(exclude_file, base, baselen);
183                 strcpy(exclude_file + baselen, dir->exclude_per_dir);
184                 add_excludes_from_file_1(exclude_file, base, baselen, el);
185         }
186         return current_nr;
187 }
188
189 void pop_exclude_per_directory(struct dir_struct *dir, int stk)
190 {
191         struct exclude_list *el = &dir->exclude_list[EXC_DIRS];
192
193         while (stk < el->nr)
194                 free(el->excludes[--el->nr]);
195 }
196
197 /* Scan the list and let the last match determines the fate.
198  * Return 1 for exclude, 0 for include and -1 for undecided.
199  */
200 static int excluded_1(const char *pathname,
201                       int pathlen,
202                       struct exclude_list *el)
203 {
204         int i;
205
206         if (el->nr) {
207                 for (i = el->nr - 1; 0 <= i; i--) {
208                         struct exclude *x = el->excludes[i];
209                         const char *exclude = x->pattern;
210                         int to_exclude = 1;
211
212                         if (*exclude == '!') {
213                                 to_exclude = 0;
214                                 exclude++;
215                         }
216
217                         if (!strchr(exclude, '/')) {
218                                 /* match basename */
219                                 const char *basename = strrchr(pathname, '/');
220                                 basename = (basename) ? basename+1 : pathname;
221                                 if (fnmatch(exclude, basename, 0) == 0)
222                                         return to_exclude;
223                         }
224                         else {
225                                 /* match with FNM_PATHNAME:
226                                  * exclude has base (baselen long) implicitly
227                                  * in front of it.
228                                  */
229                                 int baselen = x->baselen;
230                                 if (*exclude == '/')
231                                         exclude++;
232
233                                 if (pathlen < baselen ||
234                                     (baselen && pathname[baselen-1] != '/') ||
235                                     strncmp(pathname, x->base, baselen))
236                                     continue;
237
238                                 if (fnmatch(exclude, pathname+baselen,
239                                             FNM_PATHNAME) == 0)
240                                         return to_exclude;
241                         }
242                 }
243         }
244         return -1; /* undecided */
245 }
246
247 int excluded(struct dir_struct *dir, const char *pathname)
248 {
249         int pathlen = strlen(pathname);
250         int st;
251
252         for (st = EXC_CMDL; st <= EXC_FILE; st++) {
253                 switch (excluded_1(pathname, pathlen, &dir->exclude_list[st])) {
254                 case 0:
255                         return 0;
256                 case 1:
257                         return 1;
258                 }
259         }
260         return 0;
261 }
262
263 static void add_name(struct dir_struct *dir, const char *pathname, int len,
264                      int ignored_entry)
265 {
266         struct dir_entry *ent;
267
268         if (cache_name_pos(pathname, len) >= 0)
269                 return;
270
271         if (dir->nr == dir->alloc) {
272                 int alloc = alloc_nr(dir->alloc);
273                 dir->alloc = alloc;
274                 dir->entries = xrealloc(dir->entries, alloc*sizeof(ent));
275         }
276         ent = xmalloc(sizeof(*ent) + len + 1);
277         ent->ignored_entry = ignored_entry;
278         ent->len = len;
279         memcpy(ent->name, pathname, len);
280         ent->name[len] = 0;
281         dir->entries[dir->nr++] = ent;
282 }
283
284 static int dir_exists(const char *dirname, int len)
285 {
286         int pos = cache_name_pos(dirname, len);
287         if (pos >= 0)
288                 return 1;
289         pos = -pos-1;
290         if (pos >= active_nr) /* can't */
291                 return 0;
292         return !strncmp(active_cache[pos]->name, dirname, len);
293 }
294
295 /*
296  * Read a directory tree. We currently ignore anything but
297  * directories, regular files and symlinks. That's because git
298  * doesn't handle them at all yet. Maybe that will change some
299  * day.
300  *
301  * Also, we ignore the name ".git" (even if it is not a directory).
302  * That likely will not change.
303  */
304 static int read_directory_recursive(struct dir_struct *dir, const char *path, const char *base, int baselen, int check_only)
305 {
306         DIR *fdir = opendir(path);
307         int contents = 0;
308
309         if (fdir) {
310                 int exclude_stk;
311                 struct dirent *de;
312                 char fullname[PATH_MAX + 1];
313                 memcpy(fullname, base, baselen);
314
315                 exclude_stk = push_exclude_per_directory(dir, base, baselen);
316
317                 while ((de = readdir(fdir)) != NULL) {
318                         int len;
319                         int ignored_entry;
320
321                         if ((de->d_name[0] == '.') &&
322                             (de->d_name[1] == 0 ||
323                              !strcmp(de->d_name + 1, ".") ||
324                              !strcmp(de->d_name + 1, "git")))
325                                 continue;
326                         len = strlen(de->d_name);
327                         memcpy(fullname + baselen, de->d_name, len+1);
328                         ignored_entry = excluded(dir, fullname);
329
330                         if (!dir->show_both &&
331                             (ignored_entry != dir->show_ignored) &&
332                             (!dir->show_ignored || DTYPE(de) != DT_DIR))
333                                 continue;
334
335                         switch (DTYPE(de)) {
336                         struct stat st;
337                         default:
338                                 continue;
339                         case DT_UNKNOWN:
340                                 if (lstat(fullname, &st))
341                                         continue;
342                                 if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
343                                         break;
344                                 if (!S_ISDIR(st.st_mode))
345                                         continue;
346                                 /* fallthrough */
347                         case DT_DIR:
348                                 memcpy(fullname + baselen + len, "/", 2);
349                                 len++;
350                                 if (dir->show_other_directories &&
351                                     !dir_exists(fullname, baselen + len)) {
352                                         if (dir->hide_empty_directories &&
353                                             !read_directory_recursive(dir,
354                                                     fullname, fullname,
355                                                     baselen + len, 1))
356                                                 continue;
357                                         break;
358                                 }
359
360                                 contents += read_directory_recursive(dir,
361                                         fullname, fullname, baselen + len, 0);
362                                 continue;
363                         case DT_REG:
364                         case DT_LNK:
365                                 break;
366                         }
367                         contents++;
368                         if (check_only)
369                                 goto exit_early;
370                         else
371                                 add_name(dir, fullname, baselen + len,
372                                          ignored_entry);
373                 }
374 exit_early:
375                 closedir(fdir);
376
377                 pop_exclude_per_directory(dir, exclude_stk);
378         }
379
380         return contents;
381 }
382
383 static int cmp_name(const void *p1, const void *p2)
384 {
385         const struct dir_entry *e1 = *(const struct dir_entry **)p1;
386         const struct dir_entry *e2 = *(const struct dir_entry **)p2;
387
388         return cache_name_compare(e1->name, e1->len,
389                                   e2->name, e2->len);
390 }
391
392 int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen)
393 {
394         /*
395          * Make sure to do the per-directory exclude for all the
396          * directories leading up to our base.
397          */
398         if (baselen) {
399                 if (dir->exclude_per_dir) {
400                         char *p, *pp = xmalloc(baselen+1);
401                         memcpy(pp, base, baselen+1);
402                         p = pp;
403                         while (1) {
404                                 char save = *p;
405                                 *p = 0;
406                                 push_exclude_per_directory(dir, pp, p-pp);
407                                 *p++ = save;
408                                 if (!save)
409                                         break;
410                                 p = strchr(p, '/');
411                                 if (p)
412                                         p++;
413                                 else
414                                         p = pp + baselen;
415                         }
416                         free(pp);
417                 }
418         }
419
420         read_directory_recursive(dir, path, base, baselen, 0);
421         qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
422         return dir->nr;
423 }
424
425 int
426 file_exists(const char *f)
427 {
428   struct stat sb;
429   return stat(f, &sb) == 0;
430 }