/* SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only */
/* Copyright (c) 2023-2024 Brett Sheffield <bacs@librecast.net> */

#include "../src/config.h"
#define _XOPEN_SOURCE 700 /* for nftw() */
#include <ftw.h>
#include "test.h"
#include "testdata.h"
#include <assert.h>
#include <sys/types.h>
#include <unistd.h>

static char *rsrc, *rdst, *owd;

/* check mode, owner, group, times */
int statcmp_sb(const struct stat *ssb, const struct stat *dsb, const char *dst)
{
	if ((ssb->st_mode & S_IFMT) == S_IFLNK) return 0; /* don't bother with symlinks */
	test_assert(ssb->st_mode == dsb->st_mode, "st_mode: '%s' %o %o", dst, ssb->st_mode, dsb->st_mode);
	test_assert(ssb->st_uid == dsb->st_uid, "st_uid:  '%s'", dst);
	test_assert(ssb->st_gid == dsb->st_gid, "st_gid:  '%s'", dst);
	if ((ssb->st_mode & S_IFMT) != S_IFDIR) {
		/* check size, but not for directories */
		test_assert(ssb->st_size == dsb->st_size, "st_size: '%s'", dst);
	}
#ifndef HAVE_UTIMENSAT
	/* without utimensat(), we only have microsecond precision */
	ssb->st_mtim.tv_nsec /= 1000; ssb->st_mtim.tv_nsec *= 1000;
	ssb->st_atim.tv_nsec /= 1000; ssb->st_atim.tv_nsec *= 1000;
#endif
	/* No point testing atime unless the filesystem is mounted noatime */
	/* test_assert(ssb.st_atim.tv_nsec == dsb.st_atim.tv_nsec, "st_atim: '%s'", dst); */
	test_assert(ssb->st_mtim.tv_nsec == dsb->st_mtim.tv_nsec, "st_mtim: '%s'", dst);
	return 0;
}

/* check mode, owner, group, times */
int statcmp(const char *src, const char *dst)
{
	struct stat ssb, dsb;
	int rc;
	rc = stat(src, &ssb);
	if (!test_assert(rc == 0, "stat '%s'", src)) return -1;
	rc = stat(dst, &dsb);
	if (!test_assert(rc == 0, "stat '%s'", dst)) return -1;
	return statcmp_sb(&ssb, &dsb, dst);
}

static int verify_dst(const char *fpath, const struct stat *sb, int typeflag, struct FTW *ftwbuf)
{
	(void) typeflag, (void) ftwbuf;
	struct stat dsb;
	int rc;
	if (chdir(rdst) == -1) return -1;
	rc = stat(fpath, &dsb);
	if (!rc) rc = statcmp_sb(sb, &dsb, fpath);
	if (chdir(rsrc) == -1) return -1;
	return rc;
}

#if HAVE_GETCWD
#if HAVE_REALPATH
void test_verify_dirs(const char *src, const char *dst)
{
	int n = 0, rc;

	statcmp(src, dst);
	owd = getcwd(NULL, 0);
	assert(owd);
	rsrc = realpath(src, NULL);
	rdst = realpath(dst, NULL);

	test_log("src: %s\n", src);
	test_log("dst: %s\n", dst);
	test_log("rsrc: %s\n", rsrc);
	test_log("rdst: %s\n", rdst);

	if (chdir(rsrc) == -1) goto err_free;
	rc = nftw(".", &verify_dst, 32, 0);
	if (!test_assert(rc == 0, "nftw() returned %i", rc)) goto err_free;
	test_log("%i entries verified\n", n);
	test_assert(test_status == TEST_OK, "source and destination trees match");
	if (chdir(owd) == -1) perror("chdir");

err_free:
	free(rsrc); free(rdst);
	free(owd);
}
#endif /* HAVE_REALPATH */
#endif /* HAVE_GETCWD */
