/*****************************************************************************

	unsort - reorder files semi-randomly
	Copyright (C) 2007, 2008  Wessel Dankers <wsl@fruit.je>

	This program is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.

*****************************************************************************/

#include <stdbool.h>
#include <stdint.h>
#include <inttypes.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <limits.h>
#include <fcntl.h>
#include <sys/uio.h>

#include "error.h"
#include "filebuf.h"
#include "iovec.h"

struct iovec_const {
	const void *iov_base;
	size_t iov_len;
};

#ifndef IOV_MAX
#define IOV_MAX _XOPEN_IOV_MAX
#endif

static uint32_t writev_max = IOV_MAX;

void iovec_write(int fd, struct iovec *iov, uint32_t count) {
	ssize_t r;
	size_t len;

	if(!count)
		return;

	while(count) {
		r = writev(fd, iov, (int)(count > writev_max ? writev_max : count));
		if(r < 0)
			exit_perror(ERROR_SYSTEM, "Can't write to fd %d", fd);
		if(!r)
			exit_error(ERROR_SYSTEM, "Can't write to fd %d", fd);
		while(r) {
			len = iov->iov_len;
			if((size_t)r < len) {
				iov->iov_base = (char *)iov->iov_base + r;
				iov->iov_len -= (size_t)r;
				r = 0;
			} else {
				r -= (ssize_t)len;
				iov++;
				count--;
			}
		}
	}
}

bool iovec_parse(filebuf_t *fb, int sep, struct iovec *iov, uint32_t *count) {
	const char *buf, *end, *line;
	uint32_t u = 0;
	struct iovec_const *ioc;

	buf = fb->buf;
	end = buf + fb->len;

	while(buf < end) {
		line = (const char *)memchr(buf, sep, (size_t)(end - buf));
		if(!line)
			break;
		line++;
		/* buf..line is nu een regel */
		if(iov) {
			ioc = (struct iovec_const *)iov + u;
			ioc->iov_base = buf;
			ioc->iov_len = (size_t)(line - buf);
		}
		u++;
		buf = line;
	}

	if(count)
		*count = u;

	return buf < end;
}

void iovec_shuffle(struct iovec *iov, uint32_t *tlb, uint32_t count) {
	uint32_t start;
	uint32_t m, n;
	struct iovec v, w;

	for(start = 0; start < count; start++) {
		m = tlb[start];
		if(m == start)
			continue;
		v = iov[start];

		for(;;) {
			w = iov[m];
			iov[m] = v;

			n = tlb[m];
			tlb[m] = m;

			if(n == start) {
				iov[start] = w;
				break;
			}

			v = iov[n];
			iov[n] = w;

			m = tlb[n];
			tlb[n] = n;

			if(m == start) {
				iov[start] = v;
				break;
			}
		}
	}
}
