/*
   Copyright R.M.A.Lucock 1991
*/

#include <stdio.h>
#include <fcntl.h>
#include <string.h>

#define BUF_S 1024

int rwdata(
           int in_fh, int out_fh, int edflag,
           int nmode, int cbcflag, int xbflag
          )
{

char big_buf[BUF_S+16];

char *buffer;           /* pointer to data buffer */

char lhbuf[8];          /* look ahead buffer */

int look_ahead;         /* look ahead count */

int out_count;          /* bytes in buffer to be written */

int inlim;              /* number of chars to read */

int rdnum;              /* number of chars last read */

int first_block;        /* set if we are dealing with 1st block of the file */

int binbuf;             /* number of bytes currently in input buffer */

int file_end;           /* set if we have no bytes left to process */

int bytes_left;         /* set if we still have bytes left to READ */

int totbyte = 0;        /* set to total number of bytes processed */

int loop , nextra;

int rval[2];

unsigned int xorarray[2] = {0,0} , xorsave[2] = {0,0};


   first_block = 1;
   binbuf = 0;
   out_count = 0;
   file_end = 0;
   bytes_left = 1;

   /* Set up the look ahead buffer (of <= 8 bytes) */

   inlim = 8;
   buffer = lhbuf;
   look_ahead = 0;

   do
      {
      if((rdnum = read(in_fh, buffer, inlim)) < 0)
         {
         fprintf(stderr,"Error reading from input file\n");
         return(-1);
         }

      buffer += rdnum;
      look_ahead += rdnum;
      inlim -= rdnum;
      } while( (inlim > 0) && (rdnum != 0) );


   while(file_end != 1)
      {
      /*
         We now either have data left from the last read (in which
         case we use it) or we are at the end of the file (in which
         case we break out of the loop) or we should read in another
         chunk of bytes (up to BUF_S). The reading routine is slightly
         complicated because:

         a) we wish to read in a large chunk at once, not just eight
            bytes at a time.
         b) we may be reading from the standard input (eg a pipe) and
            so must cater for reading less than the requested amount
            without that actually implying that we have found the end
            of file.
         d) during encryption, the file may not be an exact multiple
            of 8 bytes. We handle the extra bytes outside of this loop.
         e) during decryption the file may not be an exact multiple of
            8 bytes. We ignore the final partial block.
         f) during decryption, if the input file is an exact multiple
            of BUF_S, we do not wish to write out the previous chunk
            of BUF_S bytes before we have determined whether there
            is more to read (if there is not, we will have to throw
            away between 1 and 8 bytes of the last block). We therefore
            decouple reads and write by using a lookahead buffer to
            'peek' the next 8 bytes.
      */

      if(binbuf == 0 && (file_end != 1))
         {
         /* write block  - but only if this is not our first time round ! */
         if(!first_block)
            {
            if((write(out_fh, big_buf, out_count)) < 0)
               {
               fprintf(stderr,"Error writing to output file\n");
               return(-1);
               }
            out_count = 0;
            }


         if(look_ahead == 0) /* nothing here means end of file */
            {
            binbuf = 0;
            bytes_left = 0;
            file_end = 1;
            }
         else
            {
            /* copy the lookahead buffer into the standard one */

            memcpy(big_buf,lhbuf,8);

            if(look_ahead < 8) /* we are on the final (partial) block */
               {
               bytes_left = 0;
               binbuf = look_ahead;
               }
            else
               {
               /*
                  read in the next chunk of BUF_S bytes. The last 8 of
                  these will go into the lookahead buffer (unless we
                  run out of input data).
               */

               inlim = BUF_S;
               buffer = big_buf + 8;
               binbuf = 8;

               do
                  {
                  if((rdnum = read(in_fh, buffer, inlim)) < 0)
                     {
                     fprintf(stderr,"Error reading from input file\n");
                     return(-1);
                     }

                  buffer += rdnum;
                  binbuf += rdnum;
                  inlim -= rdnum;

                  } while( (inlim > 0) && (rdnum != 0) );


               /*
                 ignore partial block if decrypting - can only happen
                 at the end
               */

               if(edflag)
                  {
                  if( (nextra = binbuf % 8) != 0)
                     {
                     binbuf -= nextra;
                     inlim += nextra;
                     }
                  }

               /* if more than 1024 bytes, move extra to lookahead buffer */

               if(inlim < 8)
                  {
                  memcpy(lhbuf,&big_buf[BUF_S],8);
                  look_ahead = 8 - inlim;
                  binbuf -= look_ahead;
                  rdnum = 1;
                  }
               else
                  look_ahead = 0;

               if(rdnum == 0)
                  bytes_left = 0;
               }
            }

         totbyte += binbuf;
         buffer = big_buf - 8;
         continue;
         }
      else if(binbuf > 7) /* if < 8, we handle it outside of this loop */
         {
         buffer += 8;
         binbuf -= 8;

         if(bytes_left == 0 && binbuf == 0)
            file_end = 1;
         }
      else
         {
         break;
         }


      /* en/decrypt */
      if(cbcflag == 0)
         encrypt(buffer,nmode);
      else
         {
         if(edflag)
            {
            xorarray[0] = xorsave[0];
            xorarray[1] = xorsave[1];
            xorsave[0] = ((unsigned int *)buffer)[0];
            xorsave[1] = ((unsigned int *)buffer)[1];

            encrypt(buffer,nmode);

            ((unsigned int *)buffer)[0] ^= xorarray[0];
            ((unsigned int *)buffer)[1] ^= xorarray[1];
            }
         else
            {
            ((unsigned int *)buffer)[0] ^= xorarray[0];
            ((unsigned int *)buffer)[1] ^= xorarray[1];

            encrypt(buffer,nmode);

            xorarray[0] = ((unsigned int *)buffer)[0];
            xorarray[1] = ((unsigned int *)buffer)[1];
            }
         }


      out_count += 8;

      /* remove padding added at encryption */

#ifdef DESTEST
      /* allow the program to work when being run with just 1 test block */
      if(file_end == 1 && edflag && (totbyte > 8))
#else
      if(file_end == 1 && edflag)
#endif
         {
         /* find out how much of the last block to discard */
         if( (nextra = buffer[7]) < 8)
            out_count -= (8 - nextra);
         else
            fprintf(
                    stderr,
                    "Bad `pad count' (%d) at end of decryption; count ignored\n",
                    nextra
                   );
         }

      first_block = 0;

   }


/*
   Now deal with extra bytes left over (this only occurs in encrypt
   mode). We must round up - even if there are zero bytes left (we
   round up to 8 extra !)
*/

   if(edflag == 0)
      {
      srand(time(0));
      rval[0] = rand();
      rval[1] = rand();

      buffer += 8;

      for(loop=binbuf; loop < 7; ++loop)
         buffer[loop] = ((char *)rval)[loop];

      buffer[7] = binbuf;


      /* encrypt */
      if(cbcflag == 0)
         encrypt(buffer,nmode);
      else
         {
         ((unsigned int *)buffer)[0] ^= xorarray[0];
         ((unsigned int *)buffer)[1] ^= xorarray[1];

         encrypt(buffer,nmode);

         xorarray[0] = ((unsigned int *)buffer)[0];
         xorarray[1] = ((unsigned int *)buffer)[1];
         }

      out_count += 8;

/*
      If xbflag is set, add between 0 and 7 random bytes. Note that
      we are not encrypting these. Therefore, since rand() returns a
      positive integer, we must also set bit 31 to a random value to
      ensure a true 32 bit random value (if paranoia is worth taking
      into account, it is worth taking into account properly !)
*/
      if(xbflag)
         {
         nextra = rand();
         nextra = nextra >> ( (sizeof(nextra)*8) - 4);

         buffer += 8;

         rval[0] = rand();
         rval[1] = rand();
         if(rand() > 0x3fffffff)
            rval[0] |= 0x80000000;
         if(rand() > 0x3fffffff)
            rval[1] |= 0x80000000;

         for(loop=0; loop < nextra; ++loop)
            buffer[loop] = ((char *)rval)[loop];

         out_count += nextra;
         }
      }


/* Check if there is anything left over to write out */

   if(out_count > 0)
      {
      if((write(out_fh, big_buf, out_count)) < 0)
         {
         fprintf(stderr,"Error writing to output file\n");
         return(-1);
         }
      }

   return(totbyte);
}
