#include <signal.h>
#include <socket.h>
#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
#include "beport.h"

#undef write
#undef read
#undef close
#undef select
#undef ioctl
#undef dup2
#undef open
#undef socket
#undef bind
#undef send
#undef recv
#undef receive
#undef connect

static int forked;

int setpgrp(void)
{
	return setpgid(0, 0);
}

int vhangup(void)
{
	return (kill(-getpid(), SIGHUP));
}

#define SOCKBASE 12


int
check_socket(int fam, int type, int proto)
{
	int s;

	s = socket(fam, type, proto);
	if (s < 0) {
		return (-1);
	}
	dprintf("socket() = %d\n", SOCKBASE + s);
	return (SOCKBASE + s);
}


int check_bind(int fd, const struct sockaddr *addr, int size)
{
	int ret;
	dprintf("bind(%d)...\n", fd);
	ret = bind(to_socket(fd), addr, size);
	dprintf("bind(%d) = %d\n", fd, ret);
	return (ret);
}
ssize_t check_send(int fd, const void *buf, size_t size, int flags)
{
	int ret;

	dprintf("send(%d)...\n", fd);
	ret = send(to_socket(fd), buf, size, flags);
	dprintf("send(%d) = %d\n", fd, ret);
	return (ret);
}

ssize_t check_recv(int fd, void *buf, size_t size, int flags)
{
	int ret;

	dprintf("recv(%d)...\n", fd);
	ret = recv(to_socket(fd), buf, size, flags);
	dprintf("recv(%d) = %d\n", fd, ret);

	return (ret);
}
int check_connect(int fd, struct sockaddr *addr, int size)
{
	int ret;

	dprintf("connect(%d)...\n", fd);
	ret = connect(to_socket(fd), addr, size);
	dprintf("connect(%d) = %d\n", fd, ret);
	return (ret);
}


int
is_socket(int fd)
{
	return (fd >= SOCKBASE);
}

int
to_socket(int fd)
{
	return (fd - SOCKBASE);
}

int
setenv(const char *name, const char *value, int overwrite)
{
	char buf[256];

	sprintf(buf, "%s=%s", name, value);
	return (putenv(buf));
}

int
unsetenv(const char *name)
{
	char buf[256];

	sprintf(buf, "%s=", name);
	return (putenv(buf));
}

int
check_dup2(int fd, int fd2)
{
	int ret;

	if (is_socket(fd) && !forked) {
		dprintf("Warning: dup2 on socket\n");
		errno = ENOTTY;
		return (-1);
	} else {
		dprintf("dup2(%d, %d)\n", fd, fd2);
		ret = dup2(fd, fd2);
		if (fd2 == 0) {
			forked++;
		}
		return (ret);
	}
	
}

#if DEBUG
static void
showbuf(const char *buf, int len)
{
	int i;
	char line[80];
	unsigned char c;

	for (i = 0; i < len; i++) {
		c = buf[i];
		if (c >= ' ' && c <= '~') {
			sprintf(&line[(i % 25) * 3], " %c ", c);
		} else {
			sprintf(&line[(i % 25) * 3], "x%02x", c);
		}
		if ((i % 25) == 24) {
			dprintf("%s\n", line);
		}
	}
	if ((i % 25) != 0) {
		dprintf("%s\n", line);
	}
}
#else
#define showbuf(x, y)
#endif

int
check_write(int fd, const char *buf, int len)
{
	int ret;

	if (is_socket(fd) && !forked) {
		dprintf("Warning: send(%d, %d)...\n", fd, len);
		showbuf(buf, len);
		ret = send(to_socket(fd), buf, len, 0);
		dprintf("send(%d, %d) = %d\n", fd, len, ret);
		return (ret);
	} else {
		dprintf("write(%d, %d)...\n", fd, len);
		showbuf(buf, len);
		ret = write(fd, buf, len);
		dprintf("write(%d, %d) = %d\n", fd, len, ret);
		return (ret);
	}
}

int
check_read(int fd, char *buf, int len)
{
	int ret;

	if (is_socket(fd) && !forked) {
		dprintf("Warning: recv(%d, %d)...\n", fd, len);
		ret = recv(to_socket(fd), buf, len, 0);
		if (ret >= 0) {
			showbuf(buf, ret);
		}
		dprintf("recv(%d, %d) = %d\n", fd, len, ret);
		return (ret);
	} else {
		dprintf("read(%d, %d)...\n", fd, len);
		ret = read(fd, buf, len);
		if (ret >= 0) {
			showbuf(buf, ret);
		}
		dprintf("read(%d, %d) = %d\n", fd, len, ret);
		return (ret);
	}
}

int
check_close(int fd)
{
	if (is_socket(fd) && !forked) {
		dprintf("Warning: closesocket(%d)\n", fd);
		return (closesocket(to_socket(fd)));
	} else {
		dprintf("close(%d)\n", fd);
		return (close(fd));
	}
}

int
check_open(const char *filename, int flags, int mode)
{
	int ret;

	ret = open(filename, flags, mode);
	dprintf("open(%s) = %d\n", filename, ret);
	return (ret);
}

int
check_ioctl(int fd, int cmd, char *data)
{
	int ret;

	if (is_socket(fd) && !forked) {
		if (cmd == FIONBIO) {
			int yes = 1;
			int ret;

			dprintf("setsockopt(%d)...\n", fd);
			ret = setsockopt(to_socket(fd), SOL_SOCKET, SO_NONBLOCK, &yes, 
							 sizeof(yes));
			dprintf("setsockopt(%d) = %d\n", fd, ret);
			return (ret);

		}
		dprintf("Warning: ioctl on socket (%d)\n", fd);
		errno = ENOTTY;
		return (-1);
	} else {
		if (cmd == 1) {
			debugger("ioctl");
		}
		dprintf("ioctl(%d, %08x)...\n", fd, cmd);
		ret = ioctl(fd, cmd, data);
		dprintf("ioctl(%d, %08x) = %d\n", fd, cmd, ret);
		return (ret);
	}
}

