/* ARISA - sendfile(2) linkage and drop in replacements for sendfile(2)
 * Copyright (C) 2004 Carl Ritson
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 */

#define _GNU_SOURCE
#define _REENTRANT
#define _THREAD_SAFE

#include <stdlib.h>
#include <stdio.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>

#include <unistd.h>
#include <fcntl.h>

#include "../config.h"

#include "globals.h"
#include "sendfile.h"

/*** Linux ***/
#if defined(HAVE_LINUX) && defined(HAVE_SENDFILE64) \
			&& defined(HAVE_SYS_SENDFILE_H)
#define SENDFILE 1
#include <sys/sendfile.h>
	
inline ssize_t l_sendfile(int sok, int fd, off_t *off_ptr, size_t count) {
	return sendfile(sok,fd,off_ptr,count);
}
const char *sendfile_method = "Linux sendfile(2)";

#endif /* defined(HAVE_LINUX) && defined(HAVE_SENDFILE64) && ... */
	
/*** FreeBSD ***/
#if defined(HAVE_FREEBSD) && defined(HAVE_SYS_UIO_H)
#define SENDFILE 1

#include <sys/uio.h>

inline ssize_t l_sendfile(int sok, int fd, off_t *off_ptr, size_t count) {
	off_t done = 0;
	ssize_t ret = -1;
	if(sendfile(fd,sok,*off_ptr,count,NULL,&done,0) == 0) {
		ret = (ssize_t) done;
		(*off_ptr) += done;
	}
	return ret;
}

const char *sendfile_method = "FreeBSD sendfile(2)";

#endif /* defined(HAVE_FREEBSD) && defined(HAVE_SYS_UIO_H) */

/*** MMAP Based Substitute ***/
#if 0
#if !defined(SENDFILE) && defined(HAVE_MMAP64)
#define SENDFILE 1

#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif /* HAVE_SYS_MMAN_H */

inline ssize_t l_sendfile(int sok, int fd, off_t *off_ptr, size_t count) {
	ssize_t ret = 0;
	size_t size;
	off_t off = *off_ptr;
	char *data;
	int shift;
	
	shift = off % (off_t) page_size;
	off -= (off_t) shift;
	size = count + shift;
	
	data = mmap(NULL,size,PROT_READ,MAP_PRIVATE,fd,off);
	if(data == NULL)
		return -1;
	ret = send(sok,data+shift,count,0);
	if(ret > 0)
		*off_ptr += (off_t) ret;
	munmap(data,size);

	return ret;
}
const char *sendfile_method = "mmap(2)";
#endif /* !defined(SENDFILE) && defined(HAVE_MMAP64) */
#endif

/*** READ + SEND Based Substitute ***/
#if !defined(SENDFILE)
#define SENDFILE 1

inline ssize_t l_sendfile(int sok, int fd, off_t *off_ptr, size_t count) {
	char buffer[4096];
	ssize_t tmp, sent = 0, ret = 0;
	off_t ipos = lseek(fd,0,SEEK_CUR); // get present position
	if(ipos == (off_t)-1)
		return -1;
	
	if(lseek(fd,*off_ptr,SEEK_SET) == (off_t)-1)
		return -1;
	
	while(sent < count) {
		ret = read(fd,buffer,
			count - sent < sizeof(buffer) ? 
			count - sent : sizeof(buffer));
		if(ret <= 0)
			break;
		tmp = ret; // store the number of bytes read for later
		
		ret = send(sok,buffer,ret,0);
		if(ret <= 0) 
			break;
		sent += ret;

		/* If not all bytes were sent backout, as it suggests that the
		 * next send call will fail (with EAGAIN).
		 * This also saves us messing about with file offsets. 
		 */
		if(ret < tmp) 
			break;
	}
	
	(*off_ptr) += (off_t) sent;
	if(ret != -1)
		ret = sent;
	
	lseek(fd,ipos,SEEK_SET); // restore position, if this fails *tough*
	return ret;
}
const char *sendfile_method = "read(2) + send(2)";

#endif /* ! SENDFILE */
