/*
 * Micro-X -- an X server for DOS
 * Copyright (C) 1993 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 listening = 0xffffffff;

int _fastcall
novell_socket(buf)
short _far *buf;
{
    int fd;
    struct sockaddr_in in;
    int on = 1;
    int type;
    struct net_addr _far *addr = (struct net_addr _far *)&buf[2];

    switch (buf[1]) {
	case NET_TYPE_TCP:
	    type = SOCK_STREAM;
	    break;
	case NET_TYPE_UDP:
	    type = SOCK_DGRAM;
	    break;
	default:
	    dfputs("unknown socket type\r\n");
	    buf[0] = -1;
	    buf[1] = 0;
	    buf[2] = NETERR_BADARGS;
	    return 6;
    }
    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);
    }
    buf[0] = fd;
    return sizeof (struct net_addr) + 2 * sizeof (short);
}

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

int _fastcall
novell_accept(buf)
short _far *buf;
{
    struct net_addr _far *addr = (struct net_addr _far *)&buf[1];
    struct sockaddr_in in;
    int len;
    int ns;

    len = sizeof in;
    ns = accept(buf[1], (SADDR_PTR)&in, &len);
    if (ns < 0)
	return ns;
    addr->host = in.sin_addr.s_addr;
    addr->socket = ntohs(in.sin_port);
    buf[0] = ns;
    return sizeof (struct net_addr) + sizeof (short);
}

int _fastcall
novell_info(buf)
short _far *buf;
{
    struct net_info _far *ni = (struct net_info _far *)&buf[1];

    buf[0] = 0;
    ni->ip_address = getmyipaddr();
    ni->ip_broadcast = 0xffffffff;
    return sizeof (struct net_info) + sizeof (short);
}

int _fastcall
novell_release(buf)
short _far *buf;
{
    int fd = buf[1];
    int ret;

    ret = soclose(fd);
    if (ret < 0)
	return ret;
    listening |= 1L << fd;
    buf[0] = ret;
    return 2;
}

int _fastcall
novell_select(buf)
short _far *buf;
{
    struct timeval ztime;
    int ret;
    fd_set ifds, ofds;

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

int _fastcall
novell_eof(buf)
short _far *buf;
{
    int ret;

    ret = shutdown(buf[1], 1);
    if (ret < 0)
	return ret;
    buf[0] = ret;
    return 2;
}
