/*****************************************************************************/
/* File name: BufferRead.java                                                */
/* Purpose: buffer read work for Jtar                                        */
/* Last modified: 26.07.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*                                                                           */
/* This program is distributed in the hope that it will be useful, but       */
/* WITHOUT ANY WARRANTY; without even the implied warranty of                */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General */
/* Public License for more details.                                          */
/*                                                                           */
/* This software is distributed under the terms                              */
/* of the GNU General Public License as published                            */
/* by the Free Software Foundation; either version 2, or (at your option)    */
/* any later version.                                                        */
/* See file COPYING for your rights (GNU GPL)                                */
/*****************************************************************************/

package jtar;

import io.*;
import java.awt.*;
import java.util.*;
import java.io.*;
import general.*;

public
class

BufferRead

         extends Buffer

             implements jtar.Constants, general.Constants

{

  private final static String RCSID =
"$Header: /d/NFS/jtar/RCS/BufferRead.java,v 1.3 1999/07/29 16:06:21 vklimov Exp $";

  private static TarReadResource archive;
  private TarReadable     TarRegularInputStream;
  private TarReadable     TarGzippedInputStream;

  private int nOfCurrentBlock = 0;
  private boolean                 transferDataFromTheBuffer;
  private boolean                 readNewRecordIntoTheBuffer;
  private Block                   currentBlock;
  private PosByteArrayInputStream bufferArrayInputStream;
  private DataInputStream         bufferDataInputStream;

  BufferRead( AllActions allActions )
/*****************************************************************************/
/* Function name: BufferRead                                                 */
/* File name: BufferRead.java                                                */
/* Purpose: constructor for the BufferRead object                            */
/* Parameters: allActions                                                    */
/* Returns:                                                                  */
/* Last modified: 26.07.99                                                   */
/* Author: Victor Klimov, 1998, 1999                                         */
/*****************************************************************************/
    {
                                                         /* BufferRead */
/*---------------------------------------------------------------------------*/

      super( allActions );

      transferDataFromTheBuffer = true;
      readNewRecordIntoTheBuffer = true;
      currentBlock      = null;
      bufferArrayInputStream = null;
      bufferDataInputStream = null;

    }
/*end of BufferRead*/


  void
  closeBufferDataStream()
/*****************************************************************************/
/* Function name: closeBufferDataStream                                      */
/* File name: BufferRead.java                                                */
/* Purpose: to close the buffer streams                                      */
/* Parameters:                                                               */
/* Returns:                                                                  */
/* Last modified: 23.09.98                                                   */
/* Author: Victor Klimov, 1998                                               */
/*****************************************************************************/
    {
                                                    /* closeBufferDataStream */
/*---------------------------------------------------------------------------*/

      try
        {
          if (bufferDataInputStream != null)
            bufferDataInputStream.close();
          else
            ;
        /*end of if*/

          if (bufferArrayInputStream != null)
            bufferArrayInputStream.close();
          else
            ;
        /*end of if*/

        }
      catch( IOException e )
        {
          /* nothing */
        }
    /*end of try*/
    }
/*end of closeBufferDataStream*/


  void
  openBufferDataStream( AllActions allActions )
/*****************************************************************************/
/* Function name: openBufferDataStream                                       */
/* File name: BufferRead.java                                                */
/* Purpose: to open a ByteArrayInputStream to read from the buffer           */
/* Parameters: allActions                                                    */
/* Returns:                                                                  */
/* Last modified: 26.07.99                                                   */
/* Author: Victor Klimov, 1998, 1999                                         */
/*****************************************************************************/
    {
                                              /* openBufferDataStream */
/*---------------------------------------------------------------------------*/

      bufferArrayInputStream = new
        PosByteArrayInputStream( buffer,
                                 0, /* from the beginning */
                                 allActions.recordSize     );
      bufferDataInputStream = new
        DataInputStream( bufferArrayInputStream );

    }
/*end of openBufferDataStream */


  int
  scanForMagicsReturnFormat( Block bufferBlock )
/*****************************************************************************/
/* Function name: scanForMagicsReturnFormat                                  */
/* File name: BufferRead.java                                                */
/* Purpose: to scan the buffer byte array for magic strings and              */
/*          to determine the tar format based on the magic found             */
/* Parameters: bufferBlock                                                   */
/* Returns: V7_FORMAT                                                        */
/*          OLDGNU_FORMAT                                                    */
/*          POSIX_FORMAT                                                     */
/* Last modified: 11.12.98                                                   */
/* Author: Victor Klimov, 1998                                               */
/*****************************************************************************/
    {
      String buffer;
      int index;
                                        /* scanForMagicsReturnFormat */
/*---------------------------------------------------------------------------*/

      buffer = new String( bufferBlock.buffer );

      index = buffer.indexOf( TMAGIC );
      if (index > 0)
        return POSIX_FORMAT;                           /* return ! */
      else
        ;
    /*end of if*/

      index = buffer.indexOf( OLDGNU_MAGIC );
      if (index > 0)
        return OLDGNU_FORMAT;                           /* return ! */
      else
        return V7_FORMAT;                               /* return ! */
    /*end of if*/

    }
/*end of scanForMagicsReturnFormat*/


  void
  readOldgnuHeader( Block resultBlock )
                                         throws EOFException, IOException
/*****************************************************************************/
/* Function name: readOldgnuHeader                                           */
/* File name: BufferRead.java                                                */
/* Purpose: to read field of the old GNU header from a Data Stream           */
/* Parameters: resultBlock                                                   */
/* Returns:                                                                  */
/* Last modified: 11.12.98                                                   */
/* Author: Victor Klimov, 1998                                               */
/*****************************************************************************/
    {
      int i;
                                                 /* readOldgnuHeader */
/*---------------------------------------------------------------------------*/

      bufferDataInputStream.
        skipBytes( OLDGNU_HEADER_FIELD_LENGTH_UNUSED_PAD1 );
      bufferDataInputStream.readFully( resultBlock.atime );
      bufferDataInputStream.readFully( resultBlock.ctime );
      bufferDataInputStream.readFully( resultBlock.offset );
      bufferDataInputStream.readFully( resultBlock.longnames );
      resultBlock.unusedPad2 = bufferDataInputStream.readByte();

      for (i = 0; i < SPARSES_IN_OLDGNU_HEADER; i++)
        {
          bufferDataInputStream.
            readFully( resultBlock.sp[i].offset );
          bufferDataInputStream.
            readFully( resultBlock.sp[i].numbytes );
        }
    /*end of for-loop*/

      resultBlock.isextended =  bufferDataInputStream.readByte();
      bufferDataInputStream.readFully( resultBlock.realsize );
      bufferDataInputStream.
        skipBytes( OLDGNU_HEADER_FIELD_LENGTH_UNUSED_AT_THE_END );

    }
/*end of readOldgnuHeader*/


  void
  readPosixHeader( Block resultBlock )
                                         throws EOFException, IOException
/*****************************************************************************/
/* Function name: readPosixHeader                                            */
/* File name: BufferRead.java                                                */
/* Purpose: to read field of the posix header from a Data Stream             */
/* Parameters: resultBlock                                                   */
/* Returns:                                                                  */
/* Last modified: 12.12.98                                                   */
/* Author: Victor Klimov, 1998                                               */
/*****************************************************************************/
    {
                                                 /* readPosixHeader */
/*---------------------------------------------------------------------------*/

      bufferDataInputStream.readFully( resultBlock.name );
      bufferDataInputStream.readFully( resultBlock.mode );
      bufferDataInputStream.readFully( resultBlock.uid );
      bufferDataInputStream.readFully( resultBlock.gid );
      bufferDataInputStream.readFully( resultBlock.size );
      bufferDataInputStream.readFully( resultBlock.mtime );
      bufferDataInputStream.readFully( resultBlock.chksum );
      resultBlock.typeflag = bufferDataInputStream.readByte();
      bufferDataInputStream.readFully( resultBlock.linkname );
      bufferDataInputStream.readFully( resultBlock.magic );
      bufferDataInputStream.readFully( resultBlock.version );
      bufferDataInputStream.readFully( resultBlock.uname );
      bufferDataInputStream.readFully( resultBlock.gname );
      bufferDataInputStream.readFully( resultBlock.devmajor );
      bufferDataInputStream.readFully( resultBlock.devminor );
      bufferDataInputStream.readFully( resultBlock.prefix );
      bufferDataInputStream.
        skipBytes( POSIX_HEADER_FIELD_LENGTH_UNUSED_AT_THE_END );
    }
/*end of readPosixHeader*/


  void
  skipFile( int size,
            AllActions allActions )
/*****************************************************************************/
/* Function name: skipFile                                                   */
/* File name: BufferRead.java                                                */
/* Purpose: to skip over size bytes of data in blocks in the archive         */
/* Parameters: size                                                          */
/*             allActions                                                    */
/* Returns:                                                                  */
/* Last modified: 26.07.99                                                   */
/* Author of the Java translation: Victor Klimov, 1999                       */
/*****************************************************************************/
    {
      int neededToSkip;
      Block dummyBlock;
                                                        /* skipFile */
/*---------------------------------------------------------------------------*/

      neededToSkip = size;

      if (allActions.multiVolume)
        {
          this.saveTotsize = size;
          this.saveSizeleft = size;
        }
      else
        ;
    /*end of if on multiVolume*/

      while (size > 0)
        {
          dummyBlock = getCurrentDataBlock( allActions );

          if (dummyBlock == null)
            {
              System.out.println(
                "BufferRead.skipFile: " +
                "unexpected EOF on archive file\n" +
                "needed to skip " + neededToSkip +
                " bytes, " + size +
                " left and got end of file" );
              return;                                  /* return ! */
            }
          else
            ;
        /*end of if on null*/

          permitToFillUpNewCurrentBlock();
          size -= BLOCKSIZE;
          if (allActions.multiVolume)
            this.saveSizeleft -= BLOCKSIZE;
          else
            ;
        /*end of if on multiVolume*/

        }
    /*end of while-loop*/

    }
/*end of skipFile*/


  void
  skipExtendedHeaders( AllActions allActions )
/*****************************************************************************/
/* Function name: skipExtendedHeaders                                        */
/* File name: BufferRead.java                                                */
/* Purpose: to skip over extended headers                                    */
/* Parameters: allActions                                                    */
/* Returns:                                                                  */
/* Last modified: 26.07.99                                                   */
/* Author of the Java translation: Victor Klimov, 1998, 1999                 */
/*****************************************************************************/
    {
      Block exhdr;
                                               /* skipExtendedHeaders */
/*---------------------------------------------------------------------------*/

      while( true )
        {
          exhdr = getCurrentDataBlock( allActions );

          if (exhdr == null) /* eof */
            break;                                     /* break ! */
          else
            ;
        /*end of if on null*/

          if (exhdr.isextended != 0)
            permitToFillUpNewCurrentBlock();
          else /* regular */
            {
              permitToFillUpNewCurrentBlock();
              break;                                   /* break ! */
            }
        /*end of if on isextended*/
        }
    /*end of while-loop*/

    }
/*end of skipExtendedHeaders*/


  Block
  getNewCurrentBlock()
/*****************************************************************************/
/* Function name: getNewCurrentBlock                                         */
/* File name: BufferRead.java                                                */
/* Purpose: to create a new Block object and to read data into it            */
/*          from the buffer byte array                                       */
/* Parameters:                                                               */
/* Returns: new Block object - if everything is read OK                      */
/*          null - in case of an error                                       */
/* Last modified: 12.12.98                                                   */
/* Author: Victor Klimov, 1998                                               */
/*****************************************************************************/
    {
      int i;
      int format;
      int   nBytesRead;
      Block resultBlock = null;
                                                /* getNewCurrentBlock */
/*---------------------------------------------------------------------------*/

      if (bufferDataInputStream == null)
        return null;                                   /* return ! */
      else
        ;
    /*end of if*/

      resultBlock = new Block();

      try
        {
          bufferArrayInputStream.mark(         /* mark current pos */
                bufferArrayInputStream.pos() );

          nBytesRead =
            bufferArrayInputStream.read( resultBlock.buffer,
                                         0, /* from the current pos */
                                         BLOCKSIZE                  );
          if (nBytesRead != BLOCKSIZE) /* not enough data */
            return null;                                   /* return ! */
          else
            ;
        /*end of if*/

          bufferArrayInputStream.reset(); /* reset to the previous pos    */
                                          /* to read the same bytes again */
          format =
            scanForMagicsReturnFormat( resultBlock );

          readPosixHeader( resultBlock );

          nOfCurrentBlock++;

          if (format == OLDGNU_FORMAT)
            {                                  /* reset to the previous pos */
              bufferArrayInputStream.reset();
                                             /* to read the same bytes again */
              readOldgnuHeader( resultBlock );

            }
          else
            ;
        /*end of if on OLDGNU_FORMAT*/

        }
      catch( EOFException e )
        {
          readNewRecordIntoTheBuffer = true;
          resultBlock = null;
        }
      catch( IOException e )
        {
          resultBlock = null;
        }
    /*end of try*/

      return resultBlock;

    }
/*end of getNewCurrentBlock */


  int
  availableSpaceAfter()
/*****************************************************************************/
/* Function name: availableSpaceAfter                                        */
/* File name: BufferRead.java                                                */
/* Purpose: to return the number of bytes to read yet from the array stream  */
/*          without reading in a new record from a device or from a file     */
/* Parameters:                                                               */
/* Returns: number of bytes available in the array or                        */
/*          general.Constants.JAVA_ERROR_CODE in case of an error            */
/* Last modified: 12.12.98                                                   */
/* Author: Victor Klimov, 1998                                               */
/*****************************************************************************/
    {
                                              /* availableSpaceAfter */
/*---------------------------------------------------------------------------*/

      if (bufferArrayInputStream == null)
        return general.Constants.JAVA_ERROR_CODE;      /* return ! */
      else
        {
           return bufferArrayInputStream.available();
        }
    /*end of if*/
    }
/*end of availableSpaceAfter */


  void
  permitToFillUpNewCurrentBlock()
/*****************************************************************************/
/* Function name: permitToFillUpNewCurrentBlock                              */
/* File name: BufferRead.java                                                */
/* Purpose: to set the flag that permits to fill up the new currentBlock     */
/*          from the buffer byte array                                       */
/* Parameters:                                                               */
/* Returns:                                                                  */
/* Last modified: 12.12.98                                                   */
/* Author: Victor Klimov, 1998                                               */
/*****************************************************************************/
    {
                                        /* permitToFillUpNewCurrentBlock */
/*---------------------------------------------------------------------------*/

      transferDataFromTheBuffer = true;

    }
/*end of permitToFillUpNewCurrentBlock */


  int
  currentBlockOrdinal()
/*****************************************************************************/
/* Function name: currentBlockOrdinal                                        */
/* File name: BufferRead.java                                                */
/* Purpose: to return the number of the current block                        */
/* Parameters:                                                               */
/* Returns: the number                                                       */
/* Last modified: 12.12.98                                                   */
/* Author: Victor Klimov, 1998                                               */
/*****************************************************************************/
    {
                                                /* currentBlockOrdinal */
/*---------------------------------------------------------------------------*/

      return nOfCurrentBlock;

    }
/*end of currentBlockOrdinal*/


  int
  readRecord( AllActions allActions )
/*****************************************************************************/
/* Function name: readRecord                                                 */
/* File name: BufferRead.java                                                */
/* Purpose: to read a record that contains usually several blocks            */
/* Parameters: allActions                                                    */
/* Returns: number of bytes read or                                          */
/*          JAVA_ERROR_CODE                                                  */
/* Last modified: 26.07.99                                                   */
/* Author of the java version: Victor Klimov, 1999                           */
/*****************************************************************************/
    {
      int bytesRead = 0;
      int totalBytesRead = 0;
                                                         /* readRecord */
/*---------------------------------------------------------------------------*/

      try
        {
          nBytesBuffer = 0;
          while (totalBytesRead < allActions.recordSize)
            {
              bytesRead =
                archive.read( buffer,                /* blocks */
                              totalBytesRead, /* == 0 at first iteration */
                              allActions.recordSize - totalBytesRead );
              if ( bytesRead == JAVA_ERROR_CODE )
                {
                  if (totalBytesRead > 0)
                    return totalBytesRead;                     /* return ! */
                  else
                    return JAVA_ERROR_CODE;                      /* return ! */
                /*end of if on 0*/
                }
              else
                ;
            /*end of if on EOF*/

              totalBytesRead += bytesRead;
              nBytesBuffer = totalBytesRead; 
            }
        /*end of while-loop*/

        }
      catch( java.io.IOException e )
        {
          return JAVA_ERROR_CODE;                      /* return ! */
        }
    /*end of try*/

      return totalBytesRead;

    }
/*end of readRecord*/


  public
  Block

  getCurrentDataBlock( AllActions allActions )
/*****************************************************************************/
/* Function name: getCurrentDataBlock                                        */
/* File name: BufferRead.java                                                */
/* Purpose: reads in next record and checks for EOF                          */
/* Parameters: allActions                                                    */
/* Returns: a Block object if there is a data block available                */
/*          null - in case of EOF                                            */
/* Last modified: 26.07.98                                                   */
/* Author of the java version: Victor Klimov, 1998, 1999                     */
/*****************************************************************************/
    {
      int     bytesRead;
      int     bytesLeft;
                                              /* getCurrentDataBlock */
/*---------------------------------------------------------------------------*/

      if (eofEncountered)
        {
          return null;                                   /* return ! */
        }
      else
        ;
    /*end of if on eofEncountered*/

      bytesLeft = availableSpaceAfter();

      if ( (bytesLeft > 0 ) &&
           ( ! readNewRecordIntoTheBuffer)   /* use old record */
         )
        {
          if (transferDataFromTheBuffer) /* get a block from the record */
            {
              currentBlock = getNewCurrentBlock();

              if (currentBlock != null) /* got a block */
                {
                  transferDataFromTheBuffer = false;
                  return currentBlock;                           /* return ! */
                }
              else /* nothing in the buffer */
                {
                  transferDataFromTheBuffer = true;
                }
            /*end of if on null*/
            }
          else
            ;
        /*end of if on transferDataFromTheBuffer*/

          if (currentBlock != null)
            {
              return currentBlock;                           /* return ! */
            }
          else /* get a new record and a block from there */
            {
              transferDataFromTheBuffer = true;
            }
        /*end of if*/

        }
      else /* read in a record from the device */
        {
        transferDataFromTheBuffer = true;
        }
    /*end of if on readNewRecordIntoTheBuffer*/

      bytesRead =
        readRecord( allActions );

      if (bytesRead < 0) /* eof */
        {
          eofEncountered = true;
          transferDataFromTheBuffer = false;
          return null;                                /* return ! */
        }
      else /* got a record */
        {
          readNewRecordIntoTheBuffer = false;
          closeBufferDataStream();
          openBufferDataStream( allActions );
        }
    /*end of if on bytesRead*/

      currentBlock = getNewCurrentBlock();
                                              /* deliberately no check */
                                              /* for null, the caller  */
                                              /* of this routine       */
                                              /* must check            */
      transferDataFromTheBuffer = false;

      return currentBlock;
    }
/*end of getCurrentDataBlock*/


  boolean

  open( AllActions allActions )
/*****************************************************************************/
/* Function name: open                                                       */
/* File name: BufferRead.java                                                */
/* Purpose: to open an archive for reading                                   */
/* Parameters: allActions                                                    */
/* Returns: true - if everything is all right, false - otherwise             */
/* Last modified: 26.07.99                                                   */
/* Author of the java version: Victor Klimov, 1999                           */
/*****************************************************************************/
    {
      FileInputStream fileInputStream;
      TarRegularInputStream tarRegularInputStream;
      TarGzippedInputStream tarGzippedInputStream;

      Block currentBlock = null;

      boolean result = true;
                                                            /* open */
/*---------------------------------------------------------------------------*/

      result =
        super.open( allActions );

      if (!result)
        return result;                                 /* return ! */
      else
        ;
    /*end of if*/

      try
        {
          archiveFile = new File( allActions.
                                    archiveName );
          if (allActions.gzip.flag)                      /* gzip */
            {
              fileInputStream = new
                FileInputStream( archiveFile );
              tarGzippedInputStream = new
                TarGzippedInputStream(
                  (InputStream) fileInputStream );
              archive = new
                TarReadResource( tarGzippedInputStream );
            }
          else /* regular tar */
            {
              tarRegularInputStream = new
                TarRegularInputStream( archiveFile );
              archive = new
                TarReadResource( tarRegularInputStream );
            }
        /*end of if on allActions.gzip.flag*/
        }
      catch (NullPointerException e)
        {
          System.out.println(
            "Buffer.open: no archive name specifed" );
          return false;                                /* return ! */
        }
      catch (FileNotFoundException e)
        {
          System.out.println(
            "BufferRead.open: can\'t open archive file \"" +
            allActions.archiveName + "\"" );
          return false;                                /* return ! */
        }
      catch (IOException e)
        {
          System.out.println(
            "BufferRead.open: I/O " +
            "error on archive file \"" +
            allActions.archiveName + "\"" );
          return false;                                /* return ! */
        }
      catch (SecurityException e)
        {
          System.out.println(
            "BufferRead.open: can\'t open archive file \"" +
            allActions.archiveName + "\" - security exception" );
          return false;                                /* return ! */
        }
    /*end of try-catch*/

      currentBlock =                       /* read in the first record */
        getCurrentDataBlock( allActions );

      if (currentBlock == null)
        return false;
      else
        return true;
    /*end of if*/

    }
/*end of open*/



  void
  close( )
/*****************************************************************************/
/* Function name: close                                                      */
/* File name: BufferRead.java                                                */
/* Purpose: to close an archive after reading                                */
/* Parameters:                                                               */
/* Returns:                                                                  */
/* Last modified: 16.07.99                                                   */
/* Author of the java version: Victor Klimov, 1999                           */
/*****************************************************************************/
    {
                                                            /* close */
/*---------------------------------------------------------------------------*/

      super.close();

      try
        {
          archive.close();
        }
      catch( java.io.IOException e )
        {
        }
    /*end of try-catch block*/

    }
/*end of close*/


} /*end of class BufferRead */

/*****************************************************************************/
/* End of file: BufferRead.java                                              */
/*****************************************************************************/
