/*
 *  DNET.C
 *
 *  DNET (c)Copyright 1988, Matthew Dillon, All Rights Reserved.
 */

#include "dnet.h"

/* WB2.0+ style imbeded version strings */
static const char *ver_String ="\0$VER: DNet 2.202";

#if 0  /* matt's deemu stuff is lame  and outdated anyway */
#ifndef __GNUC__
#include <local/deemu.h>
#else
#include "deemu.h"
#endif
#endif

void do_netreset();
char *GetDEnv();


extern int Enable_Abort;
/* long DResBase; */
char PortName[sizeof(DNETPORTNAME)+32];
extern char * ScriptFile;

#ifdef LATTICE

int __stdargs CXBRK(void);      /*  get around a bug in lcr.lib : onbreak() */

int __stdargs
CXBRK(void)
{
    return(0);
}

#endif

#ifdef __GNUC__
#include <signal.h>
void dummysigint() {};
#endif

#ifdef __GNUC__
int main ARGS((int, char **));
#else
void main ARGS((int, char **));
#endif
static    long baud = 0;
static    char *netdevice = "serial.device";
static    long netunit = 0;

#ifdef __GNUC__
static    char *autoclient = "niftyterm";
#else
static    char *autoclient = "FTERM";
#endif

#ifdef __GNUC__
int
#else
void  /* void main()? whose wacky compiler likes that??  DICE? */
#endif 
main(ac,av)
int ac;
char *av[];
{
    long sink_mask, dnet_mask/*, ipc_mask*/;
    short i;

    /* unlink("ENV:DNET_NORUNCLIENT");  ???  what's the point of this? */
#ifdef MANX
    Enable_Abort = 0;
#endif
#ifdef __GNUC__
    /* trap ctrl-C , disable gcc's handler */
    signal(SIGINT, dummysigint );
#endif
    BZero(Pkts,sizeof(Pkts));
    InitServers();

    for (i = 1; i < ac; ++i) {
	if (strcmp(av[i], "-Getty") == 0) {
	    Getty = 1;
	    AutoHangup = 1;
	    av[i] = "";
	}
	if (strcmp(av[i], "-DEVICE") == 0) {
	    netdevice = av[i + 1];
	    av[i] = "";
	    av[i+1] = "";
	}
	if (strcmp(av[i], "-UNIT") == 0) {
	    netunit = atoi(av[i+1]);
	    av[i] = "";
	    av[i+1] = "";
	}
    }

#ifndef __GNUC__
    ac = DoOption(ac, av, "p,m%d,b%ld,d%d,n%s,s%s,a,h%d,U%ld,D%s,N%d,8,X%d,B%ld,P%d,Z%d,S%s",
	&PDebug, &Mode7, &baud, &DDebug, &HostName, &autoclient, &AutoAnswer,
	&AutoHangup, &netunit, &netdevice, &NetworkNum, &Master8, &DialOut,
	&TOBaud, &Protocol, &Parity, &ScriptFile);
    );
#else
    ac = DoOptions(ac, av);
#endif
    if (ac < 0) {
	puts("Unknown option, valid options:\n");
	puts("dnet -pb#B#d#n<hostname>s<autorunclient>aU#D<device>N<netid>8");
	dneterror("Unknown switch");
    }
    if (AutoAnswer) {
	AutoHangup = 1;
	DialOut = 0;
    }

    if(DDebug ) 
   	 printf("Options done.\n");

    sprintf(PortName, "%s%d", DNETPORTNAME, NetworkNum);
    {
	PORT *port;
	Forbid();
	if (port = (PORT *)FindPort(PortName)) {
	    Permit();
	    puts("DNET: Network number in use");
	    /*
	    CloseLibrary(DResBase);
	    */
	    exit(1);
	} else
	    Permit();
    }

    if(DDebug)printf("Creating DNetPort \"%s\"...\n", PortName);
    DNetPort = (PORT *)CreatePort(PortName, 0);
    IOSink   = (PORT *)CreatePort(NULL,0);
    /*
    IPCPort  = (PORT *)OpenIPC("dnet.CMD", 0);
    */
    if (!DNetPort || !IOSink)
	dneterror("CreatePort");

    NewList((LIST *)&TxList);
    Rto_act = Wto_act = Cto_act = 0;
    if(DDebug) printf("DoingNetOpenRead\n");
    NetOpen(IOSink, netdevice, netunit, &baud);
    TimerOpen(&Rto, IOSink);
    TimerOpen(&Wto, IOSink);
    TimerOpen(&Cto, IOSink);

    if (TOBaud)
	SetTimeouts(TOBaud);
    else
	SetTimeouts(Baud);

    if(DDebug) printf("Doing NetStartRead\n");

    NetStartRead(3);

    if(DDebug) printf("Doing do_dnetwindow\n");
    do_dnetwindow(baud);

    if(DDebug) printf("Done Doing do_dnetwindow\n");

    if (Quit)
	dneterror(NULL);

    Rto.tr_node.io_Message.mn_Node.ln_Name = (char *)RTO_REQ;
    Wto.tr_node.io_Message.mn_Node.ln_Name = (char *)WTO_REQ;
    Cto.tr_node.io_Message.mn_Node.ln_Name = (char *)CTO_REQ;

    sink_mask = 1 << IOSink->mp_SigBit;
    dnet_mask = 1 << DNetPort->mp_SigBit;
    /*
    ipc_mask = 1 << IPCPort->mp_SigBit;
    */

    if(DDebug) printf("Doing do_netreset\n");
    do_netreset();

    if(DDebug) printf("Starting loop\n");
loop:
    if(DDebug) printf("Autoclient is %s\n", autoclient);
    if (strcmp(autoclient, "-") != 0) {
	FILE *fi = fopen("T:dscr", "w");
	if (fi) {
	    fprintf(fi, "Wait 10\n");
	    fprintf(fi, "%s -N%ld\n", autoclient, NetworkNum);
	    fclose(fi);
	    Execute("RUN <NIL: >NIL: execute T:dscr", NULL, NULL);
	}
	if(DDebug) printf("Autoclient executing...\n");
    }

    if(DDebug) printf("Doing WriteRestart()...\n");
    WriteRestart();
    Restart = 1;
    OnLine = 1;

    /*
     *	NOTE:	Routines must be particularly careful not to clear the
     *		signal mask unless it doesn't matter.  Specifically,
     *		routines in the dnet_mask section cannot arbitrarily
     *		clear the signal associated with the sink_mask section.
     *
     *		If you look at NetWrite(), you will note that the signal
     *		is restored if it must do a WaitIO().
     */

    /*
     *	Immediate return from initial Wait() ... due to looping it is
     *	possible one or more requests is ready but the signal bit has
     *	already been cleared.
     */

    Signal(FindTask(NULL), /*ipc_mask|*/ sink_mask|dnet_mask);

    while (!Quit && OnLine) {
	long mask;
	if(DDebug>1) printf("Waiting in dnet while loop.\n");
	mask = Wait(/*ipc_mask|*/sink_mask|dnet_mask|SIGBREAKF_CTRL_C);
	if(DDebug>1) printf("Processing..\n");

	/*
	if (mask & ipc_mask)
	    handle_ipc();
	*/

	if (mask & sink_mask) {     /*  IOSink returns      */
	    IOSTD *ior;
	    while (ior = (IOSTD *)GetMsg(IOSink)) {
		switch((long)ior->io_Message.mn_Node.ln_Name) {
		case PKT_REQ:
		    --NumCon;
		    if (ior->io_Length)
			FreeMem(ior->io_Data, ior->io_Length);
		    FreeMem(ior, sizeof(IOSTD));
		    break;
		case CTO_REQ:	/*  Only when line idle 	    */
		    Cto_act = 0;
		    do_cto((IOT *)ior);
		    if (Cd == 0 && AutoHangup)
			OnLine = 0;
		    break;
		case RTO_REQ:	/*  Read timeout, reset READ state  */
		    Rto_act = 0;
		    do_rto((IOT *)ior);
		    break;
		case WTO_REQ:	/*  Write-Ack timeout, send CHECK   */
		    Wto_act = 0;
		    do_wto((IOT *)ior);
		    break;
		case RNET_REQ:	/*  Receive data ready, interpret   */
		    {
			unsigned char *ptr;
			long bytes = NetReadReturned(&ptr);
			do_rnet(ptr, bytes);
		    }
		    if (Cd == 0 && AutoHangup)
			OnLine = 0;
		    break;
		case WNET_REQ:
		    NetClWrite((IOSER *)ior);
		    break;
		}
	    }
	}

	if (mask & dnet_mask) {     /*  Receive commands    */
	    IOSTD *ior;
	    while (ior = (IOSTD *)GetMsg(DNetPort)) 
	        ProcessDNetMsgs( ior);
	}

	if (mask & SIGBREAKF_CTRL_C)  /* handle signal breaking */
	    OnLine = 0;
	if(DDebug >1 )printf("Done processing, doing do_update()\n");
	do_wupdate();
    }
    if(DDebug)printf("Offline? doing do_netreset()\n");
    do_netreset();
    if (!Cd) {
	ResetConnect();
	ResetIdle();
    }
    if (!Quit)
	do_dnetwindow(baud);
    if (!Cd) {
	ResetConnect();
	ResetIdle();
    }
    if (!Quit)
	goto loop;
    if(DDebug)printf("Quitting, done.\n");
    dneterror(NULL);
}

void
do_netreset()
{
    short i;
    CHAN *ch;
    IOSTD *ior;

    while (ior = (IOSTD *)RemHead((LIST *)&TxList)) {
	ior->io_Error = 1;
	ReplyMsg((MSG *)ior);
    }
    for (i = 0, ch = Chan; i < MAXCHAN; ++i, ++ch) {
	switch(ch->state) {
	case CHAN_OPEN:
	    WritePort(Chan[i].port, DNCMD_CLOSE, NULL, 0, PKT_REQ, i);
	case CHAN_ROPEN:	/*  pending on listen port  */
	    ch->state = CHAN_CLOSE;
	    ch->flags = CHANF_RCLOSE;
	    ch->ior = NULL;
	    break;
	case CHAN_LOPEN:	/*  pending on network	    */
	    ch->ior->io_Error = 1;
	    ReplyMsg((MSG *)ch->ior);
	    ch->ior = NULL;
	    ch->state = CHAN_FREE;
	    ch->flags = 0;
	    --NumCon;
	    break;
	case CHAN_CLOSE:
	    if (!(ch->flags & CHANF_LCLOSE))
		break;
	    ch->ior->io_Error = 1;
	    ReplyMsg((MSG *)ch->ior);
	    ch->ior = NULL;
	    ch->state = CHAN_FREE;
	    ch->flags = 0;
	    --NumCon;
	}
    }
    RPStart = 0;
    WPStart = 0;
    WPUsed  = 0;
    RState  = 0;
    RChan = 0;
    WChan = 0;
    if (!Cto_act) {
	Cto.tr_time.tv_secs = 1;
	Cto.tr_time.tv_micro= 0;
	SendIO((IOR *)&Cto);
	Cto_act = 1;
    }
}

/* handle IORequests from the DNet port. This is other amiga clients
** talking _TO_ the DNet 'server' process on the amiga end */
ProcessDNetMsgs(IOSTD * ior){
    if(ior == NULL) return;

    if(PDebug){
       printf("Processing ior at %lx, chan %d, cmd %d,length %d\n",
       ior, (ulong) ior->io_Unit, ior->io_Command, ior->io_Length);
    }
    	
    ior->io_Actual = 0;  /* mark that the IORequest has ben processed */
    switch(ior->io_Command) {
      case DNCMD_WRITE:	/*  A data packet to be written */
	{
	    uword chan = (ulong)ior->io_Unit;
	    if (Chan[chan].state != CHAN_OPEN) {
		ior->io_Error = 1;
		break;
	    }
	    ior->io_Error = 0;
	    ior->io_Command = SCMD_DATA;
	    ior->io_Message.mn_Node.ln_Pri = Chan[chan].pri;
	    Enqueue(&TxList, (NODE *)ior);
	    ior = NULL;
	}
	break;
      case DNCMD_SOPEN: /*	Reply from server port on remote    */
			/*	open request			    */
	{
	    CACKCMD ack; /* 4 bytes */
	    uword chan = (ulong)ior->io_Unit;


	    ack.chanh = chan >> 8;
	    ack.chanl = chan;
	    ack.error = ior->io_Error;
	    WriteStream(SCMD_ACKCMD, &ack, sizeof(ack), chan);
	    if (ack.error) {
		Chan[chan].state = CHAN_FREE;
		--NumCon;
	    } else {
		if (Chan[chan].state == CHAN_CLOSE && !ack.error) {
		    WritePort(Chan[chan].port, DNCMD_CLOSE, NULL, 0, PKT_REQ, chan);
		    goto sopenbrk;
		}
		Chan[chan].state = CHAN_OPEN;
		Chan[chan].port  = (PORT *)(ior->io_Offset);
		Chan[chan].flags = CHANF_ROK|CHANF_WOK;
	    }
sopenbrk:
	    if (ior->io_Length)
		FreeMem(ior->io_Data, ior->io_Length);
	    FreeMem(ior, sizeof(IOSTD));
	    ior = NULL;
	}
	break;
      case DNCMD_EOF:  /* signal an EOF on a channel */
	{
	    CEOFCMD eof;
	    uword chan = (ulong)ior->io_Unit;

	    ior->io_Error = 0;
	    eof.chanh = chan >> 8;
	    eof.chanl = chan;
	    eof.flags = CHANF_ROK;
	    WriteStream(SCMD_EOFCMD, &eof, sizeof(CEOFCMD), chan);
	    Chan[chan].flags &= ~CHANF_WOK;
	}
	break;
      case DNCMD_IOCTL:
	{
	    CIOCTL cio;
	    uword chan = (ulong)ior->io_Unit;

	    ior->io_Error = 0;
	    cio.chanh = chan >> 8;
	    cio.chanl = chan;
	    cio.valh   = (ubyte)((ulong)ior->io_Data >> 24);
	    cio.vall   = (ubyte)((ulong)ior->io_Data >> 16);
	    cio.valaux = (ubyte)((ulong)ior->io_Data >> 8);
	    cio.cmd = (ubyte)(ulong)ior->io_Data;
	    WriteStream(SCMD_IOCTL, &cio, sizeof(CIOCTL), chan);
	}
	break;
      case DNCMD_QUIT:
	{
	    char dummy;

	    WriteStream(SCMD_QUIT, &dummy, 1, (uword)-1);
	}
	break;
      case DNCMD_INFO:
	{
	    char *ptr = (char *)ior->io_Data;
	    sprintf(ptr, "         Bytes  Packets   Errors\n"
			    "OUT:  %8ld %8ld %8ld\n"
			    "IN :  %8ld %8ld %8ld\n"
			    "Garbage Bytes In: %ld\n\n"
			    "                        6-expn   7-comp   8-bin\n"
			    "Packet Breakdown Send: %8ld %8ld %8ld\n"
			    "Packet Breakdown Recv: %8ld %8ld %8ld\n",

			BytesOut, PacketsOut, PacketsResent,
			BytesIn,  PacketsIn,  PacketsNakd,
			GarbageIn,
			Packets6Out, Packets7Out, Packets8Out,
			Packets6In,  Packets7In,  Packets8In
	    );
	}
	break;
      case DNCMD_OPEN:
	ior->io_Error = 0;
	{
	    uword chan = alloc_channel();
	    COPEN co;
	    if (chan >= MAXCHAN) {
		ior->io_Error = 1;
		break;
	    }
	    co.chanh = chan >> 8;
	    co.chanl = chan;
	    co.porth = (ulong)ior->io_Unit >> 8;  /* port #   */
	    co.portl = (ulong)ior->io_Unit;
	    co.error= 0;
	    co.pri = (char)(long)ior->io_Message.mn_Node.ln_Name;
	    Chan[chan].ior = ior;
	    Chan[chan].port= (PORT *)(ior->io_Offset);
	    Chan[chan].state = CHAN_LOPEN;
	    Chan[chan].flags = 0;
	    Chan[chan].pri = ior->io_Message.mn_Node.ln_Pri;
	    WriteStream(SCMD_OPEN, &co, sizeof(COPEN), chan);
	    ior = NULL;
	}
	break;
      case DNCMD_CLOSE:	/*  same io_Command for CCTL_?	*/
	ior->io_Error = 0;
	{
	    CCLOSE cc;
	    uword chan = (ulong)ior->io_Unit;

	    cc.chanh = chan >> 8;
	    cc.chanl = chan;
	    WriteStream(SCMD_CLOSE, &cc, sizeof(CCLOSE), chan);
	    Chan[chan].ior = ior;
	    Chan[chan].state = CHAN_CLOSE;
	    Chan[chan].flags |= CHANF_LCLOSE;
	    if (Chan[chan].flags & CHANF_RCLOSE) {
		Chan[chan].state = CHAN_FREE;
		Chan[chan].ior = NULL;
	    } else {
		ior = NULL;
	    }
	}
	break;
      case DNCMD_EXEC:
	Execute((char *)(ior->io_Offset), NULL, NULL);
	break;
    }
    if (ior)
	ReplyMsg((MSG *)ior);
}


#ifdef __GNUC__

/* generic argument parsing code */
/* all options with arguments work both as -ffoo and -f foo */

int DoOptions( int pargc, char **pargv){
    void * numptr;
    int numsize;
    char * strptr;
    char ** strargptr;
    int OptionalArg;
    int NumParsed = 0;

    if(DDebug)printf("DoingOptions...\n");

    pargv++; /* skip argv[0] == program name! */
    while(*pargv != NULL){
        if(**pargv == '-'){
	    char swchar;
	    numptr = NULL;
	    numsize = sizeof(int);  /* assume numptrs are int sizes */
	    strargptr = NULL;
	    OptionalArg = 0;
	    swchar = *((*pargv)+1);
	    if(DDebug) 
	    	printf("Checking switch character \"%c\"...\n",swchar);
	    switch(swchar){
	        case 'X': /* set MANUAL mode */
			if(DDebug) printf("DialOut set.\n");
			DialOut = 1; break;
	        case 'a': /* set autoanswer mode */
			if(DDebug) printf("AutoAnswer set.\n");
			AutoAnswer = 1; break;
	        case '8': /* set 8-bit mode */
			if(DDebug) printf("Master8 set.\n");
			Master8 = 1; break;
	        case 'd': /* set debug mode on : -d or -dnum */
			if(DDebug) printf("setting Debug mode\n");
			DDebug = 1;
			numptr = &DDebug; 
			numsize = sizeof(DDebug);
			OptionalArg = 1;
			break;
	        case 'p': /* set packet debug mode on */
			if(DDebug) printf("Packet Debug set.\n");
			PDebug = 1; break;
	        case 'h': /* set enable Carrier Checking */
			if(DDebug) printf("Carrier Checking set.\n");
			AutoHangup = 1; break;
	        case 'F': /* set enable Carrier Checking */
			if(DDebug) printf("Fast8 set.\n");
			Fast8 = 1; break;

		/* string argument options */
	        case 's': /* set autostartup client mode -sclient */
			if(DDebug) printf("setting autoclient\n");
			strargptr = &autoclient; break;
	        case 'n': /* set hostname mode -nhostname*/
			if(DDebug) printf("setting hostname\n");
			strargptr = &HostName; break;
	        case 'D': /* set device -DDevice */
			if(DDebug) printf("setting netdevice\n");
			strargptr = &netdevice; break;
	        case 'S': /* set autoscript */
			if(DDebug) printf("setting autoscript\n");
			strargptr = &ScriptFile; break;

		/* numeric argument options */
	        case 'b': /* set baudrate mode  -bbaud */
			if(DDebug) printf("setting baud\n");
			numptr = &baud; break;
	        case 'B': /* set baudrate -Bbaud  */
			if(DDebug) printf("setting TOBaud\n");
			numptr = &TOBaud; break;
	        case 'U': /* set unit number -u# */
			if(DDebug) printf("setting netunit\n");
			numptr = &netunit; break;    
	        case 'N': /* set network id# -N# */
			if(DDebug) printf("setting networknum\n");
			numptr = &NetworkNum; 
			numsize = sizeof(NetworkNum);
			break; 
	        case 'm': /* set 8bit/7bit packet mode -m0/-m1 */
			if(DDebug) printf("setting Mode7\n");
			numptr = &Mode7; break;  /* ubyte */
	        case 'P': /* set MANUAL mode -P# */
			if(DDebug) printf("setting Protocol\n");
			numptr = &Protocol; 
			numsize = sizeof(Protocol);
			break;  
	        case 'Z': /* set MANUAL mode -Z# */
			if(DDebug) printf("setting Parity\n");
			numptr = &Parity; 
			numsize = sizeof(Parity);
			break; /* (uword) */
		default:
		    printf("%s:invalid argument\n", *pargv);
		    return -1;
	    }
	    NumParsed ++;
	    if(numptr != NULL ){
		/* pointer to character after argument switch character */
		strptr = (*pargv)+2;

		if(DDebug)
			printf("checking numeric arg for %s\n", *pargv );

		/* check for switch arg: -fbar */
		if( *strptr == '\0'){
		    strptr = NULL;

		    if(DDebug>2) puts("no immediate following arg\n");

		    /* No? how bout for switch arg -f bar */
		    if(*(pargv+1) != NULL){
		    	strptr = *(pargv+1);
			/* make sure it's not -f -bar */
			if( *strptr == '-'){
			    strptr = NULL;
		            if(DDebug>2) puts("following arg-arg (-f -bar)\n");
			} else {
			    /* it was -f bar ; have to skip that extra arg */
		            if(DDebug>2) 
			    	printf("following arg %s %s\n", pargv, strptr);
			    pargv++;
			}
		    } else {
			if(DDebug>2) puts("no following args \n");
		    }
		}

		/* if an arg was found, strptr points to it */
		if(strptr != NULL){
		    switch(numsize){
			case sizeof(short):
			    *((short *)numptr) = (short) atoi( strptr ); 
			    if(DDebug>2) printf("numptr set to %d \n", 
				*((short*)numptr));
			    break;
			case sizeof(char):
			    *((char *)numptr) = (char) atoi( strptr ); 
			    if(DDebug>2) printf("numptr set to %d \n", 
				*((char*)numptr));
			    break;
		    	case sizeof(int):
			default:
			    *((int *)numptr) = atoi( strptr ); 
			    if(DDebug>2) printf("numptr set to %d \n", 
				*((int*)numptr));
		    }
		} else {
		    if(!OptionalArg){
			printf("Requires argument: %s\n", *pargv);
			return -1;
		    }
		}
	    }  else if(strargptr) {
		/* pointer to character after argument switch character */
		strptr = (*pargv)+2;

		/* check for switch arg: -fbar */
		if( *strptr == '\0'){
		    strptr = NULL;

		    /* No? how bout for switch arg -f bar */
		    if(*(pargv+1) != NULL){
		    	strptr = *(pargv+1);
			/* make sure it's not -f -bar */
			if( *strptr == '-'){
			    strptr = NULL;
			} else {
			    /* it was -f bar need to skip that extra arg bar */
			    pargv++;
			}
		    }
		}

		/* if an arg was found, strptr points to it */
		if(strptr){
		    *strargptr = strptr;
		} else {
		    printf("Requires argument: %s\n", *pargv);
		    return -1;
		}
	    }
	} else {
	    printf("Invalid Option: %s\n", *pargv);
	    return -1;
	}
	pargv++;
    }
    if(DDebug){
    	printf("After DoOptions() settings are:\n");
	printf("DialOut: %d\tAutoAnswer %d\n", DialOut, AutoAnswer);
	printf("Master8: %d\tDDebug %d\n", Master8, DDebug);
	printf("PDebug: %d\tAutoHangup %d\n", PDebug, AutoHangup);

	printf("autoclient: \"%s\"\tHostName:\"%s\"\n", autoclient, HostName);
	printf("netdevice: \"%s\"\n", netdevice );
	printf("ScriptFile: \"%s\"\n", ScriptFile );

	printf("baud: %d\tTOBaud %d\n", baud, TOBaud);
	printf("netunit: %d\tNetworkNum %d\n", netunit, NetworkNum);
	printf("Mode7: %d\tProtocol %d\n", Mode7, Protocol);
	printf("Parity: %d\n", Parity);

    }
    return NumParsed;
}
#endif __GNUC__
