/*	@(#)receive.c	1.1	1/26/85	*/

/*
 *	receive - routines for receiving data
 *
 *	r e c s w
 */

#include "kermit.h"
#include "errors.h"

static char rinit ();
static char rfile ();
static char rdata ();


VOID Receive ()
{
    DBUG_ENTER ("receive");
    DBUG_2 ("rec", "begin receive attempt");
    if (recsw () == FALSE) {
	(VOID) printf ("Receive failed.\n");
    } else {
	(VOID) printf ("OK\n");
    }
    DBUG_VOID_RETURN;
}


recsw ()
{
    DBUG_ENTER ("recsw");
    DBUG_2 ("init", "initializing receive variables");
    state = 'R';		/* receive is the start state */
    n = 0;			/* initialize message number */
    numtry = 0;			/* say no tries yet */
    while (TRUE) {		/* do this as long as necessary */
	DBUG_3 ("states", "state %c found", state);
	switch (state) {
	    case 'D': 
		state = rdata ();
		break;		/* data receive state */
	    case 'F': 
		state = rfile ();
		break;		/* fil receive state */
	    case 'R': 
		state = rinit ();
		break;		/* send initiate state */
	    case 'C': 
		DBUG_RETURN (TRUE);	/* complte state */
	    case 'A': 
		DBUG_RETURN (FALSE);	/* unknown state, abort */
	}
    }
}


/*
 *	 r i n i t
 */

static char rinit ()
{
    auto int len;
    auto int num;
    register char newstate;

    DBUG_ENTER ("rinit");
    if (numtry++ > MAXTRY) {
	newstate = 'A';
    } else {
	DBUG_2 ("state", "begin receive init");
	switch (rpack (&len, &num, packet)) {
	    case 'S': 				/* send-init */
		DBUG_2 ("init", "got S packet");
		rpar (packet);			/* get the init data */
		spar (packet);		/* fill up packet with init info */
		spack ('Y', n, 6, packet);	/* ACK with my parameters */
		oldtry = numtry;
		numtry = 0;			/* and start a new counter */
		n = (n + 1) % 64;		/* bump packet number */
		newstate = 'F';
		break;
	    case FALSE: 
		DBUG_2 ("init", "no S packet");
		newstate = state;
		break;
	    default: 
		newstate = 'A';
		break;
	}
    }
    DBUG_3 ("states", "new state %c", newstate);
    DBUG_RETURN (newstate);
}


/*
 *	r f i l e
 */

static char rfile ()
{
    auto int num;
    auto int len;
    char *bp;
    register char newstate;

    DBUG_ENTER ("rfile");
    if (numtry++ > MAXTRY) {
	DBUG_3 ("tries", "%d tries", numtry - 1);
	newstate = 'A';
    } else {
	switch (rpack (&len, &num, packet)) {
	    case 'S':
		DBUG_2 ("S", "got S packet");
		if (oldtry++ > MAXTRY) {
		    newstate = 'A';
		} else if (num == n - 1) {
		    spar (packet);
		    spack ('Y', num, 6, packet);
		    numtry = 0;
		    newstate = state;
		} else {
		    newstate = 'A';
		}
		break;
	    case 'Z': 				/* end-of-file */
		if (oldtry++ > MAXTRY) {
		    newstate = 'A';
		} else if (num == n - 1) {	/* acknowledge good packet */
		    spack ('Y', num, 0, packet);
		    numtry = 0;
		    newstate = state;
		} else {
		    newstate = 'A';
		}
		break;
	    case 'F': 				/* file-header */
		if (num != n) {
		    newstate = 'A';
		    break;
		}
		DBUG_3 ("pack", "converting %s", packet);
		if (mflg) {
		    for (bp = &packet[0]; *bp != NULL; bp++) {
			*bp = tolower(*bp);
		    }
		}
		DBUG_3 ("files", "open file %s", packet);
		if (!getfil (packet)) {
		    Error (RCVFILE, packet);
		    newstate = 'A';
		} else {
		    if (host) {
			(VOID) printf ("Receiving %s\n", packet);
		    }
		    spack ('Y', n, 0, packet);		/* acknowledge */
		    oldtry = numtry;
		    numtry = 0;
		    n = (n + 1) % 64;
		    newstate = 'D';
		}
		break;
	    case 'B':
		if (num != n) {
		    newstate = 'A';
		} else {
		    spack ('Y', n, 0, packet);
		    newstate = 'C';
		}
		break;
	    case FALSE: 
		newstate = state;
		break;
	    default: 
		newstate = 'A';
		break;
	}
    }
    DBUG_3 ("states", "new state %c", newstate);
    DBUG_RETURN (newstate);
}


/*
 *	r d a t a
 */

static char rdata ()
{
    auto int num;
    auto int len;
    register char newstate;

    DBUG_ENTER ("rdata");
    DBUG_2 ("rcv", "receive data");
    if (numtry++ > MAXTRY) {
	newstate = 'A';
    } else {
	switch (rpack (&len, &num, packet)) {		/* get packet */
	    case 'D': 					/* data */
		DBUG_2 ("rcv", "got D packet");
		DBUG_3 ("rcv", "length %d", len);
		if (num != n) {				/* right packet? */
		    if (oldtry++ > MAXTRY) {
			newstate = 'A';
		    } else if (num == n - 1) {
			spar (packet);
			spack ('Y', num, 6, packet);
			numtry = 0;
			newstate = state;
		    } else {
			newstate = 'A';
		    }
		} else {
		    bufemp (packet, fd, len);
		    spack ('Y', n, 0, packet);
		    oldtry = numtry;
		    numtry = 0;
		    n = (n + 1) % 64;
		    newstate = 'D';
		}
		break;
	    case 'F': 					/* file-send */
		DBUG_2 ("rcv", "got F packet");		
		if (oldtry++ > MAXTRY) {
		    newstate = 'A';
		} else if (num == n - 1) {		/* ack good packet */
		    spack ('Y', num, 0, packet);
		    numtry = 0;
		    newstate = state;
		} else {
		    newstate = 'A';
		}
		break;
	    case 'Z': 					/* end-of-file */
		DBUG_2 ("rcv", "got Z packet");
		if (num != n) {
		    newstate = 'A';
		} else {
		    spack ('Y', n, 0, packet);		/* say OK */
		    (VOID) close (fd);			/* close up file */
		    n = (n + 1) % 64;
		    newstate = 'F';
		}
		break;
	    case FALSE: 
		newstate = state;
		break;
	    default: 
		newstate = 'A';
		break;
	}
    }
    DBUG_3 ("states", "new state %c", newstate);
    DBUG_RETURN (newstate);
}
