/* Create xrb - xmodem_request_block */

/* Xmodem, and associated routines for z-term */
/* Nik Conwell   4/20/87 */

#include "term.h"                /* Defines */
#include "term.common"

/* Send a character to the serial port */

void sendchar(ch,zt)
char ch;
struct Z_Term *zt;
{
  zt->rs_out = ch;
  DoIO(zt->write_request);
}





int readchar(ch,zt)
char *ch;
struct Z_Term *zt;

{

  set_timer(zt->timeout_time,zt);                   /* set up the timer */
dowait:  zt->wakeupmask = Wait(SERIAL_INPUT |
                     W1_INTUITION_MESSAGE |
		     TIMER_MESSAGE);
  if (zt->wakeupmask & SERIAL_INPUT)
   {
    WaitIO(zt->read_request);
    *ch = zt->rs_in;
    BeginIO(zt->read_request);
   }  /* end if */
  if (zt->wakeupmask & W1_INTUITION_MESSAGE)
    {
     zt->message = (struct IntuiMessage *) GetMsg(zt->w1->UserPort);
     zt->class = zt->message->Class;
     zt->code = zt->message->Code;
     ReplyMsg(zt->message);
     if ((zt->class == MENUPICK) && (zt->code != MENUNULL))
        {
         zt->menunum = MENUNUM(zt->code);
         zt->menuitem = ITEMNUM(zt->code);
         if ((zt->menunum == MENU_FILESTUFF) && 
             (zt->menuitem == MENU_FILESTUFF_ABORT_XMODEM))
          {
           con_put_str("\nUser Cancelled Transfer.\n",zt);
           return(USER_ABORT);
          }
        }  /* end if the user is playing with the menu */
     goto dowait;
    }  /* end if we woke up because of intuition */
  if (zt->wakeupmask & TIMER_MESSAGE)
    {
     con_put_str("\nTimeout waiting for character.\n",zt);
     return(TIMEOUT);
    }
  return(NULL);
}






int xmodem_read_file(zt)
struct Z_Term *zt;

{
  int sectnum, errors, errorflag;
  unsigned int checksum, i, j, bufptr;
  unsigned char sectcurr, sectcomp, sentchecksum1, sentchecksum2;
  unsigned char ourchecksum1, ourchecksum2, firstchar;
  int xfer_current_blocksize, xfer_current_error_detect_alg;
  int fd;
  char numb[10];
  char bufr[BUFSIZE];
  int temp;

  firstchar = NULL;
  sectcurr = NULL;
  sectcomp = NULL;
  sentchecksum1 = NULL;
  sentchecksum2 = NULL;
  xfer_current_blocksize = zt->xfer_blocksize;   /* copy blocksize */
  xfer_current_error_detect_alg = zt->xfer_error_detect_alg;   /* copy spec alg  */
  if ((fd = creat(zt->filename, 0)) < 0)
   {
    con_put_str("\nCannot Open ",zt);
    con_put_str(zt->filename,zt);
    con_put_str(" .  File exists.\n",zt);
    goto xmodem_read_file_exit;
   }
  else
   {
    con_put_str("\nReceiving ",zt);
    con_put_str(zt->filename,zt);
    con_put_str(" using ",zt);
    if (zt->xfer_blocksize == XMODEM_128_BLOCK)
     {
      con_put_str("128",zt);
     }
    if (zt->xfer_blocksize == XMODEM_1024_BLOCK)
     {
      con_put_str("1024",zt);
     }
    con_put_str(" byte blocks, with ",zt);
    if (zt->xfer_error_detect_alg == XMODEM_CHECKSUM)
     {
      con_put_str("checksum",zt);
     }
    if (zt->xfer_error_detect_alg == XMODEM_CRC16)
     {
      con_put_str("crc-16",zt);
     }
    con_put_str(" error detection algorithm.\n",zt);
   }
  zt->timeout_time = 3;
  sectnum = errors = bufptr = 0;
  do			/* Loop until SOH or STX is received */
   {
    if (xfer_current_error_detect_alg == XMODEM_CHECKSUM)
     {
      sendchar((char) NAK,zt);		/* NAK specifies checksum recv */
     }
    if (xfer_current_error_detect_alg == XMODEM_CRC16)
     {
      sendchar((char) CCHAR,zt);		/* C specifies crc16 recv */
     }

   zt->st = readchar(&firstchar,zt);

   if (zt->st == USER_ABORT)
     {
      goto xmodem_read_file_exit;
     }
   if ((zt->st == TIMEOUT) && (xfer_current_error_detect_alg == XMODEM_CHECKSUM)) 
     {
      con_put_str("\nSender not responding to initial NAK.\n",zt);
      goto xmodem_read_file_exit;			/* if checksum, getout */
     }
   if ((zt->st == TIMEOUT) && (xfer_current_error_detect_alg == XMODEM_CRC16))
     {
      xfer_current_error_detect_alg = XMODEM_CHECKSUM;
      con_put_str("\nSender not responding to CRC option.\n",zt);
      con_put_str("\nXferring using checksum algorithm.\n",zt);
     }
   if ((firstchar != SOH) && (firstchar != STX))  /* if !SOH, tell what it is */
     {
      con_put_str("\nSender not sending SOH or STX.\n",zt);
      temp = (int) firstchar;
      stci_d(numb,temp,10);
      con_put_str(numb,zt);
      con_put_str(" ascii received instead.\n",zt);
     }
   }
  while ((firstchar != SOH) && (firstchar != STX));
  zt->timeout_time = 10;			/* 10 second timeout period */
  while ((firstchar != EOT) && (errors < ERRORMAX))
   {
    errorflag = FALSE;
    if ((firstchar == SOH) || (firstchar == STX))
     {
      if ((firstchar == SOH) && (xfer_current_blocksize == XMODEM_1024_BLOCK))
       {
        xfer_current_blocksize = XMODEM_128_BLOCK;
	con_put_str("\nSender specified 128 byte block.\n",zt);
	con_put_str("Temporarily switched block size.\n",zt);
       }
      if ((firstchar == STX) && (xfer_current_blocksize == XMODEM_128_BLOCK))
       {
        xfer_current_blocksize = XMODEM_1024_BLOCK;
	con_put_str("\nSender specified 1024 byte block.\n",zt);
	con_put_str("Temporarily switched block size.\n",zt);
       }
      con_put_str("Receiving Block ",zt);
      stci_d(numb,sectnum,10);
      con_put_str(numb,zt);
      con_put_str("...",zt);

      zt->st = readchar(&sectcurr,zt);
      if (zt->st == USER_ABORT)
        {
	 goto xmodem_read_file_exit;
	}
      if (zt->st == TIMEOUT)
       {
	con_put_str("\nTimeout occured while waiting for sectcurr.\n",zt);
	goto xmodem_read_file_exit;
       }

      zt->st = readchar(&sectcomp,zt);
      if (zt->st == USER_ABORT)
        {
	 goto xmodem_read_file_exit;
	}
      if (zt->st == TIMEOUT)
       {
	con_put_str("\nTimeout occured while waiting for sector complement.\n",zt);
	goto xmodem_read_file_exit;
       }
      if ((sectcurr + sectcomp) == 255)		/* must be compliment */
       {
        if (sectcurr == (sectnum + 1 & 0xff))	/* must be same sector */
         {
          checksum = 0;
          for (j = bufptr; j < (bufptr + xfer_current_blocksize); j++)
           {
            zt->st = readchar((char *) &bufr[j],zt);
            if (zt->st == USER_ABORT)
              {
               goto xmodem_read_file_exit;
   	      }
            if (zt->st == TIMEOUT)
	      {
	       con_put_str("\nTimeout occurred waiting for data in block.\n",zt);
	       goto xmodem_read_file_exit;
	      }
            if (xfer_current_error_detect_alg == XMODEM_CHECKSUM)  /* calc checksum */
	      {
	       checksum = (checksum + bufr[j]) & 0xff;
	      }
	    if (xfer_current_error_detect_alg == XMODEM_CRC16)
	      {
	       checksum = checksum ^ (int) bufr[j] << 8;	/* calc crc16 */
	       for (i = 0;i < 8; ++i)
	        {
		 if (checksum & 0x8000)
		  {
		   checksum = checksum << 1 ^ 0x1021;
		  }
		 else
		  {
		   checksum = checksum << 1;
		  }
		}  /* end for */
	       checksum = checksum & 0xffff;
	      }  /* end if error detect alg == XMODEM_CRC16 */
           }  /* end for */
	  if (zt->xfer_error_detect_alg == XMODEM_CHECKSUM)
	   {
	    zt->st = readchar(&sentchecksum1,zt);
            if (zt->st == USER_ABORT)
              {
	       goto xmodem_read_file_exit;
	      }
	    ourchecksum1 = checksum;
	   }
	  if (xfer_current_error_detect_alg == XMODEM_CRC16)
	   {
	    zt->st = readchar(&sentchecksum1,zt);
            if (zt->st == USER_ABORT)
              {
	       goto xmodem_read_file_exit;
	      }

	    zt->st = readchar(&sentchecksum2,zt);
            if (zt->st == USER_ABORT)
              {
	       goto xmodem_read_file_exit;
	      }
	    ourchecksum1 = checksum >> 8;
	    ourchecksum2 = checksum;
	   }
          if (((xfer_current_error_detect_alg == XMODEM_CHECKSUM) &&
	      (ourchecksum1 == sentchecksum1)) ||
	      ((xfer_current_error_detect_alg == XMODEM_CRC16) &&
	       (ourchecksum1 == sentchecksum1) &&
	       (ourchecksum2 == sentchecksum2)))
           {
            errors = 0;
            sectnum++;
            bufptr += xfer_current_blocksize;
            con_put_str("ok\n",zt);
            if (bufptr == BUFSIZE)
             {
              bufptr = 0;
              if (write(fd, bufr, BUFSIZE) == EOF)
               {
                con_put_str("\nError Writing File\n",zt);
                goto xmodem_read_file_exit;
               }  /* end if */
             }  /* end if */
            sendchar((char) ACK,zt);
           }  /* end if */
          else		/* checksum not what we computed!!! */
           {
            errorflag = TRUE;
            if (zt->st == TIMEOUT) 
	     {
	      goto xmodem_read_file_exit;
	     }
	    con_put_str("\nCalculated checksum != received checksum.\n",zt);
	    con_put_str("\nCalculated checksum1 = ",zt);
	    stci_d(numb,(int) ourchecksum1,10);
	    con_put_str(numb,zt);
	    con_put_str(".  Received checksum1 = ",zt);
	    stci_d(numb,(int) sentchecksum1,10);
	    con_put_str(numb,zt);
	    con_put_str(".\n",zt);
	    if (xfer_current_error_detect_alg == XMODEM_CRC16)
	     {
	      con_put_str("\nCalculated checksum2 = ",zt);
	      stci_d(numb,(int) ourchecksum2,10);
	      con_put_str(numb,zt);
	      con_put_str(". Received checksum2 = ",zt);
	      stci_d(numb,(int) sentchecksum2,10);
	      con_put_str(numb,zt);
	      con_put_str(".\n",zt);
	     }  /* end if */
           }  /* end else */
         }  /* end if sectcurr == (sectnum + 1 & 0xff) */
        else
         {
          if (sectcurr == (sectnum & 0xff))
           {
            con_put_str("\nReceived Duplicate Sector\n",zt);
            sendchar((char) ACK,zt);
           }  /* end if sectcurr == (sectnum & 0xff) */
          else 
	   {			/* didn't receive expected sector number */
	    errorflag = TRUE;
	    con_put_str("\nExpecting to receive sector ",zt);
	    stci_d(numb,(int) sectcurr,10);
	    con_put_str(numb,zt);
	    con_put_str(".\nBut received sector ",zt);
	    stci_d(numb,(int) sectcurr,10);
	    con_put_str(numb,zt);
	    con_put_str(" instead.\n",zt);
	   }
         }  /* end else */
       }  /* end if sectcurr + sectcomp == 255 */
       else
        {
         con_put_str("\nSector number, and compliment are not that.\n",zt);
	 con_put_str("\nSector number sent = ",zt);
	 stci_d(numb,(int) sectcurr,10);
	 con_put_str(numb,zt);
	 con_put_str(" ascii.\n",zt);
	 con_put_str("\nSector complement sent = ",zt);
	 stci_d(numb,(int) sectcomp,10);
	 con_put_str(numb,zt);
	 con_put_str(" ascii.\n",zt);
         errorflag=TRUE;
        }  /* end else */
     }  /* end if firstchar == SOH || STX */
    else
     {
      con_put_str("\nOOPS!!  We didn't receive SOH or STX!!\n",zt);
     }
    if (errorflag == TRUE)
     {
      errors++;
      con_put_str("\nError\n",zt);
      sendchar((char) NAK,zt);
     }  /* end if errorflag == true */
				/* Read the SOH ^ STX */
   zt->st = readchar(&firstchar,zt);
   if (zt->st == USER_ABORT)
     {
      goto xmodem_read_file_exit;
     }

   }  /* end while */

  write(fd,bufr,bufptr);
  close(fd);

  if ((firstchar == EOT) && (errors < ERRORMAX)) 
   {
    sendchar((char) ACK,zt);
    return(TRUE);
   }
  goto xmodem_read_file_exit_do;
xmodem_read_file_exit:
  write(fd,bufr,bufptr);
  close(fd);
xmodem_read_file_exit_do:
  return(FALSE);
}





int xmodem_send_file(zt)

struct Z_Term *zt;

{
  int sectnum, bytes_to_send, size, attempts, j;
  unsigned char sectsend, c;
  unsigned int checksum, bufptr;
  struct FileLock *lock1, *Lock();
  struct FileInfoBlock *infoblock1 = 0;  
  LONG filesize;
  int xfer_current_error_detect_alg;
  int i;
  unsigned char outchecksum1;
  int sync_lost_count;
  int xfer_total_blocks;
  int timeout_time, fd;
  char bufr[BUFSIZE];
  char numb[10];
  int temp;

  xfer_current_error_detect_alg = NULL; 	/* nullify alg */
  timeout_time = 10;				/* 10 sec timeout period */
  if ((lock1 = Lock(zt->filename,ACCESS_READ)) != NULL)	/* get shared lock */
   {
    infoblock1 = (struct FileInfoBlock *)
            AllocMem(sizeof(*infoblock1),MEMF_PUBLIC|MEMF_CLEAR);
    Examine(lock1,infoblock1);
    filesize = infoblock1->fib_Size;
    UnLock(lock1);
    FreeMem(infoblock1,sizeof(*infoblock1));
    con_put_str("\nSending ",zt);
    con_put_str(zt->filename,zt);
    con_put_str(".\n",zt);
    con_put_str("File is ",zt);
    stci_d(numb,(int) filesize,10);
    con_put_str(numb,zt);
    con_put_str(" bytes long.\n",zt);
    con_put_str("Current blocksize is ",zt);
    stci_d(numb,zt->xfer_blocksize,10);
    con_put_str(numb,zt);
    con_put_str(" bytes.\nCurrent error detection algorithm is ",zt);
    if (zt->xfer_error_detect_alg == XMODEM_CHECKSUM)
      {
       con_put_str("checksum",zt);
      }
    if (zt->xfer_error_detect_alg == XMODEM_CRC16)
      {
       con_put_str("crc-16",zt);
      }
    xfer_total_blocks = (filesize / zt->xfer_blocksize);
    xfer_total_blocks += 1;
    con_put_str(".\nTotal blocks to send is ",zt);
    stci_d(numb,xfer_total_blocks,10);
    con_put_str(numb,zt);
    con_put_str(".",zt);
    if ((fd = open(zt->filename, 1)) == NULL)
     {
      con_put_str("\nError opening send file.\n",zt);
      goto xmodem_send_file_exit;
     }
    con_put_str("\nSending File\n",zt);
   }  /* end if lock1 != NULL */
  else
   {
    con_put_str("\nCould not open ",zt);
    con_put_str(zt->filename,zt);
    con_put_str(" for input.\n",zt);
    goto xmodem_send_file_exit;
   }
 
  attempts = 0;
  sectnum = 1;
  sectsend = 1;

  /* wait for sync char */

  sync_lost_count = 1;



  do
    {
     zt->st = readchar(&c,zt);
    if (zt->st == USER_ABORT)
       {
        goto xmodem_send_file_exit;
       }

    if ((c != NAK) && (c != CCHAR))
     {
      con_put_str("Not receiving C or NAK.\nAscii ",zt);
      temp = (int) c;
      stci_d(numb,temp,10);
      con_put_str(numb,zt);
      con_put_str(" received instead.\n",zt);
     }
    }
  while (((c != NAK) && (c != CCHAR)) && (sync_lost_count++ < ERRORMAX));

  if (sync_lost_count >= (ERRORMAX))
   {
    con_put_str("\nCouldn't sync up.\n",zt);
    con_put_str("\nReceiver not sending NAKs or C's.\n",zt);
    goto xmodem_send_file_exit;
   }

  if (c == NAK)
   {
    xfer_current_error_detect_alg = XMODEM_CHECKSUM;
   }
  if (c == CCHAR)
   {
    xfer_current_error_detect_alg = XMODEM_CRC16;
   }
  if (xfer_current_error_detect_alg == NULL)
   {
    con_put_str("\nERROR!!  Receiver specified unknown error detect alg.\n",zt);
    temp = (int) c;
    stci_d(numb,temp,10);
    con_put_str("\nAscii ",zt);
    con_put_str(numb,zt);
    con_put_str(" was received.\n",zt);
    goto xmodem_send_file_exit;
   }
  if (xfer_current_error_detect_alg != zt->xfer_error_detect_alg)
   {
    con_put_str("\nReceiver specified error detection algorithm to be ",zt);
    if (xfer_current_error_detect_alg == XMODEM_CHECKSUM)
     {
      con_put_str("checksum",zt);
     }
    if (xfer_current_error_detect_alg == XMODEM_CRC16)
     {
      con_put_str("crc-16",zt);
     }
    con_put_str(" instead of ",zt);
    if (zt->xfer_error_detect_alg == XMODEM_CHECKSUM)
     {
      con_put_str("checksum.\n",zt);
     }
    if (zt->xfer_error_detect_alg == XMODEM_CRC16)
     {
      con_put_str("crc-16.\n",zt);
     }
   }  /* end if current_error_detect_alg != error_detect_alg */

  while ((bytes_to_send = read(fd, bufr, BUFSIZE)) && (attempts != RETRYMAX))
   {
    if (bytes_to_send == EOF)
     {
      con_put_str("\nError Reading File\n",zt);
      goto xmodem_send_file_exit;
     }

    bufptr = 0;
    while ((bytes_to_send > 0) && (attempts != RETRYMAX))
     {
      attempts = 0;
      do
       {
	if (zt->xfer_blocksize == XMODEM_128_BLOCK)
	 {
	  sendchar((char) SOH,zt);
	 }
	if (zt->xfer_blocksize == XMODEM_1024_BLOCK)
	 {
	  sendchar((char) STX,zt);
	 }
	sendchar((char) sectsend,zt);
	sendchar((char) ~sectsend,zt);

        checksum = 0;
        size = zt->xfer_blocksize 
               <= bytes_to_send ? zt->xfer_blocksize : bytes_to_send;
        bytes_to_send -= size;
        for (j = bufptr; j < (bufptr + zt->xfer_blocksize); j++)
         {
          if (j < (bufptr + size))
           {
            sendchar((char) bufr[j],zt);
	    if (xfer_current_error_detect_alg == XMODEM_CHECKSUM)
	     {
	      checksum += bufr[j];
	     }
	    if (xfer_current_error_detect_alg == XMODEM_CRC16)
	     {
	      checksum = checksum ^ (int) bufr[j] << 8;
	      for (i = 0;i < 8; ++i)
	       {
	        if (checksum & 0x8000)
		 {
		  checksum = checksum << 1 ^ 0x1021;
		 }
		else
		 {
		  checksum = checksum << 1;
		 }
	       }  /* end for */
	      checksum = checksum & 0xffff;
	     }  /* end if */
           }
          else 
           {
           sendchar((char) NULL,zt);
           }
         }  /* end for */
	if (xfer_current_error_detect_alg == XMODEM_CHECKSUM)
	 {
          sendchar((char) checksum & 0xff,zt);
	 }
        if (xfer_current_error_detect_alg == XMODEM_CRC16)
	 {
	  outchecksum1 = (checksum >> 8);
	  sen                                                                                                                                                                                                                                                                                                                                                                                     