dir.con commit builtin-prune: memory diet. (16157b8)
   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
  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 */
  55static 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 */
  86int 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
 110void 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
 126static 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
 168void 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
 175int 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
 189void 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 */
 200static 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
 247int 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
 263struct 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
 284static 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 */
 304static 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                }
 371exit_early:
 372                closedir(fdir);
 373
 374                pop_exclude_per_directory(dir, exclude_stk);
 375        }
 376
 377        return contents;
 378}
 379
 380static 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
 389int 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
 422int
 423file_exists(const char *f)
 424{
 425  struct stat sb;
 426  return stat(f, &sb) == 0;
 427}