#include <sys/types.h>
#include <sys/dirent.h>

#include <dirent.h>
#include <err.h>
#include <fcntl.h>
#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int
main(int argc, char **argv)
{
	struct dirent *entry;
	off_t base, before, start;
	char *buffer;
	char *end;
	size_t buffer_size;
	ssize_t bytes;
	int calls, fd;

	if (argc < 2 || argc > 5)
		errx(2, "usage: %s directory [offset [buffer-size [calls]]]",
		    argv[0]);
	start = argc >= 3 ? strtoll(argv[2], NULL, 0) : 0;
	buffer_size = argc >= 4 ? strtoul(argv[3], NULL, 0) : 128;
	calls = argc >= 5 ? strtol(argv[4], NULL, 0) : 32;
	if (buffer_size < 32 || calls < 1)
		errx(2, "invalid buffer size or call count");

	fd = open(argv[1], O_RDONLY | O_DIRECTORY);
	if (fd < 0)
		err(1, "open %s", argv[1]);
	if (lseek(fd, start, SEEK_SET) < 0)
		err(1, "lseek %jd", (intmax_t)start);
	buffer = malloc(buffer_size);
	if (buffer == NULL)
		err(1, "malloc");

	for (int call = 0; call < calls; call++) {
		before = lseek(fd, 0, SEEK_CUR);
		if (before < 0)
			err(1, "lseek current");
		base = -1;
		bytes = getdirentries(fd, buffer, buffer_size, &base);
		if (bytes < 0)
			err(1, "getdirentries");
		printf("call=%d before=%jd after=%jd base=%jd bytes=%zd\n",
		    call, (intmax_t)before,
		    (intmax_t)lseek(fd, 0, SEEK_CUR), (intmax_t)base, bytes);
		if (bytes == 0)
			break;
		end = buffer + bytes;
		for (entry = (struct dirent *)buffer;
		    (char *)entry < end;
		    entry = (struct dirent *)((char *)entry + entry->d_reclen)) {
			if (entry->d_reclen == 0 ||
			    (char *)entry + entry->d_reclen > end)
				errx(1, "invalid dirent record");
			printf("  off=%jd ino=%ju reclen=%u type=%u name=%.*s\n",
			    (intmax_t)entry->d_off, (uintmax_t)entry->d_fileno,
			    entry->d_reclen, entry->d_type, entry->d_namlen,
			    entry->d_name);
		}
	}

	free(buffer);
	close(fd);
	return (0);
}
