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

#include "testdata.h"

#ifdef HAVE_LIBLIBRECAST
static size_t getbufsize(void)
{
	int buflen = sysconf(_SC_PAGESIZE);
	return (size_t)(16 * buflen);
}
#endif

int test_random_bytes(void *ptr, size_t sz)
{
	FILE *fr;
	size_t byt = 0, bi;
	fr = fopen("/dev/urandom", "r");
	if (!fr) return -1;
	while (byt < sz) {
		bi = fread(ptr, 1, sz - byt, fr);
		if (bi > 0) {
			byt += bi;
			ptr = (char *)ptr + bi;
		}
	}
	fclose(fr);
	return 0;
}

int test_sparsify_fd(int fd, size_t sz)
{
	FILE *f;
	int fdup;
	fdup = dup(fd);
	f = fdopen(fdup, "r+");
	if (!f) return -1;
	fseek(f, sz - 1, SEEK_SET);
	fputc(0, f);
	fclose(f);
	return 0;
}

static int test_randomize_file(int fd, size_t sz)
{
	FILE *f, *fr;
	char buf[BUFSIZ];
	size_t byt = 0, bi;
	int fdup;
	fdup = dup(fd);
	f = fdopen(fdup, "w+");
	if (!f) return -1;
	fr = fopen("/dev/urandom", "r");
	if (!fr) goto exit_0;
	while (byt < sz) {
		bi = fread(buf, 1, MIN(sizeof buf, sz - byt), fr);
		if (bi > 0) {
			byt += fwrite(buf, 1, bi, f);
		}
	}
	fclose(fr);
exit_0:
	fclose(f);
	return 0;
}

int test_data_file(char *filename, size_t sz, int flags)
{
	int fd;
	if (flags & TEST_TMP) {
		if ((fd = mkstemp(filename)) == -1) {
			perror("mkstemp");
			return -1;
		}
	}
	else if ((fd = open(filename, flags)) == -1) {
		perror("open");
		return -1;
	}
	if (flags & TEST_RND) {
		if (test_randomize_file(fd, sz) == -1) {
			close(fd);
			return -1;
		}
	}
	else if (sz > 0) {
		/* size requested, but no data => sparse */
		if (test_sparsify_fd(fd, sz) == -1) {
			close(fd);
			return -1;
		}
	}
	return fd;
}

int test_data_size(char *filename, size_t sz)
{
	struct stat sb;
	if (stat(filename, &sb) == -1) return -1;
	if ((sb.st_mode & S_IFMT) != S_IFREG) return -1;
	if ((size_t)sb.st_size != sz) return -1;
	return 0;
}
#ifdef HAVE_LIBLIBRECAST
int test_hash_file(const char *filename, unsigned char *hash, size_t hashlen, char *buf, size_t buflen)
{
	hash_state state;
	FILE *f;
	size_t byt;
	int free_buf = (buf == NULL); /* was buffer provided ? */
	if (!buf) {
		buflen = getbufsize();
		buf = malloc(buflen);
		if (!buf) return -1;
	}
	f = fopen(filename, "r");
	if (!f) goto exit_0;
	hash_init(&state, NULL, 0, hashlen);
	while ((byt = fread(buf, 1, buflen, f))) {
		hash_update(&state, (unsigned char *)buf, byt);
	}
	hash_final(&state, hash, hashlen);
	fclose(f);
exit_0:
	if (free_buf) free(buf);
	return 0;
}

int test_file_match(const char *file1, const char *file2)
{
	unsigned char hash[2][HASHSIZE];
	char buf[BUFSIZ];
	memset(hash, 0, sizeof hash);
	test_hash_file(file1, hash[0], HASHSIZE, buf, sizeof buf);
	test_hash_file(file2, hash[1], HASHSIZE, buf, sizeof buf);
	fprintf(stderr, "%s: ", file1);
	hash_hex_debug(stderr, hash[0], HASHSIZE);
	fprintf(stderr, "%s: ", file2);
	hash_hex_debug(stderr, hash[1], HASHSIZE);
	return memcmp(hash[0], hash[1], HASHSIZE);
}
#endif

int test_file_scratch(const char *filename, long offset, size_t len)
{
	char *buf;
	FILE *f, *fr;
	size_t byt = 0, bi;
	int buflen = MIN(getpagesize(), (int)len);
	if (!len) return 0;
	f = fopen(filename, "r+");
	if (!f) return -1;
	fr = fopen("/dev/urandom", "r");
	if (!fr) goto exit_0;
	if (offset < 0) {
		if (fseek(f, offset, SEEK_END) == -1) goto exit_1;
	}
	else if (offset > 0) {
		if (fseek(f, offset, SEEK_SET) == -1) goto exit_1;
	}
	buf = malloc(buflen);
	if (!buf) goto exit_1;
	while (byt < len) {
		bi = fread(buf, 1, MIN((size_t)buflen, len - byt), fr);
		if (bi > 0) {
			byt += fwrite(buf, 1, bi, f);
		}
	}
	free(buf);
exit_1:
	fclose(fr);
exit_0:
	fclose(f);
	return 0;
}
