Allow passing of an alternative CVSROOT via -d.
[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_in_full(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 struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
264 {
265         struct dir_entry *ent;
266
267         if (cache_name_pos(pathname, len) >= 0)
268                 return NULL;
269
270         if (dir->nr == dir->alloc) {
271                 int alloc = alloc_nr(dir->alloc);
272                 dir->alloc = alloc;
273                 dir->entries = xrealloc(dir->entries, alloc*sizeof(ent));
274         }
275         ent = xmalloc(sizeof(*ent) + len + 1);
276         ent->ignored = ent->ignored_dir = 0;
277         ent->len = len;
278         memcpy(ent->name, pathname, len);
279         ent->name[len] = 0;
280         dir->entries[dir->nr++] = ent;
281         return 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
320                         if ((de->d_name[0] == '.') &&
321                             (de->d_name[1] == 0 ||
322                              !strcmp(de->d_name + 1, ".") ||
323                              !strcmp(de->d_name + 1, "git")))
324                                 continue;
325                         len = strlen(de->d_name);
326                         memcpy(fullname + baselen, de->d_name, len+1);
327                         if (excluded(dir, fullname) != dir->show_ignored) {
328                                 if (!dir->show_ignored || DTYPE(de) != DT_DIR) {
329                                         continue;
330                                 }
331                         }
332
333                         switch (DTYPE(de)) {
334                         struct stat st;
335                         default:
336                                 continue;
337                         case DT_UNKNOWN:
338                                 if (lstat(fullname, &st))
339                                         continue;
340                                 if (S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))
341                                         break;
342                                 if (!S_ISDIR(st.st_mode))
343                                         continue;
344                                 /* fallthrough */
345                         case DT_DIR:
346                                 memcpy(fullname + baselen + len, "/", 2);
347                                 len++;
348                                 if (dir->show_other_directories &&
349                                     !dir_exists(fullname, baselen + len)) {
350                                         if (dir->hide_empty_directories &&
351                                             !read_directory_recursive(dir,
352                                                     fullname, fullname,
353                                                     baselen + len, 1))
354                                                 continue;
355                                         break;
356                                 }
357
358                                 contents += read_directory_recursive(dir,
359                                         fullname, fullname, baselen + len, 0);
360                                 continue;
361                         case DT_REG:
362                         case DT_LNK:
363                                 break;
364                         }
365                         contents++;
366                         if (check_only)
367                                 goto exit_early;
368                         else
369                                 dir_add_name(dir, fullname, baselen + len);
370                 }
371 exit_early:
372                 closedir(fdir);
373
374                 pop_exclude_per_directory(dir, exclude_stk);
375         }
376
377         return contents;
378 }
379
380 static int cmp_name(const void *p1, const void *p2)
381 {
382         const struct dir_entry *e1 = *(const struct dir_entry **)p1;
383         const struct dir_entry *e2 = *(const struct dir_entry **)p2;
384
385         return cache_name_compare(e1->name, e1->len,
386                                   e2->name, e2->len);
387 }
388
389 int read_directory(struct dir_struct *dir, const char *path, const char *base, int baselen)
390 {
391         /*
392          * Make sure to do the per-directory exclude for all the
393          * directories leading up to our base.
394          */
395         if (baselen) {
396                 if (dir->exclude_per_dir) {
397                         char *p, *pp = xmalloc(baselen+1);
398                         memcpy(pp, base, baselen+1);
399                         p = pp;
400                         while (1) {
401                                 char save = *p;
402                                 *p = 0;
403                                 push_exclude_per_directory(dir, pp, p-pp);
404                                 *p++ = save;
405                                 if (!save)
406                                         break;
407                                 p = strchr(p, '/');
408                                 if (p)
409                                         p++;
410                                 else
411                                         p = pp + baselen;
412                         }
413                         free(pp);
414                 }
415         }
416
417         read_directory_recursive(dir, path, base, baselen, 0);
418         qsort(dir->entries, dir->nr, sizeof(struct dir_entry *), cmp_name);
419         return dir->nr;
420 }
421
422 int
423 file_exists(const char *f)
424 {
425   struct stat sb;
426   return stat(f, &sb) == 0;
427 }