/*
 * 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 <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <xti.h>
#include <tklib.h>
#include <sys/tk_types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <errno.h>
#include "funcdef.h"
#include "../defs.h"

unsigned long my_address;

int _fastcall
nfs_init(arg, len, buf)
int arg, _near *len;
void _far *buf;
{
    int i;

    fputs("PC/NFS transport layer\n", stdout);
    i = rtm_install(T_TCP);
    if (i < 0) {
	t_errno = TSYSERR;
	if (errno == ENOENT)
	    fputs("\nPC/NFS is not installed.\n", stdout);
	else
	    t_error("rtm_install");
	return -1;
    }
    *len = 0;
    return 0;
}

int _fastcall
nfs_shutdown(arg, len, buf)
int arg, _near *len;
void _far *buf;
{
    rtm_cleanup();
    return 0;
}

int _fastcall
nfs_socket(arg, len, buf)
int arg, _near *len;
void _far *buf;
{
    struct net_addr _far *addr = (struct net_addr _far *)buf;
    int fd;
    char *name;
    struct t_bind *bind;
    struct sockaddr_in *sap;

    switch (arg) {
	case NET_TYPE_TCP:
	    name = T_TCP;
	    break;
	case NET_TYPE_UDP:
	    name = T_UDP;
	    break;
	default:
	    dfputs("unknown socket type\r\n");
	    return -1;
    }
    fd = t_open(name, O_RDWR|O_NONBLOCK, NULL);
    if (fd < 0)
	return -1;
    bind = (struct t_bind *)t_alloc(fd, T_BIND, T_ALL);
    if (!bind) {
	t_error("t_alloc");
	t_close(fd);
	return -1;
    }
    bind->qlen = 1;
    bind->addr.len = sizeof (struct sockaddr_in);
    sap = (struct sockaddr_in *)bind->addr.buf;
    sap->sin_family = AF_INET;
    sap->sin_port = htons(addr->socket);
    sap->sin_addr.s_addr = INADDR_ANY;

    if (t_bind(fd, bind, bind) < 0) {
	t_error("t_bind");
	t_free((char *)bind, T_BIND);
	(void)t_close(fd);
	return -1;
    }
    my_address = sap->sin_addr.s_addr;
    addr->host = sap->sin_addr.s_addr;
    addr->socket = ntohs(sap->sin_port);
    *len = sizeof (struct net_addr);
    t_free((char *)bind, T_BIND);
    return fd;
}

int _fastcall
nfs_read(arg, len, buf)
int arg, _near *len;
void _far *buf;
{
    int flags;
    int ret;
    struct t_discon *discon;

    ret = t_rcv(arg, buf, *len, &flags);
    if (ret < 0) {
	if (t_errno == TLOOK) {
	    switch (ret = t_look(arg)) {
		case T_ORDREL:
		    (void)t_rcvrel(arg);
		    t_errno = TOUTSTATE;
		    return -1;
		case T_DISCONNECT:
		    discon = (struct t_discon *)t_alloc(arg, T_DIS, 0);
		    if (discon) {
			discon->udata.buf = buf;
			discon->udata.maxlen = *len;
		    }
		    if (t_rcvdis(arg, discon) < 0)
			t_error("t_rcvdis");
		    if (discon) {
			discon->udata.buf = 0;
			discon->udata.maxlen = 0;
			ret = discon->udata.len;
			t_free((char *)discon, T_DIS);
			if (ret) {
			    *len = ret;
			    return ret;
			}
		    }
		    t_errno = TOUTSTATE;
		    return -1;
		default:
printf("t_rcv: state=%d\n", ret);
	    }
	}
	return -1;
    }
    *len = ret;
    return ret;
}

int _fastcall
nfs_drain(arg, len, buf)
int arg, _near *len;
void _far *buf;
{
    char junk[512];
    int ret = sizeof junk;

    return nfs_read(arg, &ret, junk);
}

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

    ret = t_snd(arg, buf, *len, 0);
    if (ret < 0)
	return ret;
    *len = ret;
    return ret;
}

int _fastcall
nfs_recv(arg, len, buf)
int arg, _near *len;
void _far *buf;
{
    struct net_addr _far *addr = (struct net_addr _far *)buf;
    struct t_unitdata *ud;
    int flags;

    ud = (struct t_unitdata *)t_alloc(arg, T_UNITDATA, T_ADDR);
    if (!ud)
	return -1;
    ud->udata.buf = (char _far *)buf + sizeof (struct net_addr);
    ud->udata.maxlen = *len - sizeof (struct net_addr);
    ud->udata.len = 0;
    if (t_rcvudata(arg, ud, &flags) < 0) {
	ud->udata.buf = NULL;
	t_free((char *)ud, T_UNITDATA);
	return -1;
    }
    addr->host = ((struct sockaddr_in *)ud->addr.buf)->sin_addr.s_addr;
    addr->socket = ntohs(((struct sockaddr_in *)ud->addr.buf)->sin_port);
    flags = ud->udata.len;
    ud->udata.buf = NULL;
    t_free((char *)ud, T_UNITDATA);
    *len = flags + sizeof (struct net_addr);
    return flags;
}

int _fastcall
nfs_sendto(arg, len, buf)
int arg, _near *len;
void _far *buf;
{
    struct sendto_head head = *(struct sendto_head _far *)buf;
    struct t_unitdata *ud;

    ud = (struct t_unitdata *)t_alloc(arg, T_UNITDATA, T_ADDR);
    if (!ud)
	return -1;
    ud->udata.buf = (char _far *)buf + sizeof (struct sendto_head);
    ud->udata.maxlen = *len - sizeof (struct sendto_head);
    ud->udata.len = *len - sizeof (struct sendto_head);
    ud->addr.len = sizeof (struct sockaddr_in);
    ((struct sockaddr_in *)ud->addr.buf)->sin_family = AF_INET;
    ((struct sockaddr_in *)ud->addr.buf)->sin_addr.s_addr = head.addr.host;
    ((struct sockaddr_in *)ud->addr.buf)->sin_port = htons(head.addr.socket);
    if (t_sndudata(arg, ud) < 0) {
	ud->udata.buf = 0;
	ud->udata.maxlen = 0;
	t_free((char *)ud, T_UNITDATA);
	return -1;
    }
    ud->udata.buf = 0;
    ud->udata.maxlen = 0;
    t_free((char *)ud, T_UNITDATA);
    return 0;	/* ??? */
}
