/* file.c -- some common file operations */

#include <fcntl.h>
#include <errno.h>
#include <memory.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>

#include "toba.h"
#include "runtime.h"

#ifndef O_BINARY
#define O_BINARY 0
#endif

/* common code for opening a file given its FileDescriptor */
Void fd_open(struct in_java_io_FileDescriptor *fd, Object unameo, int oflags) 
{
    struct in_java_lang_String *uname = 
        (struct in_java_lang_String *)unameo;
    char *name;
    int unixfd;

    if(!fd || !uname)
        throwNullPointerException("open");

    name = cstring(uname);
   
#ifdef NT_OPEN_HACK
    /*
     * work-around for bug in cygwin/NT's open() call:
     * a file opened with O_CREAT is always truncated.
     */

    /* try without O_CREAT (ie. if file already exists) */
    unixfd = open(name, (oflags & ~O_CREAT) | O_BINARY, 0666);

    /* retry with O_CREAT if that failed and we wanted O_CREAT */
    if(unixfd == -1 && errno == ENOENT && (oflags & O_CREAT)) 
        unixfd = open(name, oflags | O_BINARY, 0666);
#else
    unixfd = open(name, oflags | O_BINARY, 0666);
#endif
    if(unixfd == -1) {
        switch(errno) {
        case ENOENT:
            throwFileNotFoundException(name, errno);
            break;
        default:
            throwIOException(name, errno);
            break;
        }
    }

    fd->fd = java_fd(unixfd);
}

/* common code for reading from a FileDescriptor */
Int fd_read_char(struct in_java_io_FileDescriptor *fd) 
{
    int len;
    char c;

    if(!fd)
        throwNullPointerException("read");

    /* don't throw interrupted exception, simply retry */
    do {
        len = read(unix_fd(fd->fd), &c, 1);
        if(len == -1 && errno != EINTR)
            throwIOException("read", errno);
    } while(len == -1);

    /* end of file */
    if(len == 0)
        return -1;

    return (unsigned char)c;
}

/* Common code for reading from a FileDescriptor */
Int fd_read_bytes(struct in_java_io_FileDescriptor *fd, Object Harg2, Int off, Int len) 
{
    struct barray *bytes = (struct barray *)Harg2;
    int res;

    if(!bytes || !fd)
        throwNullPointerException("readBytes");

    if(off < 0 || off > bytes->length ||
       len < 0 || off+len > bytes->length)
        throwIndexOutOfBoundsException("readBytes");

    /*
     * we only issue one read even though the read
     * may not complete the request because the fd
     * may be a datagram socket
     */
    do {
        res = read(unix_fd(fd->fd), bytes->data + off, len);
        if(res == -1 && errno != EINTR)
            throwIOException("read", errno);
    } while (res == -1);

    if (res == 0) {
	return -1;		/* EOF */
    } else {
        return res;
    }
}

/* common code to close a FileDescriptor */
Void fd_close(struct in_java_io_FileDescriptor *fd) 
{
    if(!fd)
        throwNullPointerException("close");

    if(fd->fd)
        if (close(unix_fd(fd->fd)) == -1)
            throwIOException("close", errno); 

    /* Zero out this fd since it has been closed.
       This is necessary since finalizers may cause 
       fd_close to be called more than once. */
    fd->fd = 0;
}


/* common code to write to a FileDescriptor */
Void fd_write_char(struct in_java_io_FileDescriptor *fd, Int arg2) 
{
    int len;
    char c;

    if(!fd)
        throwNullPointerException("write");

    c = arg2;
    /* don't throw interrupted exception... simply retry */
    do {
        len = write(unix_fd(fd->fd), &c, 1);
        if(len == -1 && errno != EINTR)
            throwIOException("write", errno);
    } while(len == -1);
}

/* common code to write to a FileDescriptor */
Void fd_write_bytes(struct in_java_io_FileDescriptor *fd, Object Harg2, Int off, Int len) 
{
    struct barray *bytes = (struct barray *)Harg2;
    int wlen;

    if(!bytes || !fd)
        throwNullPointerException("writeBytes");

    if(off < 0 || off > bytes->length ||
       len < 0 || off+len > bytes->length)
        throwIndexOutOfBoundsException("arguments out of range for writeBytes");

    while(len) {
        wlen = write(unix_fd(fd->fd), bytes->data + off, len);
        if(wlen == -1) {
            switch(errno) {
            case EINTR:
                /* don't throw interrupted exception.. simply retry */
                continue;
            default:
                throwIOException("write", errno);
                break;
            }
        } else {
            len -= wlen;
            off += wlen;
        }
    }
}

/* stat a file given its name */
int
name_stat(struct in_java_lang_String *uname, struct stat *buf) 
{
    char *name;

    if(!uname)
        throwNullPointerException("open");
    name = cstring(uname);
   
    return stat(name, buf);
}

/* determine access to a file given its name */
Boolean
name_access(struct in_java_lang_String *uname, int amode) 
{
    char *name;

    if(!uname)
        throwNullPointerException("open");
    name = cstring(uname);
   
    /*
     * we could distinguish between ENOENT and EPERM
     * and throw an exception if EPERM
     * but sun's implementation does not
     */
    if(access(name, amode) == -1)
        return JAVA_FALSE;
    else
        return JAVA_TRUE;
}

/* We encode unixfd's as unixfd + 1 so javafd 0 represents invalid */
int
java_fd(int unixfd)
{
    return unixfd + 1;
}

/* Get back a unix fd given a java fd */
int
unix_fd(int javafd)
{
    return javafd - 1;
}

/* determine if a java fd is valid */
int
java_fd_valid(int javafd)
{
    /*
     * is the javafd is uninitialized (0) the fd is -1 and the 
     * seek fails.  If the unixfd has been closed the seek
     * fails.
     */
    if(lseek(unix_fd(javafd), (off_t)0, SEEK_CUR) == -1)
        return 0;
    else
        return 1;
}


/* fill in a C address structure given the java InetAddress */
void
set_address(struct in_java_net_InetAddress *jaddr, Int port, struct sockaddr_in
*uaddr)
{
    /* assumes jaddr->family = AF_INET */
    if(!jaddr)
        throwNullPointerException("InetAddress");
 
    memset(uaddr, 0, sizeof(*uaddr));
    uaddr->sin_family = AF_INET;
    uaddr->sin_port = htons(port);
    uaddr->sin_addr.s_addr = htonl(jaddr->address);
}

