/*
 * libopenraw - posix_io.c
 *
 * Copyright (C) 2005 Hubert Figuiere
 *
 * This library is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation, either version 3 of
 * the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library.  If not, see
 * <http://www.gnu.org/licenses/>.
 */


#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <errno.h>

#include "io_private.h"
#include "posix_io.h"


/** private data to be store in the _RawFile */
struct io_data_posix {
	/** POSIX fd returned by open() */
	int fd;
};

static IOFileRef raw_posix_open(const char *path, int mode);
static int raw_posix_close(IOFileRef f);
static int raw_posix_seek(IOFileRef f, off_t offset, int whence);
static int raw_posix_read(IOFileRef f, void *buf, size_t count);
static off_t raw_posix_filesize(IOFileRef f);
static void *raw_posix_mmap(IOFileRef f, size_t length, off_t offset);
static int raw_posix_munmap(IOFileRef f, void *addr, size_t length);

/** posix io methods instance. Constant. */
struct io_methods posix_io_methods = {
	&raw_posix_open,
	&raw_posix_close,
	&raw_posix_seek,
	&raw_posix_read,
	&raw_posix_filesize,
	&raw_posix_mmap,
	&raw_posix_munmap
};


/** posix implementation for open() */
static IOFileRef raw_posix_open(const char *path, int mode)
{
	struct io_data_posix *data =
		(struct io_data_posix *)malloc(sizeof(struct io_data_posix));
	IOFileRef f = (IOFileRef)malloc(sizeof(struct _IOFile));

	memset(f, 0, sizeof(struct _IOFile));
	memset(data, 0, sizeof(struct io_data_posix));

	f->methods = &posix_io_methods;
	f->_private = data;
	f->path = strdup(path);
	data->fd = open(path, mode);
	if (data->fd == -1) {
		free(data);
		free(f->path);
		free(f);
		f = NULL;
	}


	return f;
}



/** posix implementation for close() */
static int raw_posix_close(IOFileRef f)
{
	int retval = 0;
	struct io_data_posix *data = (struct io_data_posix*)f->_private;

	retval = close(data->fd);
	free(data);
	free(f->path);
	return retval;
}


/** posix implementation for seek() */
static int raw_posix_seek(IOFileRef f, off_t offset, int whence)
{
	int retval = 0;
	struct io_data_posix *data = (struct io_data_posix*)f->_private;

	retval = lseek(data->fd, offset, whence);
	if (retval == -1) {
		f->error = errno;
	}
	else {
		f->error = 0;
	}
	return retval;
}


/** posix implementation for read() */
static int raw_posix_read(IOFileRef f, void *buf, size_t count)
{
	int retval = 0;
	struct io_data_posix *data = (struct io_data_posix*)f->_private;

	retval = read(data->fd, buf, count);
	if (retval == -1) {
		f->error = errno;
	}
	else {
		f->error = 0;
	}
	return retval;
}


static off_t raw_posix_filesize(IOFileRef f)
{
	off_t size = -1;
	struct io_data_posix *data = (struct io_data_posix*)f->_private;
	struct stat sb;
    
	if(fstat(data->fd, &sb) >= 0) {
		size = sb.st_size;
	}

	return size;
}



static void *raw_posix_mmap(IOFileRef f, size_t length, off_t offset)
{
	struct io_data_posix *data = (struct io_data_posix*)f->_private;
	
	return mmap(NULL, length, PROT_READ, MAP_SHARED, data->fd, offset);
}


static int raw_posix_munmap(IOFileRef f, void *addr, size_t length)
{
	(void)f;
	return munmap(addr, length);
}
