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}