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