/*
 * fop_file.c: iml file operation - ``file'' scheme
 */

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <ulimit.h>
#include <utime.h>
#include <sys/time.h>
#include <sys/uio.h>

#include "fop.h"


typedef struct {
    char *		name;
    void *		function;
} fop_name_function_t;


typedef union {
    int		fd;
    DIR *	dirp;
} fop_data_t;


static fop_data_t *
fop_open(
    fopc_t *		fopc,
    const char *	path,
    int			oflag,
    mode_t		mode
)
{
    int			fd;
    fop_data_t *	data;

    fd = open(path, oflag, mode);
    if (0 <= fd) {
	data = (fop_data_t *)malloc(sizeof (fop_data_t));
	if (NULL == data) {
	    close(fd);
	    errno = EAGAIN;
	    return NULL;
	}
	data->fd = fd;
	return data;
    } else {
	return NULL;
    }
}


static int
fop_close(
    fopc_t *		fopc,
    fop_data_t *	data
)
{
    int		fd;

    if (NULL == data) {
	errno = EBADF;
	return -1;
    }

    fd = data->fd;
    free(data);

    return close(fd);
}


static fop_data_t *
fop_creat(
    fopc_t *		fopc,
    const char *	path,
    mode_t		mode
)
{
    int			fd;
    fop_data_t *	data;

    fd = creat(path, mode);
    if (0 <= fd) {
	data = (fop_data_t *)malloc(sizeof (fop_data_t));
	if (NULL == data) {
	    close(fd);
	    errno = EAGAIN;
	    return NULL;
	}
	data->fd = fd;
	return data;
    } else {
	return NULL;
    }
}


static size_t
fop_read(
    fopc_t *		fopc,
    fop_data_t *	data,
    void *		buf,
    size_t		nbyte
)
{
    if (NULL == data) {
	errno = EBADF;
	return -1;
    }

    return read(data->fd, buf, nbyte);
}


static int
fop_readv(
    fopc_t *			fopc,
    fop_data_t *		data,
    const struct iovec *	iov,
    int				iovcnt
)
{
    if (NULL == data) {
	errno = EBADF;
	return -1;
    }

    return readv(data->fd, iov, iovcnt);
}


static size_t
fop_write(
    fopc_t *		fopc,
    fop_data_t *	data,
    const void *	buf,
    size_t		nbyte
)
{
    if (NULL == data) {
	errno = EBADF;
	return -1;
    }

    return write(data->fd, buf, nbyte);
}


static int
fop_writev(
    fopc_t *			fopc,
    fop_data_t *		data,
    const struct iovec *	iov,
    int				iovcnt
)
{
    if (NULL == data) {
	errno = EBADF;
	return -1;
    }

    return writev(data->fd, iov, iovcnt);
}


static off_t
fop_lseek(
    fopc_t *		fopc,
    fop_data_t *	data,
    off_t		offset,
    int			whence
)
{
    if (NULL == data) {
	errno = EBADF;
	return -1;
    }

    return lseek(data->fd, offset, whence);
}


static int
fop_stat(
    fopc_t *		fopc,
    const char *	path,
    struct stat *	buf
)
{
    return stat(path, buf);
}


static int
fop_lstat(
    fopc_t *		fopc,
    const char *	path,
    struct stat *	buf
)
{
    return lstat(path, buf);
}


static int
fop_fstat(
    fopc_t *		fopc,
    fop_data_t *	data,
    struct stat *	buf
)
{
    if (NULL == data) {
	errno = EBADF;
	return -1;
    }

    return fstat(data->fd, buf);
}


static int
fop_symlink(
    fopc_t *		fopc,
    const char *	name1,
    const char *	name2
)
{
    return symlink(name1, name2);
}


static int
fop_readlink(
    fopc_t *		fopc,
    const char *	path,
    char *		buf,
    size_t		bufsiz
)
{
    return readlink(path, buf, bufsiz);
}


static int
fop_link(
    fopc_t *		fopc,
    const char *	existing,
    const char *	new
)
{
    return link(existing, new);
}


static int
fop_unlink(
    fopc_t *		fopc,
    const char *	path
)
{
    return unlink(path);
}


static int
fop_rename(
    fopc_t *		fopc,
    const char *	old,
    const char *	new
)
{
    return rename(old, new);
}


static int
fop_fcntl(
    fopc_t *		fopc,
    fop_data_t *	data,
    int			cmd,
    ...
)
{
    va_list		ap;
    int			arg_int;
    struct flock *	arg_flock;
#if defined(F_FREESP64) || defined(F_GETLK64) || \
    defined(F_SETLK64) || defined(F_SETLKW64)
    struct flock64 *	arg_flock64;
#endif /* F_FREESP64 || F_GETLK64 || F_SETLK64 || F_SETLKW64 */
    int			rv;

    if (NULL == data) {
	errno = EBADF;
	return -1;
    }

    va_start(ap, cmd);
    switch(cmd) {
    case F_DUPFD:
#if defined(F_DUP2FD)
    case F_DUP2FD:
#endif /* F_DUP2FD */
    case F_SETFD:
    case F_SETFL:
    case F_SETOWN:
	arg_int = va_arg(ap, int);
	rv = fcntl(data->fd, cmd, arg_int);
	break;
#if defined(F_FREESP)
    case F_FREESP:
#endif /* F_FREESP */
    case F_GETLK:
    case F_SETLK:
    case F_SETLKW:
	arg_flock = va_arg(ap, struct flock *);
	rv = fcntl(data->fd, cmd, arg_flock);
	break;
#if __WORDSIZE != 64 && !defined(__USE_FILE_OFFSET64)
	/* When __WORDSIZE is 64 or __USE_FILE_OFFSET64 is defined,
	   F_*64 has similar number to F_*(non-64) */
#if defined(F_FREESP64)
    case F_FREESP64:
#endif /* F_FREESP64 */
#if defined(F_GETLK64)
    case F_GETLK64:
#endif /* F_GETLK64 */
#if defined(F_SETLK64)
    case F_SETLK64:
#endif /* F_SETLK64 */
#if defined(F_SETLKW64)
    case F_SETLKW64:
#endif /* F_SETLKW64 */

#if defined(F_FREESP64) || defined(F_GETLK64) || defined(F_SETLK64) || defined(F_SETLKW64)
	arg_flock64 = va_arg(ap, struct flock64 *);
	rv = fcntl(data->fd, cmd, arg_flock64);
	break;
#endif /* F_FREESP64 || F_GETLK64 || F_SETLK64 || F_SETLKW64 */
#endif
    default:
	rv = fcntl(data->fd, cmd, 0);
	break;
    }
    va_end(ap);

    return rv;
}


static int
fop_truncate(
    fopc_t *		fopc,
    const char *	path,
    off_t		length
)
{
    return truncate(path, length);
}


static int
fop_ftruncate(
    fopc_t *		fopc,
    fop_data_t *	data,
    off_t		length
)
{
    if (NULL == data) {
	errno = EBADF;
	return -1;
    }

    return ftruncate(data->fd, length);
}


static int
fop_mkdir(
    fopc_t *		fopc,
    const char *	path,
    mode_t		mode
)
{
    return mkdir(path, mode);
}


static int
fop_rmdir(
    fopc_t *		fopc,
    const char *	path
)
{
    return rmdir(path);
}


static fop_data_t *
fop_opendir(
    fopc_t *		fopc,
    const char *	dirname
)
{
    DIR *		dirp;
    fop_data_t *	data;

    dirp = opendir(dirname);
    if (NULL != dirp) {
	data = (fop_data_t *)malloc(sizeof (fop_data_t));
	if (NULL == data) {
	    closedir(dirp);
	    errno = EAGAIN;
	    return NULL;
	}
	data->dirp = dirp;
	return data;
    } else {
	return NULL;
    }

}


static struct dirent *
fop_readdir(
    fopc_t *		fopc,
    fop_data_t *	data
)
{
    if (NULL == data) {
	errno = EBADF;
	return NULL;
    }

    return readdir(data->dirp);
}


static int
fop_closedir(
    fopc_t *		fopc,
    fop_data_t *	data
)
{
    DIR *	dirp;

    if (NULL == data) {
	errno = EBADF;
	return -1;
    }

    dirp = data->dirp;
    free(data);

    return closedir(dirp);
}


static int
fop_access(
    fopc_t *		fopc,
    const char *	path,
    int			amode
)
{
    return access(path, amode);
}


static int
fop_chmod(
    fopc_t *		fopc,
    const char *	path,
    mode_t		mode
)
{
    return chmod(path, mode);
}


static int
fop_fchmod(
    fopc_t *		fopc,
    fop_data_t *	data,
    mode_t		mode
)
{
    if (NULL == data) {
	errno = EBADF;
	return -1;
    }

    return fchmod(data->fd, mode);
}


static int
fop_chown(
    fopc_t *		fopc,
    const char *	path,
    uid_t		owner,
    gid_t		group
)
{
    return chown(path, owner, group);
}


#if 0
static int
fop_lchown(
    fopc_t *		fopc,
    const char *	path,
    uid_t		owner,
    gid_t		group
)
{
    return lchown(path, owner, group);
}
#endif /* 0 */


static int
fop_fchown(
    fopc_t *		fopc,
    fop_data_t *	data,
    uid_t		owner,
    gid_t		group
)
{
    if (NULL == data) {
	errno = EBADF;
	return -1;
    }

    return fchown(data->fd, owner, group);
}


static long
fop_pathconf(
    fopc_t *		fopc,
    const char *	path,
    int			name
)
{
    return pathconf(path, name);
}


static long
fop_fpathconf(
    fopc_t *		fopc,
    fop_data_t *	data,
    int			name
)
{
    if (NULL == data) {
	errno = EBADF;
	return -1;
    }

    return fpathconf(data->fd, name);
}


static int
fop_utime(
    fopc_t *			fopc,
    const char *		path,
    const struct utimbuf *	times
)
{
    return utime(path, times);
}


static int
fop_utimes(
    fopc_t *			fopc,
    const char *		path,
    const struct timeval *	times
)
{
    return utimes(path, times);
}


static fop_name_function_t fop_name_function_list[] = {
	{"access",	(void *)fop_access},
	{"chmod",	(void *)fop_chmod},
	{"chown",	(void *)fop_chown},
	{"close",	(void *)fop_close},
	{"close",	(void *)fop_close},
	{"closedir",	(void *)fop_closedir},
	{"creat",	(void *)fop_creat},
	{"fchmod",	(void *)fop_fchmod},
	{"fchown",	(void *)fop_fchown},
	{"fcntl",	(void *)fop_fcntl},
	{"fpathconf",	(void *)fop_fpathconf},
	{"fstat",	(void *)fop_fstat},
	{"ftruncate",	(void *)fop_ftruncate},
#if 0
	{"lchown",	(void *)fop_lchown},
#endif /* 0 */
	{"link",	(void *)fop_link},
	{"lseek",	(void *)fop_lseek},
	{"lstat",	(void *)fop_lstat},
	{"mkdir",	(void *)fop_mkdir},
	{"open",	(void *)fop_open},
	{"opendir",	(void *)fop_opendir},
	{"pathconf",	(void *)fop_pathconf},
	{"read",	(void *)fop_read},
	{"readdir",	(void *)fop_readdir},
	{"readv",	(void *)fop_readv},
	{"rename",	(void *)fop_rename},
	{"rmdir",	(void *)fop_rmdir},
	{"stat",	(void *)fop_stat},
	{"symlink",	(void *)fop_symlink},
	{"readlink",	(void *)fop_readlink},
	{"truncate",	(void *)fop_truncate},
	{"unlink",	(void *)fop_unlink},
	{"utime",	(void *)fop_utime},
	{"utimes",	(void *)fop_utimes},
	{"write",	(void *)fop_write},
	{"writev",	(void *)fop_writev},
	{NULL,		(void *)NULL},
};


void *
fop_scheme_file_get_function(
    const char *	name
)
{
    fop_name_function_t *	nf;

    for (nf = fop_name_function_list; NULL != nf->name; nf += 1) {
	if (0 == strcmp(nf->name, name)) return nf->function;
    }

    return NULL;
}


/* Local Variables: */
/* c-file-style: "iiim-project" */
/* End: */
