/*
 * Micro-X -- an X server for DOS
 * Copyright (C) 1994 StarNet Communications Corp.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 * 
 * StarNet Communications Corp.
 * 550 Lakeside Dr. #3
 * Sunnyvale CA 94086 US
 * http://www.starnet.com
 * x-dos@starnet.com
 */

#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include "funcdef.h"
#include "../defs.h"

/*
 * We shouldn't select on a listening socket for write because a listening
 * socket will always appear writable.  Therefore we keep a mask of sockets
 * that aren't listening and AND that with the ofds given to us in the
 * select call.
 */
static u_long _near listening = 0xffffffff;

int _fastcall
novell_socket(arg, len, buf)
int arg, _near *len;
void _far *buf;
{
    int fd;
    struct sockaddr_in in;
    int on = 1;
    int type;
    struct net_addr _far *addr = (struct net_addr _far *)buf;

    switch (arg) {
	case NET_TYPE_TCP:
	    type = SOCK_STREAM;
	    break;
	case NET_TYPE_UDP:
	    type = SOCK_DGRAM;
	    break;
	default:
	    printn(arg);
	    dfputs(": unknown socket type\r\n");
	    errno = EPROTOTYPE;
	    return -1;
    }
    fd = socket(AF_INET, type, 0);
    if (fd < 0) {
	net_perror("socket");
	return fd;
    }

    if (ioctl(fd, FIONBIO, (char *)&on) < 0) {
	net_perror("ioctl");
	soclose(fd);
	return -1;
    }
    (void)setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on);
    in.sin_family = AF_INET;
    in.sin_port = htons(addr->socket);
    in.sin_addr.s_addr = addr->host;
    memset(in.sin_zero, 0, sizeof in.sin_zero);

    if (bind(fd, (SADDR_PTR)&in, sizeof in) < 0) {
	net_perror("bind");
	soclose(fd);
	return -1;
    }
    if (type == SOCK_STREAM) {
	(void)listen(fd, 5);
	listening &= ~(1L << fd);
    }
    addr->socket = ntohs(in.sin_port);
    addr->host = in.sin_addr.s_addr;
    *len = sizeof (struct net_addr);
    return fd;
}

void
net_perror(str)
char _near *str;
{
    dfputs(str);
    dfputs(": Error ");
    printd(errno);
    dfputs("\r\n");
}

int _fastcall
novell_accept(arg, len, buf)
int arg, _near *len;
void _far *buf;
{
    struct sockaddr_in in;
    int inlen;
    int ns;

    inlen = sizeof in;
    ns = accept(arg, (SADDR_PTR)&in, &inlen);
    if (ns < 0)
	return ns;
    ((struct net_addr _far *)buf)->socket = ntohs(in.sin_port);
    ((struct net_addr _far *)buf)->host = in.sin_addr.s_addr;
    *len = sizeof (struct net_addr);
    return ns;
}

int _fastcall
novell_info(arg, len, buf)
int arg, _near *len;
void _far *buf;
{
    ((struct net_info _far *)buf)->ip_address = getmyipaddr();
    ((struct net_info _far *)buf)->ip_broadcast = 0xffffffff;
    *len = sizeof (struct net_info);
    return 0;
}

int _fastcall
novell_release(arg, len, buf)
int arg, _near *len;
void _far *buf;
{
    int ret;

    ret = soclose(arg);
    if (ret < 0)
	return ret;
    listening |= 1L << arg;
    return ret;
}

int _fastcall
novell_select(arg, len, buf)
int arg, _near *len;
void _far *buf;
{
    struct timeval ztime;
    int ret;
    fd_set ifds, ofds;

    ztime.tv_sec = 0;
    ztime.tv_usec = 0;
    *(long *)&ifds = ((long _far *)buf)[0];
    *(long *)&ofds = ((long _far *)buf)[1];
    ofds.fds_bits[0] &= listening;
    ret = select(32, &ifds, &ofds, 0, &ztime);
    if (ret < 0)
	return ret;
    ((long _far *)buf)[0] = *(long *)&ifds;
    ((long _far *)buf)[1] = *(long *)&ofds;
    *len = 2 * sizeof (long);
    return ret;
}

int _fastcall
novell_eof(arg, len, buf)
int arg, _near *len;
void _far *buf;
{
    return shutdown(arg, 1);
}
