/* Comm WXMODEM receive file routines */
#define  WXMDMRECV 1
#include "globals.h"
#include <fcntl.h>

extern  int WXch(), timedch();
extern  void  send_proto(), emits_rx();
extern  int   readchar();

static  short   eotflg, NAKsent, dirty;
static  int     index, errsect, naks;

WXmodem_rec(file)
UBYTE *file;
{
   unsigned crchi, crclo, sect, comp;
   int ch, secerrs;
   unsigned count;

   void chop_file();
   ULONG  bytes;

   secerrs = errsect = naks = index = bytes = 0;
   sector = 1;
   cancel = eotflg = abort = dirty = FALSE;
   
   while(secerrs < RETRYMAX)
   {
      if(abort)                            /* keyboard abort (ESC) */
      {
         abortxfer();
         return FALSE;
      }

      if(cancel)                           /* remote sent CAN */
      {
         send_proto(ACK);
         chop_file(index); close( fd );
         if(( ch = timedch(5)) == CAN)
            send_proto(ACK);
         return TRUE;
      }
/* start of block */
      do {                                 /* look for SYN/CAN/EOT */
         ch = timedch(10);
         if( ch == ERROR )
         {
            secerrs++;
            send_proto(NAK); send_proto(errsect & 3);
         }
         if( (secerrs > RETRYMAX) || abort || cancel)
         {
            abortxfer();
            return FALSE;
         }
         if(ch == EOT)
         {
             send_proto(NAK);
             ch = timedch(5);
             if(ch == EOT || ch == ERROR)
             {
                send_proto(ACK);
                chop_file(index);  close( fd );
                return TRUE;
             }
         }
      } while (ch != SYN);

/* got one SYN character -- gobble up the others */
      while((ch = timedch(10)) == SYN)  ;

/* we got a non SYN character
   if SOH, then we have a block, if not...who knows, so start over again
*/
      if(ch != SOH)  continue;

/* block number and compliment */
      sect = WXch(10);  emit_rx_protocol((UBYTE)sect);
      comp = WXch(10);  emit_rx_protocol((UBYTE)comp);

      if( sect == ERROR || comp == ERROR)
      {
         Nak();
         continue;
      }

/* start of data
      clear out the last block and start a new one
*/
      if(dirty)
      {
         movmem(xbuffer,&diskbuff[index++ * SECSIZ],SECSIZ);
         dirty = FALSE;
      }

      crc = count = 0;
      while( count < SECSIZ )
      {
         ch = WXch(10);
         if( ch == ERROR ) break;
         do_crc(ch);
         xbuffer[ count++ ] = ch;
         if(viewflg)
            emit_vw(ch);
      }

      if( ch == ERROR )
      {
         Nak();
         continue;
      }

/* first CRC byte */
      if((crchi = WXch(10)) == ERROR)
      {
         Nak();
         continue;
      }
      do_crc(crchi);

/* second CRC byte */
      if((crclo = WXch(10)) == ERROR)
      {
         Nak();
         continue;
      }

/* see if checksum is valid */
      if(!verify_checksum( crclo ))
      {
        emits_rx("  Checksum error\n");
        Nak();
        secerrs++;
        status(file,bytes,naks,errsect);
        continue;
      }

/* does the block number match the compliment? */
      if((UBYTE)sect != (UBYTE)~comp)
      {
         Nak(); secerrs++;
         continue;      /* no: this isn't a real block! */
      }

/* if this isn't the sector we're waiting for, try again */
      if( (UBYTE)sect != (UBYTE)sector )
      {
         Nak();
         continue;
      }

/* otherwise, this must be a good sector and it must have good data,
     so ACK it
*/
    Ack(sect);
    NAKsent = FALSE;
    secerrs = 0;
    sprintf(sbuff,"\rReceived block %d",sector);
    emits_rx(sbuff);

/* accept the sector */
    sector++;  dirty = TRUE;  bytes += SECSIZ;
    status(file,bytes,naks,errsect);

    if(index == numbufs)
    {
      sendchar(XOFF);
      if(write(fd,diskbuff,numbufs * SECSIZ) != numbufs * SECSIZ)
      {
         emits_rx("\nERROR writing to file\n");
         abortxfer();
         return FALSE;
      }
      index = 0;
      sendchar(XON);
    }
  }
  abortxfer();
  return FALSE;
}

/* send ACK */
Ack(sect)
int sect;
{
   send_proto(ACK);
   send_proto(sect & 3);
}

/* Send NAK unless we've already done so */
Nak()
{
   if(!NAKsent)
   {
      send_proto(NAK);
      send_proto(sector & 3);
      errsect = sector;
      NAKsent = TRUE;
   }
   naks++;
}

timedch(time)
int time;
{
   int ch;

   Process_window_event();
   if(abort) return ERROR;

   ch = readchar(time,0);
   if(ch == TIMEOUT)  return ERROR;

   emit_rx_protocol(ch);
   return ch;
}

WXch(time)
int time;
{
   int gotdle,ch;

   gotdle = 0;

   Process_window_event();
   if(abort) return ERROR;

   while(TRUE)
   {
      ch = readchar(time,0);
      if(ch == TIMEOUT)  return ERROR;
      switch (ch)
      {
         case DLE:
                    gotdle = 64;
                    continue;
         case SYN:
                    return ERROR;
         default:
                    return (ch ^ gotdle);
      }
   }
}

abortxfer()
{
   int ch;

   sendchar(XOFF);
   chop_file(index);
   sendchar(XON);
   send_proto(CAN);   send_proto(CAN);   send_proto(CAN);

   if(waitcan() == TRUE)
      return TRUE;
   return FALSE;
}

waitcan()
{
   int ch,err;

   err = 0;
   emits_rx("\nAborting...Wait...\n");
   while (readchar(2,0) != TIMEOUT)
      ;

   send_proto(CAN); send_proto(CAN);
   ch = readchar(5,0);   ch = readchar(5,0);

   do {
     if(err++ == 3) break;
     send_proto(ACK);
     emits_rx("Sending abort...\n");
     Delay(50L);
     send_proto(CAN); send_proto(CAN);
     ch = readchar(3,0);
   }  while( ch != CAN );

   send_proto(ACK);
   return TRUE;
}

sendWs()
{
   int ch,err;
   err = 0;
   send_proto('W');
   do {
      ch = timedch(3);
      if(ch == SYN)
         return TRUE;
      if(ch == ERROR)
         send_proto('W');
   } while ( err++ < 3 );
   return FALSE;
}


