master xplshn/aruu / shared / libutil / recurse.c
  1/* See LICENSE file for copyright and license details. */
  2#include <dirent.h>
  3#include <errno.h>
  4#include <fcntl.h>
  5#include <limits.h>
  6#include <stdio.h>
  7#include <stdlib.h>
  8#include <string.h>
  9#include <sys/stat.h>
 10#include <sys/types.h>
 11#include <unistd.h>
 12
 13#include "../fs.h"
 14#include "../util.h"
 15
 16int recurse_status = 0;
 17
 18void
 19recurse(int dirfd, const char *name, void *data, struct recursor *r)
 20{
 21  struct dirent *d;
 22  struct history *new, *h;
 23  struct stat st, dst;
 24  DIR        *dp;
 25  int         flags   = 0, fd;
 26  size_t      pathlen = r->pathlen;
 27
 28  if (dirfd == AT_FDCWD)
 29    pathlen = estrlcpy(r->path, name, sizeof(r->path));
 30
 31  if (r->follow == 'P' || (r->follow == 'H' && r->depth))
 32    flags |= AT_SYMLINK_NOFOLLOW;
 33
 34  if (fstatat(dirfd, name, &st, flags) < 0) {
 35    if (!(r->flags & SILENT)) {
 36      weprintf("stat %s:", r->path);
 37      recurse_status = 1;
 38    }
 39    return;
 40  }
 41  if (!S_ISDIR(st.st_mode)) {
 42    r->fn(dirfd, name, &st, data, r);
 43    return;
 44  }
 45
 46  new       = emalloc(sizeof(struct history));
 47  new->prev = r->hist;
 48  r->hist   = new;
 49  new->dev  = st.st_dev;
 50  new->ino  = st.st_ino;
 51
 52  for (h = new->prev; h; h = h->prev)
 53    if (h->ino == st.st_ino && h->dev == st.st_dev)
 54      return;
 55
 56  if (!r->depth && (r->flags & DIRFIRST))
 57    r->fn(dirfd, name, &st, data, r);
 58
 59  if (!r->maxdepth || r->depth + 1 < r->maxdepth) {
 60    fd = openat(dirfd, name, O_RDONLY | O_CLOEXEC | O_DIRECTORY);
 61    if (fd < 0) {
 62      weprintf("open %s:", r->path);
 63      recurse_status = 1;
 64    }
 65    if (!(dp = fdopendir(fd))) {
 66      if (!(r->flags & SILENT)) {
 67        weprintf("fdopendir:");
 68        recurse_status = 1;
 69      }
 70      return;
 71    }
 72    if (r->path[pathlen - 1] != '/')
 73      r->path[pathlen++] = '/';
 74    if (r->follow == 'H')
 75      flags |= AT_SYMLINK_NOFOLLOW;
 76    while ((d = readdir(dp))) {
 77      if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, ".."))
 78        continue;
 79      r->pathlen = pathlen + estrlcpy(r->path + pathlen, d->d_name, sizeof(r->path) - pathlen);
 80      if (fstatat(fd, d->d_name, &dst, flags) < 0) {
 81        if (!(r->flags & SILENT)) {
 82          weprintf("stat %s:", r->path);
 83          recurse_status = 1;
 84        }
 85      } else if ((r->flags & SAMEDEV) && dst.st_dev != st.st_dev) {
 86        continue;
 87      } else {
 88        r->depth++;
 89        r->fn(fd, d->d_name, &dst, data, r);
 90        r->depth--;
 91      }
 92    }
 93    r->path[pathlen - 1] = '\0';
 94    r->pathlen           = pathlen - 1;
 95    closedir(dp);
 96  }
 97
 98  if (!r->depth) {
 99    if (!(r->flags & DIRFIRST))
100      r->fn(dirfd, name, &st, data, r);
101
102    while (r->hist) {
103      h       = r->hist;
104      r->hist = r->hist->prev;
105      free(h);
106    }
107  }
108}