#include "ufk.h"

int offset,                      /* Offset in data buffer to start */
    org_size;                    /* Original packet size */
char size_lowered;               /* Flag if packet shortening has been done */

sendfile()
{
   if (numprm <= 1)                    /* check parameter */
      prterr(ER_FSPCREQ);
   else if (!get_file_spec(TRUE))      /* Get filespec for send */
      prterr(ER_NOFILSEL);
   else
   {
      if (!open_port(TRUE,FALSE))      /* Setup communication port */
         prterr(ER_POPNERR);
      else
      {
         if (alloc_pkt(SEND))          /* Allocate packet memory */
         {
            sflg++;
            set_frame();
            if (!sendsw())                /* Send the file(s) */
               disp(0,18,"Send failed.\n"); /* Report failure */
            beep();
            while (get_file_spec(FALSE)); /* Flush directory buffer */
         }
         close_port(sflg,FALSE);
      }
   }
}

/*
 *  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()
{
   char sinit(), sfile(), sattr(), sdata(), seof(), sbreak();

   init_xfer();                           /* Reset counters */
   set_default_comm();                    /* Set communication parameters */
   state = 'S';                           /* Send initiate is start state */
   n = 0;                                 /* Initialize message number */
   offset = 0;                            /* Offset in data buffer to start */
   size_lowered = FALSE;                  /* No long packet error received */
   numtry = 0;                            /* Say no tries yet */
   if (remote && (sflg != 2))             /* Wait before sending if remote */
      kdelay(send_delay);                 /* and if no server mode request */
   purgeline(ttyfd);                      /* Eat old input */
   while(TRUE)                            /* Do this as long as necessary */
   {
      if (debug)
      {
         prtdbgf("Send state: ");
         if (!screen)
            fputs("Send state: ",stdout);
         disp_state(50,13,state);
      }
      switch(state)
      {
         case 'S':                        /* Send-init */
               state = sinit();
               break;

         case 'F':                        /* Send-file */
               state = sfile();
               break;

         case 'A':                        /* Send attributes */
               state = sattr();
               break;

         case 'D':                        /* Send-data */
               state = sdata();
               break;

         case 'Z':                        /* Send-End-of-File */
               state = seof();
               break;

         case 'B':                        /* Send-Break */
               state = sbreak();
               break;

         case 'C':                        /* Complete */
               fin_xfer();
               return (TRUE);

         case 'Q':
         default:                         /* Unknown or abort, fail */
               fin_xfer();
               return (FALSE);
      }
   }
}


/*
 *  s i n i t
 *
 *  Send Initiate: send this host's parameters and get other side's back.
 */

char sinit()
{
   char  num;                             /* Packet number */
   int len;                               /* length */

   if (numtry++ >= maxtry)                /* If too many tries, give up */
      return('Q');
   spar(sndpkt,&len);                     /* Fill up init info packet */

   spack('S',n,len,sndpkt,0,1);           /* Send an S packet */
   switch(rpack(&len,&num,recpkt,1))      /* What was the reply? */
   {
      case 'N':                           /* NAK, try it again */
            return(state);

      case 'Y':                           /* ACK */
            if (n != num)                 /* If wrong ACK, stay in S state */
               return(state);             /* and try again */
            rpar(recpkt,len);             /* Get other side's init info */
            numtry = 0;                   /* Reset try counter */
            n = ( n + 1) % 64;            /* Bump packet count */
            return('F');                  /* OK, switch state to F */

      case 'E':                           /* Error packet received */
            prerrpkt(recpkt);             /* Print it out and */
            return('Q');                  /* Abort */

      case 'T':                           /* Timeout */
            if (aborted)
               return('Q');               /* aborted by user */
      case FALSE:                         /* Receive failure, try again */
            return(state);

      default:                            /* Anything else, just "abort" */
            return('Q');
   }
}


/*
 *  s f i l e
 *
 *  Send File Header.
 */

char sfile()
{
   char  num,                             /* Packet number */
         fnm[15];
   int   i, len;                          /* length */

   if (numtry++ >= maxtry)                /* If too many tries, give up */
      return('Q');

   aborted = FALSE;
   if (fp != ERROR)                       /* If left open, close it */
      fclose(fp);
   fp = fopen(filnam,"r");                /* open the file to be sent */
   if (fp == NULL)                        /* If bad file pointer, give up */
   {
      error(PER_OPEN,"%s",filnam);        /* Can't open file */
      if (!get_file_spec(FALSE))
         return('C');                     /* No more files to do */
      else
         return('S');                     /* Try next file */
   }
   for (i = strlen(filnam) - 1; i >= 0; --i)
      if (filnam[i] == '/')               /* Skip directory specification */
         break;
   strcpy(fnm,&filnam[i+1]);
   map_case(fnm,OUT);                     /* map to correct case */
   if (!nooutput)
   {
      if (screen)
      {
         disp(0,2,"Sending: ");
         posit(20,2);
         printf("%s as %s", filnam, fnm);
         clreol(-1,-1);
      }
      else
         printf("\n\lSending: %s as %s\n\l",filnam,fnm);
   }
   fnm[strlen(fnm)] = EOF;                /* Terminate data */
   len = bufill(sndpkt,TRUE,fnm);
   spack('F',n,len,sndpkt,0,block_check_type); /* Send an F packet */
   switch(rpack(&len,&num,recpkt,block_check_type))/* What was the reply? */
   {
      case 'N':                           /* NAK, just stay in this state, */
            num = (--num < 0 ? 63 : num); /* unless it's NAK for next packet */
            if (n != num)                 /* which is just like an ACK for */
               return(state);             /* this packet so fall thru to... */

      case 'Y':                           /* ACK */
            if (n != num)                 /* If wrong ACK, stay in F state */
               return(state);
            numtry = 0;                   /* Reset try counter */
            n = (n + 1) % 64;             /* Bump packet count */
            if (attribute)
               return('A');               /* Switch to attribute state */
            else
            {
               if ((size = bufill(sndpkt,TRUE,0)) == EOF)/* data from file */
                  return('Z');            /* If EOF then empty data file */
               org_size = size;           /* Remember original size */
                  return('D');            /* Switch state to D */
            }

      case 'E':                           /* Error packet received */
            prerrpkt(recpkt);             /* Print it out and */
            return('Q');                  /* abort */

      case 'T':                           /* Timeout */
            if (aborted)
            {
               error(PER_ABORTED);        /* send error packet */
               return('Q');               /* aborted by user */
            }
      case FALSE:                         /* Receive failure, stay in F */
            return(state);

      default:                            /* Something else, just "abort" */
            return('Q');
   }
}

/*
 *  s a t t r
 *
 *  Send File Attributes
 */

char sattr()
{
   char  num;                             /* Packet number */
   int len;                               /* length */

   if (numtry++ >= maxtry)                /* If too many tries "abort" */
      return('Q');

   snd_attributes();                      /* Prepare send attribute packet */
   spack('A',n,size,sndpkt,0,block_check_type);/* Send an A packet */
   switch (rpack(&len,&num,recpkt,block_check_type))/* What was the reply? */
   {
      case 'N':                           /* NAK, just stay in this state, */
            num = (--num < 0 ? 63 : num); /* unless NAK for previous packet, */
            if (n != num)                 /* which is just like an ACK for */
               return(state);             /* this packet so fall thru to... */

      case 'Y':                           /* ACK */
            if (n != num)                 /* If wrong ACK, fail */
               return(state);
            numtry = 0;                   /* Reset try counter */
            n = (n + 1) % 64;             /* and bump packet count */
            if ((size = bufill(sndpkt,TRUE,0)) == EOF)/* Get data from file */
               return('Z');               /* If EOF then empty data file */
            org_size = size;              /* Remember original size */
            return('D');                  /* Switch state to data */

      case 'E':                           /* Error packet received */
            prerrpkt(recpkt);             /* Print it out and */
            return('Q');                  /* abort */

      case 'T':                           /* Timeout */
            if (aborted)
            {
               error(PER_ABORTED);        /* send error packet */
               return('Q');               /* aborted by user */
            }
      case FALSE:                         /* Receive failure, stay in B */
            return(state);

      default:                            /* Other, "abort" */
            return ('Q');
   }
}


/*
 *  s d a t a
 *
 *  Send File Data
 */

char sdata()
{
   char  num,                             /* Packet number */
         tmpc;
   int   len;                             /* length */

   if (numtry++ >= maxtry)                /* If too many tries, give up */
      return('Q');
   spack('D',n,size,sndpkt,offset,block_check_type);/* Send a D packet */
   switch(rpack(&len,&num,recpkt,block_check_type))/* What was the reply? */
   {
      case 'N':                           /* NAK, just stay in this state, */
            num = (--num < 0 ? 63 : num); /* unless it's NAK for next packet */
            if (n != num)                 /* which is just like an ACK for */
            {                             /* this packet so fall thru to 'Y' */
               if ((org_size > 91) && auto_recover)/* If extended length... */
                  lower_size();           /* Lower packet size on error */
               return(state);             /* Try again */
            }

      case 'Y':                           /* ACK */
            if (n != num)                 /* If wrong ACK, fail */
               return(state);
            numtry = 0;                   /* Reset try counter */
            n = (n + 1) % 64;             /* Bump packet count */
            if (size_lowered)
               adjust_size();             /* Still data in buffer */
            else
            {
               offset = 0;                /* No more old data in buffer */
               if ((size = bufill(sndpkt,FALSE,0)) == EOF)/* Get new data */
                  return('Z');            /* If EOF set state to that */
               org_size = size;           /* Remember original size */
            }
            tmpc = *recpkt;
            if (len == 1)                 /* Data on ack */
            {
               if (tmpc == 'Z')           /* Stop entire batch */
                  filecount = 0;          /* No more files to send */
               if ((tmpc == 'X') || (tmpc == 'Z'))
                  aborted = tmpc - 64;    /* Make binary */
            }
            if ((aborted == ABORTX) || (aborted == ABORTZ))
            {
               if (screen)
                  disp(0,19,"Transfer interrupted.");
               else
                  fputs("Transfer interrupted.\n\l",stdout);
               beep();
               return('Z');               /* Send abort */
            }
            return('D');                  /* Got data, stay in state D */

      case 'E':                           /* Error packet received */
            prerrpkt(recpkt);             /* Print it out and */
            return('Q');                  /* abort */

      case 'T':                           /* Timeout */
            if (aborted)
            {
               error(PER_ABORTED);        /* send error packet */
               return('Q');               /* aborted by user */
            }
            if ((org_size > 91) && auto_recover) /* If extended length... */
               lower_size();              /* Lower packet size on error */

      case FALSE:                         /* Receive failure, stay in D */
            return(state);

      default:                            /* Anything else, "abort" */
            return('Q');
   }
}


/*
 *  s e o f
 *
 *  Send End-Of-File.
 */

char seof()
{
   char  num;                             /* Packet number */
   int len, tlen;                         /* length */

   if (numtry++ >= maxtry)                /* If too many tries, "abort" */
      return('Q');

   tlen = 0;                              /* Assume no abort */
   if ((aborted == ABORTX) || (aborted == ABORTZ))
   {
      *sndpkt = 'D';                      /* Discard file on abort */
      tlen = 1;
      *(sndpkt+sizeof(char)) = '\0';      /* For debug printout */
   }
   spack('Z',n,tlen,sndpkt,0,block_check_type); /* Send a 'Z' packet */
   switch(rpack(&len,&num,recpkt,block_check_type))/* What was the reply? */
   {
      case 'N':                           /* NAK, just stay in this state, */
            num = (--num < 0 ? 63 : num); /* unless it's NAK for next packet */
            if (n != num)                 /* which is just like an ACK for */
               return(state);             /* this packet so fall thru to... */

      case 'Y':                           /* ACK */
            if (n != num)                 /* If wrong ACK, hold out */
               return(state);
            numtry = 0;                   /* Reset try counter */
            n = (n + 1) % 64;             /* and bump packet count */
            fclose(fp);                   /* Close the input file */
            fp = ERROR;                   /* flag no file open */

            if (!get_file_spec(FALSE))
               return ('B');              /* Stop if no more files */
            if (aborted == ABORTZ)        /* Abort the rest ? */
            {
               while (get_file_spec(FALSE)); /* Flush directory buffer */
               return ('B');
            }
            return('F');                  /* More files, switch state to F */

      case 'E':                           /* Error packet received */
            prerrpkt(recpkt);             /* Print it out and */
            return('Q');                  /* abort */

      case 'T':                           /* Timeout */
            if (aborted)
            {
               error(PER_ABORTED);        /* send error packet */
               return('Q');               /* aborted by user */
            }
      case FALSE:                         /* Receive failure, stay in Z */
            return(state);

      default:                            /* Something else, "abort" */
            return('Q');
   }
}


/*
 *  s b r e a k
 *
 *  Send Break (EOT)
 */

char sbreak()
{
   char  num;                             /* Packet number */
   int len;                               /* length */

   if (numtry++ >= maxtry)                /* If too many tries "abort" */
      return('Q');

   spack('B',n,0,sndpkt,0,block_check_type);/* Send a B packet */
   switch (rpack(&len,&num,recpkt,block_check_type))/* What was the reply? */
   {
      case 'N':                           /* NAK, just stay in this state, */
            num = (--num < 0 ? 63 : num); /* unless NAK for previous packet, */
            if (n != num)                 /* which is just like an ACK for */
               return(state);             /* this packet so fall thru to... */

      case 'Y':                           /* ACK */
            if (n != num)                 /* If wrong ACK, fail */
               return(state);
            numtry = 0;                   /* Reset try counter */
            n = (n + 1) % 64;             /* and bump packet count */
            return('C');                  /* Switch state to Complete */

      case 'E':                           /* Error packet received */
            prerrpkt(recpkt);             /* Print it out and */
            return('Q');                  /* abort */

      case 'T':                           /* Timeout */
            if (aborted)
            {
               error(PER_ABORTED);        /* send error packet */
               return('Q');               /* aborted by user */
            }
      case FALSE:                         /* Receive failure, stay in B */
            return(state);

      default:                            /* Other, "abort" */
            return ('Q');
   }
}

lower_size()
{
   int rest;

   if (size >= 80)                    /* Minimum size to cut packet size to */
   {
      rest = size % 2;                /* Find out if odd or even */
      size /= 2;                      /* Half of it */
      size += rest;                   /* Add rest if odd */
      spsiz = size;                   /* Use the new size from now on */
      size_lowered = TRUE;            /* Flag size has been modified */
      check_prefix();         /* Make sure not in middle of prefix sequence */
   }
}

adjust_size()
{
   offset += size;             /* Increment offset to next unsend character */
   if (offset + size >= org_size) /* Adjust size if past end of buffer */
   {
      size = org_size - offset;
      size_lowered = FALSE;    /* Flag modification done */
   }
   else
      check_prefix();         /* Make sure not in middle of prefix sequence */
}

check_prefix()
{
   register char *p;
   char *q;

   p = sndpkt + offset;              /* Start check at this place */
   q = p + size;                     /* Until end of current window */
   while (p < q)
   {
      if (*p == repeat_quote)        /* If it's a repeat quote character .. */
         p += 2 * sizeof(char);      /* Skip count */
      if (*p == eight_quote)         /* If it's an eight-bit quote */
         p++;                        /* Skip it */
      if (*p == quote)               /* Same for control quote character */
         p++;
      p++;                           /* Point to next data character */
   }
   size = p - (sndpkt + offset);     /* Calculate new size */
}
