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

#include "kermit.h"

static char sinit ();
static char sfile ();


VOID Send (argv)
char *argv[];
{
    extern int optind;
    
    DBUG_ENTER ("send");
    DBUG_2 ("send", "begin send attempt");
    filnam = argv[optind];
    if (filnam == NULL) {
	Usage ();
    }
    filelist = &argv[optind+1];
    if (sendsw () == FALSE) {
	(VOID) printf ("Send failed.\n");
    } else {
	(VOID) printf ("OK\n");
    }
    DBUG_VOID_RETURN;
}


/*
 *	s e n d s w
 *
 *	Sendsw is the state table switcher for sending
 *	files.  It loops until either it finishes, or
 *	an error is encountered.  The routines called by
 *	sendsw are responsible for changing the state.
 */

sendsw ()
{
    DBUG_ENTER ("sendsw");
    state = 'S';
    numtry = 0;
    DBUG_2 ("states", "state S set");
    while (TRUE) {
	DBUG_3 ("states", "state %c found", state);
	switch (state) {
	    case 'D': 				/* Data-Send state */
		state = sdata ();
		break;
	    case 'F':				/* File-Send */
		state = sfile ();
		break;
	    case 'Z': 				/* End-of-File */
		state = seof ();
		break;
	    case 'S':				/* Send-Init */
		state = sinit ();
		break;
	    case 'B':				/* Break-Send */
		state = sbreak ();
		break;
	    case 'C':				/* Complete */
		DBUG_RETURN (TRUE);
	    case 'A': 				/* Abort */
		DBUG_RETURN (FALSE);
	    default: 				/* Unknown, abort */
		DBUG_RETURN (FALSE);
	}
    }
}

/*
 *	s i n i t
 */

static char sinit ()
{
    int num;
    int len;
    register char newstate;

    DBUG_ENTER ("sinit");
    DBUG_2 ("sinit", "begin init attempt");
    if (numtry++ > MAXTRY) {
	DBUG_3 ("tries", "%d tries", numtry - 1);
	newstate = 'A';
    } else {
	spar (packet);
	DBUG_3 ("n", "packet number %d", n);
	spack ('S', n, 6, packet);
	switch (rpack (&len, &num, recpkt)) {
	    case 'N':
		DBUG_2 ("nak", "got N packet");
		if (n != num--) {
		    newstate = state;
		    break;
		}
	    case 'Y':
		DBUG_2 ("ack", "got Y packet");
		if (n != num) {
		    newstate = state;
		    break;
		}
		rpar (recpkt);			/* Get the init info */
		if (eol == 0) {			/* check and set defaults */
		    eol = '\n';
		}
		if (quote == 0) {
		    quote = '#';
		}
		numtry = 0;			/* reset try counter */
		n = (n + 1) % 64;
		DBUG_3 ("files", "open file %s", filnam);
		fd = open (filnam, 0, 0777);
		if (fd < 0) {
		    newstate = 'A';
		} else {
		    if (host) {
			(VOID) printf ("Sending %s\n", filnam);
		    }
		    newstate = 'F';
		}
		break;
	    case FALSE: 
		newstate = state;
		break;
	    default: 
		newstate = 'A';
		break;
	}
    }
    DBUG_3 ("states", "new state %c", newstate);
    DBUG_RETURN (newstate);
}



/*
 *	s f i l e
 */

static char sfile ()
{
    int num;
    int len;
    register char newstate;

    DBUG_ENTER ("sfile");
    DBUG_2 ("sfile", "begin send file attempt");
    if (numtry++ > MAXTRY) {
	DBUG_3 ("tries", "%d tries", numtry - 1);
	newstate = 'A';
    } else {
	for (len = 0; filnam[len] != '\0'; len++);	/* count the length */
	len++;
	spack ('F', n, len, filnam);			/* send an 'F' pkt */
	switch (rpack (&len, &num, recpkt)) {
	    case 'N': 					/* NAK */
		DBUG_2 ("nak", "got N packet");
		if (n != num--) {
		    newstate = state;
		    break;
		}
	    case 'Y': 					/* ACK */
		DBUG_2 ("ack", "got Y packet");
		if (n != num) {				/* Wrong ACK, fail */
		    newstate = state;
		} else {
		    numtry = 0;
		    n = (n + 1) % 64;
		    size = bufill (packet);
		    newstate = 'D';
		}
		break;
	    case FALSE: 
		newstate = state;
		break;
	    default: 
		newstate = 'A';
		break;
	}
    }
    DBUG_3 ("states", "new state %c", newstate);
    DBUG_RETURN (newstate);
}


/*
 *	s d a t a 
 */

sdata ()
{
    int num;
    int len;
    register int newstate;

    DBUG_ENTER ("sdata");
    if (numtry++ > MAXTRY) {			/* If too many tries, abort */
	newstate = 'A';
    } else {
	spack ('D', n, size, packet);		/* send a 'D' packet */
	switch (rpack (&len, &num, recpkt)) {	/* What was the reply */
	    case 'N': 				/* NAK */
		DBUG_2 ("nak", "got N packet");
		if (n != num--) {
		    newstate = state;
		    break;
		}
	    case 'Y': 				/* ACK */
		DBUG_2 ("ack", "got Y packet");
		if (n != num) {			/* Wrong ACK, fail */
		    newstate = state;
		    break;
		}
		numtry = 0;			/* rest try counter */
		n = (n + 1) % 64;
		if ((size = bufill (packet)) == EOF) {
		    newstate = 'Z';
		} else {
		    newstate = 'D';
		}
		break;
	    case FALSE: 
		newstate = state;
		break;
	    default: 
		newstate = 'A';
		break;
	}
    }
    DBUG_3 ("states", "new state %c", newstate);
    DBUG_RETURN (newstate);
}


/*
 *	s e o f
 */

seof ()
{
    int num;
    int len;
    register int newstate;

    DBUG_ENTER ("seof");
    DBUG_2 ("eof", "begin seof");
    if (numtry++ > MAXTRY) {
	DBUG_3 ("tries", "%d tries", numtry - 1);
	newstate = 'A';
    } else {
	spack ('Z', n, 0, packet);		/* Send a 'Z' packet */
	DBUG_2 ("packets", "Z packet sent");
	switch (rpack (&len, &num, recpkt)) {	/* What was the reply? */
	    case 'N':		 		/* NAK */
		DBUG_2 ("nak", "got N packet");
		if (n != num--) {
		    newstate = state;
		    break;
		}
	    case 'Y': 				/* ACK */
		DBUG_2 ("ack", "got Y packet");
		if (n != num) {			/* if wrong ACK, fail */
		    newstate = state;
		    break;
		}
		numtry = 0;			/* reset try counter */
    /*		n = (n+1)%64; */
		DBUG_3 ("files", "closing file %s", filnam);
		(VOID) close (fd);		/* close the input file */
		DBUG_2 ("files", "getting next file");
		if (gnxtfl () == FALSE) {	/* No more files go? */
		    n = (n + 1) % 64;
		    newstate = 'B';
		} else {
		    DBUG_3 ("files", "new file is %s", filnam);
		    newstate = 'S';
		}
		break;
	    case FALSE: 
		newstate = state;
		break;
	    default: 
		newstate = 'A';
		break;
	}
    }
    DBUG_3 ("states", "new state %c", newstate);
    DBUG_RETURN (newstate);
}


/*
 *	s b r e a k
 */

sbreak ()
{
    int num;
    int len;
    register int newstate;

    DBUG_ENTER ("sbreak");
    DBUG_2 ("break", "begin break transmission");
    if (numtry++ > MAXTRY) {			/* If too many tries abort */
	DBUG_3 ("tries", "%d tries", numtry - 1);
	newstate = 'A';
    } else {
	spack ('B', n, 0, packet);		/* Send a B packet */
	switch (rpack (&len, &num, recpkt)) {	/* What was the reply? */
	    case 'N': 				/* NAK */
		DBUG_2 ("nak", "got N packet");
		if (n != num--) {
		    newstate = state;
		    break;
		}
	    case 'Y': 				/* ACK */
		DBUG_2 ("ack", "got Y packet");
		if (n != num) {
		    newstate = state;
		} else {
		    numtry = 0;			/* reset try counter */
		    n = (n + 1) % 64;		/* and bump packet count */
		    newstate = 'C';
		}
		break;
	    case FALSE: 
		newstate = state;
		break;
	    default: 
		newstate = 'A';
		break;
	}
    }
    DBUG_3 ("states", "new state %c", newstate);
    DBUG_RETURN (newstate);
}
