#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/clonefile.h>

#define MAX_SIZE (1024 * 1024 * 100)
char msg[MAX_SIZE];

int main(int argc, char ** argv)
{
	// sparse copy test
	// block size i 4 KB
	// c d67000  p 1367000  h 1367000  d 600000
	// total bytes copied d67000 / 1a46640
	int src = open("cc1", O_RDONLY);
	int dst = open("cc1-sparse", O_CREAT | O_TRUNC | O_RDWR, 0700);
	printf("src %i  dst %i\n", src, dst);
	//- printf("SET %i  CUR %i  END %i  HOLE %i  DATA %i\n", SEEK_SET, SEEK_CUR, SEEK_END, SEEK_HOLE, SEEK_DATA);

	off_t a = 0;
	off_t b = 0;
	off_t d = 0;
	off_t e = 0;
	off_t h = 0;
	off_t p = 0;
	off_t s = -1;
	off_t t = 0;
	ssize_t c = 0;
	ssize_t i = 0;
	ssize_t r = 0;
	ssize_t w = 0;
	int ea = 0;
	int eb = 0;
	int ed = 0;
	int ee = 0;
	int eh = 0;
	int ep = 0;

	do
	{
		if (++i >= 10)
		{
			printf("LOOP %zi\n", i);
			break;
		}

		errno = 0;
		d = lseek(src, d, SEEK_DATA);
		ed = errno;
		h = lseek(src, d, SEEK_HOLE);
		eh = errno;
		a = lseek(src, d, SEEK_SET);
		ea = errno;
		b = lseek(dst, d, SEEK_SET);
		eb = errno;
		c = h - d;

		if ((a == -1) || (b == -1) || (d == -1) || (h == -1))
		{
			int handled = 0;

			if ((d == -1) && (ed == ENXIO))
			{
				p = lseek(src, 0, SEEK_END);
				ep = errno;

				if (p == -1)
				{
					printf(
						"lseek(SEEK_END) failed  p %lli %2i %s\n",
						p, ep, strerror(ep)
					);
				}
				else
				{
					e = ftruncate(dst, p);
					ee = errno;

					if (e == -1)
					{
						printf(
							"ftruncate(%lli) failed  %lli %2i %s\n",
							p, e, ee, strerror(ee)
						);
					}
					else
					{
						handled = 1;
					}
				}
			}

			if (!handled)
			{
				printf(
					"lseek failed"
					"  ENXIO %i  EBADF %i  EINVAL %i  EOVERFLOW %i  ESPIPE %i\n"
					"  p %lli\n"
					"  d %lli %2i %s\n"
					"  h %lli %2i %s\n"
					"  a %lli %2i %s\n"
					"  b %lli %2i %s\n",
					ENXIO, EBADF, EINVAL, EOVERFLOW, ESPIPE,
					p,
					d, ed, strerror(ed),
					h, eh, strerror(eh),
					a, ea, strerror(ea),
					b, eb, strerror(eb)
				);
			}

			break;
		}

		if (c < 0)
		{
			printf("negative size c %zi = h %lli - d %lli\n", c, h, d);
			continue;
		}

		if (s == d)
		{
			printf("EOF %llu %llu\n", s, d);
			break;
		}

		s = d;

		if (c > sizeof(msg))
		{
			printf("msg too small %zu %zi\n", sizeof(msg), c);
			break;
		}

		r = read(src, msg, c);
		w = write(dst, msg, c);

		if ((r != c) || (w != c))
		{
			printf("expected %zi  got %zi %zi\n", c, r, w);
			break;
		}

		p = lseek(src, 0, SEEK_CUR);
		t += c;
		printf("c %zx  p %llx  h %llx  d %llx\n", c, p, h, d);
		d = p;
	} while (1);

	close(src);
	close(dst);

	printf("total bytes copied %llx / %llx\n", t, p);

	return 0;
}
