/*
 * Copyright (c) 1996 Carnegie Mellon University.
 * All rights reserved.
 *
 * Permission to use, copy, modify and distribute this software and
 * its documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 *
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 *
 * Carnegie Mellon requests users of this software to return to
 *
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 *
 * any improvements or extensions that they make and grant Carnegie Mellon the
 * rights to redistribute these changes.
 *
 * Network-Attached Secure Disks (NASD) Project, Parallel Data Laboratory
 * Carnegie Mellon University, http://www.cs.cmu.edu/Web/Groups/NASD
 *
 */

/***********************************************************************
 *
 * sio_stdio.c 
 *
 * stdio-like sio interface
 *
 ***********************************************************************/
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
#include <malloc.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sio_fs.h>
#include "sio_time.h"
#include "sio_stdio.h"

#define SIO_STDIO_TABLE_SIZE 32

#define SIO_STDIO_DEBUG 0

typedef struct sio_stdio_of_s {
     int fd;
     int offset;
} sio_stdio_of_t;

sio_stdio_of_t sio_stdio_of[SIO_STDIO_TABLE_SIZE];

#ifdef _SIO_DEMO 
extern int _Stdio_meter;
#endif

/* proto */
int
sio_stdio_insert(int fd);
int
sio_stdio_remove(int fd);
int
sio_stdio_set_offset(int fd, int offset);
int
sio_stdio_get_offset(int fd);
int
sio_stdio_isopen(int fd);

int 
sio_stdio_open(char *fname, int mode)
{
      int rv;
      int fd;
      int sio_mode = 0;

      if (mode & O_WRONLY )
	sio_mode |= SIO_MODE_WRITE;
      if (mode & O_RDONLY )
	sio_mode |= SIO_MODE_READ;
      if (mode & O_CREAT )
	sio_mode |= SIO_MODE_CREATE;
      if (mode & O_RDWR )
	sio_mode |= SIO_MODE_READ | SIO_MODE_WRITE;

      if (sio_mode==0)
	sio_mode = SIO_MODE_READ;

      rv = sio_open(&fd, fname,  sio_mode, NULL, 0);

      if (!rv) {
#if SIO_STDIO_DEBUG > 1
	_sio_warning("opened file %s, fd=%d\n", fname, fd);
#endif
	sio_stdio_insert(fd);
	return fd;
      } else { 
#if SIO_STDIO_DEBUG > 1
	_sio_warning("open file %s failed \n", fname);
#endif
	return ( (rv<0)?rv:(-1*rv));      
      }

}

int 
sio_stdio_read(int fd, char *buf, int size)
{
	sio_file_io_list_t fl[1];
        sio_mem_io_list_t ml[1];
        sio_transfer_len_t xfer=0;
        sio_return_t rv;
	int new_offset;
	double bw;
	DECLARE_TIME

#ifdef _SIO_DEMO 
	INIT_TIME
	BEGIN_TIME
#endif

	if ( !sio_stdio_isopen(fd) ) {
#if SIO_STDIO_DEBUG > 1
	_sio_warning("read to fd=%d failed (file not open)\n", fd);
#endif
	 return ENOENT;
	}

#if SIO_STDIO_DEBUG > 0
	_sio_warning("received read of %d bytes\n", size);
#endif
        fl[0].offset = sio_stdio_get_offset(fd);
        fl[0].size = size;
        fl[0].stride = 0;
        fl[0].element_cnt = 1;

	ml[0].addr = buf;
        ml[0].size = size;
        ml[0].stride = 0;
        ml[0].element_cnt = 1;

#if SIO_STDIO_DEBUG > 0
	_sio_warning("read to fd=%d, size=%d, offset=%d received\n", fd, size, fl[0].offset);
#endif
	rv = sio_sg_read(fd, fl, 1, ml, 1, &xfer);
	if (xfer>=0) {
	  new_offset = xfer + sio_stdio_get_offset(fd);
	  sio_stdio_set_offset( fd, new_offset );
	}

#ifdef _SIO_DEMO
	END_TIME
	bw = xfer*1000.0/tt;
	UpdateMeter(_Stdio_meter, bw, 0);
#endif

	if (rv) {
#if SIO_STDIO_DEBUG > 0
	_sio_warning("read to fd=%d, size=%d failed (new_offset=%d) rv=%d\n", fd, size, new_offset, rv);
#endif
	  return ( (rv<0)?rv:(-1*rv));      
	} else {
#if SIO_STDIO_DEBUG > 0
	_sio_warning("read to fd=%d, size=%d success xfer=%d, (new_offset=%d)\n", fd, size, xfer, new_offset);
#endif
	  return xfer;
	}

}

int 
sio_stdio_write(int fd, char *buf, int size)
{  
	sio_file_io_list_t fl[1];
        sio_mem_io_list_t ml[1];
        sio_transfer_len_t xfer;
        sio_return_t rv;
	int new_offset;

       if ( !sio_stdio_isopen(fd) ) {
#if SIO_STDIO_DEBUG > 1
	_sio_warning("write to fd=%d failed (file not open)\n", fd);
#endif
	 return ENOENT;
       }

#if SIO_STDIO_DEBUG > 0
	_sio_warning("received write of %d bytes\n", size);
#endif
        fl[0].offset = sio_stdio_get_offset(fd);
        fl[0].size = size;
        fl[0].stride = 0;
        fl[0].element_cnt = 1;

	ml[0].addr = buf;
        ml[0].size = size;
        ml[0].stride = 0;
        ml[0].element_cnt = 1;

	rv = sio_sg_write(fd, fl, 1, ml, 1, &xfer);
	if (xfer>=0) {
	  new_offset = xfer + sio_stdio_get_offset(fd);
	  sio_stdio_set_offset( fd, new_offset ); 
	}

	if (rv) {
#if SIO_STDIO_DEBUG > 0
	_sio_warning("write to fd=%d, size=%d failed (new_offset=%d) rv=%d\n", fd, size, new_offset, rv);
#endif
	  return ( (rv<0)?rv:(-1*rv));      
	} else {
#if SIO_STDIO_DEBUG > 0
	_sio_warning("size to fd=%d, size=%d success (new_offset=%d)\n", fd, size, new_offset, rv);
#endif
	  return xfer;
	}

}

int 
sio_stdio_seek(int fd, int offset, int whence)
{
       int file_offset;

       if ( !sio_stdio_isopen(fd) )
	 return ENOENT;

       file_offset = sio_stdio_get_offset(fd);
       if (whence==SEEK_SET)
	 file_offset = offset;
       if (whence==SEEK_CUR)
	 file_offset = offset;
       if (whence==SEEK_END)
	 file_offset += offset;
       
       sio_stdio_set_offset(fd, file_offset);
       return file_offset;
}

int 
sio_stdio_close(int fd)
{
       sio_return_t rv;

       if ( !sio_stdio_isopen(fd) )
	 return ENOENT;

       sio_stdio_remove(fd);
       rv = sio_close(fd);
       return rv;

}

void
sio_stdio_init()
{
       int i;

       for (i=0; (i< SIO_STDIO_TABLE_SIZE); i++) {
	 sio_stdio_of[i].offset = 0;
	 sio_stdio_of[i].fd = -1;
       }

}

int
sio_stdio_insert(int fd)
{
       int i;
       int found = FALSE;

       for (i=0; (i< SIO_STDIO_TABLE_SIZE) && (!found); i++) {
	 if (sio_stdio_of[i].fd == -1) {
	   found = TRUE; /* empty slot */
	 }
       }
       if (found) {
	 sio_stdio_of[i].offset = 0;
	 sio_stdio_of[i].fd = fd;
	 return 0;
       } else
	 return -1;

}

int
sio_stdio_isopen(int fd)
{
       int i;
       int found = FALSE;

       for (i=0; (i< SIO_STDIO_TABLE_SIZE) && (!found); i++) {
	 if (sio_stdio_of[i].fd == fd) {
	   found = TRUE;
	 }
       }
       return found;
}


int
sio_stdio_remove(int fd)
{
       int i;
       int found = FALSE;

       
       for (i=0; (i< SIO_STDIO_TABLE_SIZE) && (!found); i++) {
	 if (sio_stdio_of[i].fd == fd) {
	   found = TRUE;
	 }
       }
       if (found) {
	 sio_stdio_of[i].offset = 0;
	 sio_stdio_of[i].fd = -1;
	 return 0;
       } else
	 return -1;

}



int
sio_stdio_set_offset(int fd, int offset)
{
       int i;
       int found = FALSE;

       for (i=0; (i< SIO_STDIO_TABLE_SIZE) && (!found); i++) {
	 if (sio_stdio_of[i].fd == fd) {
	   found = TRUE;
	 }
       }
       if (found) {
	 sio_stdio_of[i].offset = offset;
	 return 0;
       } else
	 return -1;
       
    
}

int
sio_stdio_get_offset(int fd)
{

       int i;
       int found = FALSE;

       for (i=0; (i< SIO_STDIO_TABLE_SIZE) && (!found); i++) {
	 if (sio_stdio_of[i].fd == fd) {
	   found = TRUE;
	 }
       }
       if (found)
	 return sio_stdio_of[i].offset;
       else
	 return -1;

}



