/*****************************************************************************/
/* File name: Header.java                                                    */
/* Purpose: header class for Jtar                                            */
/* Last modified: 08.08.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 java.util.*;
import java.text.DecimalFormat;
import java.io.StringWriter;
import general.*;
import io.*;

public
class

Header

     implements jtar.Constants, general.Constants

{

  private final static String RCSID =
"$Header: /d/NFS/jtar/RCS/Header.java,v 1.5 1999/08/08 20:28:03 vklimov Exp $";


  final static int  STRING_MODES_LENGTH = 11;

  final static char MODE_COMPONENT_NOT_SET_CHAR = '-';
  final static char INIT_MODES_0_CHAR   = '?';
  final static char GNUTYPE_VOLHDR_MODES_0_CHAR = 'V';
  final static char GNUTYPE_MULTIVOL_MODES_0_CHAR = 'M';
  final static char GNUTYPE_NAMES_MODES_0_CHAR = 'N';
  final static char GNUTYPE_SPARSE_MODES_0_CHAR = '-';
  final static char GNUTYPE_DUMPDIR_MODES_0_CHAR = 'd';
  final static char DIRTYPE_MODES_0_CHAR = 'd';
  final static char SYMTYPE_MODES_0_CHAR = 'l';
  final static char BLKTYPE_MODES_0_CHAR = 'b';
  final static char CHRTYPE_MODES_0_CHAR = 'c';
  final static char FIFOTYPE_MODES_0_CHAR = 'p';
  final static char CONTTYPE_MODES_0_CHAR = 'C';

  final static int UGSWIDTH = 18;    /* maximum width encountered so far   */

  final static int DATEWIDTH = 18;   /* DATEWIDTH is the number of columns */
                                     /* taken by the date and time fields. */

  final static char MAJOR_MINOR_PRINTOUT_SEPARATOR = ',';

  final private static String TEXT_CHECKSUMS_NOT_EQUAL =
                                "Header.readHeader: " +
                                "checksums are not equal\n";

  static String nextLongName = null;
  static String nextLongLink = null;

  static Block currentHeader;
  static int ugswidth = UGSWIDTH;
  static boolean warnedOnce = false;

  int status = HEADER_STILL_UNREAD;

  private static int MODE_BIT_LETNGTH = 32;

  int format;

  Header( )
/*****************************************************************************/
/* Function name: Header                                                     */
/* File name: Header.java                                                    */
/* Purpose: constructor for the Header object                                */
/* Parameters:                                                               */
/* Returns:                                                                  */
/* Last modified: 23.09.98                                                   */
/* Author: Victor Klimov, 1998                                               */
/*****************************************************************************/
    {
                                                         /* Header */
/*---------------------------------------------------------------------------*/

    }
/*end of Header*/


  void
  finishHeader( Block header,
                BufferWrite writeBuffer,
                AllActions allActions    )
/*****************************************************************************/
/* Function name: finishHeader                                               */
/* File name: Header.java                                                    */
/* Purpose: to finish off a filled-in header block and write it out.         */
/*          We also print the file name and/or full info if verbose is on    */
/* Parameters: header                                                        */
/*             writeBuffer                                                   */
/*             allActions                                                    */
/* Returns:                                                                  */
/* Last modified: 26.07.99                                                   */
/* Author of the Java translation: Victor Klimov, 1999                       */
/*****************************************************************************/
    {
      int i, sum;
      byte[] chkBlanks;
                                                       /* finishHeader */
/*---------------------------------------------------------------------------*/

      chkBlanks = CHKBLANKS.getBytes();
      try
        {
          System.arraycopy( chkBlanks, 0,
                            header.chksum, 0,
                            HEADER_FIELD_LENGTH_CHKSUM );
        }
      catch( ArrayIndexOutOfBoundsException e )
        {
          System.out.println(
            "Header.finishHeader: " +
            e.getMessage() +
            ", this should not happen" );
          return;                                      /* return ! */
        }
      catch( ArrayStoreException e )
        {
          System.out.println(
            "Header.finishHeader: " +
            e.getMessage() +
            ", this should not happen" );
          return;                                      /* return ! */
        }
    /*end of try-catch*/

      header.fillUpBufferPosix();

      sum = 0;
      for ( i = header.buffer.length-1; i >= 0; i-- )
        sum += 0xFF & header.buffer[i];
    /*end of for-loop*/
                                              /* Fill in the checksum field */
                                              /* It's formatted differently */
                                              /* from the other fields: it  */
                                              /* has [6] digits, a null,    */
                                              /* then a space -- rather     */
                                              /* than digits, a space, then */
                                              /* a null. We use toOct then  */
                                              /* write the null in over     */
                                              /* toOct's space. The final   */
                                              /* space is already there,    */
                                              /* from checksumming, and     */
                                              /* toOct doesn't modify it    */
      Octal.toOct( sum,
              HEADER_FIELD_LENGTH_CHKSUM,
              header.chksum               );
      header.chksum[6] = general.Constants.NULL_BYTE;      /* zap the space */

      writeBuffer.writeBlock( header );

      if ( allActions.verbose.flag &&
           header.typeflag != GNUTYPE_LONGLINK &&
           header.typeflag != GNUTYPE_LONGNAME     )
        {
          /* These globals are parameters to printHeader, sigh.  */

          currentHeader = header;         /* currentStat is already set up */
          this.format = allActions.archiveFormat;
          printHeader( (Buffer) writeBuffer,
                       false, null,
                       allActions             );
        }
    /*end of if on verbose*/

    }
/*end of finishHeader*/


  boolean
  writeLong( String fileNameString, int type)
/*****************************************************************************/
/* Function name: writeLong                                                  */
/* File name: Header.java                                                    */
/* Purpose: to write a GNUTYPE_LONGLINK or GNUTYPE_LONGNAME block            */
/*          Cross recursion between startHeader and writeLong!               */
/* Parameters: fileNameString                                                */
/* Returns: true - if everything is OK, false - otherwise                    */
/* Last modified: 12.12.98                                                   */
/* Author of the Java translation: Victor Klimov, 1998                       */
/*****************************************************************************/
    {
                                                       /* writeLong */
/*---------------------------------------------------------------------------*/

      return false;

    }
/*end of writeLong*/


  Block

  startHeader( String fileNameString,
                c.Stat currentStat,
               AllActions allActions  )
/*****************************************************************************/
/* Function name: startHeader                                                */
/* File name: Header.java                                                    */
/* Purpose: to make a header object for dumping a file into a tar archive    */
/* Parameters: fileNameString                                                */
/*             currentStat                                                   */
/*             allActions                                                    */
/* Returns: header block if everything all right or null in case of an error */
/* Last modified: 26.07.99                                                   */
/* Author of the Java translation: Victor Klimov, 1999                       */
/*****************************************************************************/
    {
      int i;
      boolean writtenLong = true;
      StringBuffer fileNameStringBuffer = null;
      String       fileNameStringNew = null;
      Block        header;
                                                       /* startHeader */
/*---------------------------------------------------------------------------*/


      fileNameStringBuffer = new
        StringBuffer( fileNameString );

      if ( ! allActions.absoluteNames )
        {
          if (msdos.System.isMs())
            {
              fileNameStringBuffer =
                msdos.Path.
                  removeDriveSpecificationPrefix(
                    fileNameStringBuffer          );

              if ( !warnedOnce )
                {
                  warnedOnce = true;
                }
              else
                ;
            /*end of if on warnedOnce*/
            }
          else /* other than ms */
            ;
        /*end of if on isMs*/

          fileNameStringNew =
            util.String.
              removeLeadingPathSeparator(
                fileNameString            );
          if ( ! fileNameStringNew.equals(
                   fileNameString         ) &&
               ! warnedOnce )
            {
              warnedOnce = true;
            }
          else
            ;
        /*end of if on warnedOnce*/

        }
      else /* absoluteNames */
        fileNameStringNew =
          fileNameString;
    /*end of if on absoluteNames*/

      fileNameStringBuffer =
        new StringBuffer( fileNameStringNew );

                                      /* Check the file fileNameStringBuffer */
                                      /* and put it in the block             */

      if ( fileNameStringBuffer.length() >=
           NAME_FIELD_SIZE                  )
        writtenLong =
          writeLong( fileNameStringBuffer.
                        toString(),
                      GNUTYPE_LONGNAME      );
      else
        ;
    /*end of if on NAME_FIELD_SIZE*/

      if ( !writtenLong )
        return null;                                         /* return ! */
      else
        ;
    /*end of if on NAME_FIELD_SIZE*/

      header = new Block();

      allActions.currentFileName =
        fileNameStringBuffer.toString();

      util.String.stringAndNullsIntoByteArray(
        allActions.currentFileName,
        header.name                     );

      Octal.toOct( currentStat.size,
              HEADER_FIELD_LENGTH_SIZE,
              header.size                );
      Octal.toOct( currentStat.mode,
              HEADER_FIELD_LENGTH_MODE,
              header.mode                );

      header.typeflag =
        allActions.archiveFormat == V7_FORMAT ?
        AREGTYPE : REGTYPE;

      switch( allActions.archiveFormat )
        {
        case DEFAULT_FORMAT:
        case V7_FORMAT:
          break;

        case OLDGNU_FORMAT:

          util.String.stringAndNullsIntoByteArray(
            OLDGNU_MAGIC,
            header.magic              );
          break;                                       /* break */

        case POSIX_FORMAT:
        case GNU_FORMAT:
          util.String.stringAndNullsIntoByteArray(
            TMAGIC,
            header.magic              );
          util.String.stringAndNullsIntoByteArray(
            TVERSION,
            header.version              );
          break;                                       /* break */
        }
    /*end of switch on archiveFormat*/

      return header;

    }
/*end of startHeader*/


  int
  magicToFormat( Block header )
/*****************************************************************************/
/* Function name: magicToFormat                                              */
/* File name: Header.java                                                    */
/* Purpose: to determine current tar format from the magic field             */
/* Parameters: header                                                        */
/* Returns: V7_FORMAT                                                        */
/*          OLDGNU_FORMAT                                                    */
/*          POSIX_FORMAT                                                     */
/* Last modified: 12.12.98                                                   */
/* Author of Java modification: Victor Klimov, 1998                          */
/*****************************************************************************/
    {
      int format;
      String magic;
                                                       /* magicToFormat */
/*---------------------------------------------------------------------------*/

      magic = new String( header.magic );

      if (magic.equals( TMAGIC ))
        format = POSIX_FORMAT;
      else
        {
        if (magic.equals(OLDGNU_MAGIC))
          format = OLDGNU_FORMAT;
        else
          format = V7_FORMAT;            /* everything else is V7 */
      /*end of if on OLDGNU_MAGIC*/
        }
    /*end of if on TMAGIC*/

      return format;

    }
/*end of magicToFormat*/


  static
  String
  decodeMode( int mode )
/*****************************************************************************/
/* Function name: decodeMode                                                 */
/* File name: Header.java                                                    */
/* Purpose: to decode mode from its binary form in a stat structure and      */
/*          encode it into a string                                          */
/* Parameters: mode (bynary)                                                 */
/* Returns: the decoded string                                               */
/* Last modified: 12.12.98                                                   */
/* Author of the Java translation: Victor Klimov, 1998                       */
/*****************************************************************************/
    {
      int i;
      int mask;
      StringBuffer result = new StringBuffer();
      final String rwx = "rwxrwxrwx";
                                                       /* decodeMode */
/*---------------------------------------------------------------------------*/

      i = 0;
      for (mask = 0400; mask != 0; mask >>= 1)
        {
          if ( (mode & mask) != 0 )
            result.append( rwx.charAt( i++ ) );
          else
            {
              result.append( MODE_COMPONENT_NOT_SET_CHAR );
              i++;
            }
        }
    /*end of for-loop*/

      return result.toString();

    }
/*end of decodeMode*/



  void
  printBlockNumber( Buffer currentBuffer )
/*****************************************************************************/
/* Function name: printBlockNumber                                           */
/* File name: Header.java                                                    */
/* Purpose: to a number of the archive input block                           */
/* Parameters: currentBuffer - buffer object for input/output                */
/*                             of information                                */
/* Returns:                                                                  */
/* Last modified: 16.07.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
      DecimalFormat blockNumberFormat;
                                                   /* printBlockNumber */
/*---------------------------------------------------------------------------*/

      blockNumberFormat = new DecimalFormat( "0000000000" );
      blockNumberFormat.setMinimumIntegerDigits(1);

    }
/*end of printBlockNumber*/


  void
  printHeader( Buffer currentBuffer,
               boolean fillFileSystem,
               io.TarFileSystem ramFileSystem,
               AllActions allActions     )
/*****************************************************************************/
/* Function name: printHeader                                                */
/* File name: Header.java                                                    */
/* Purpose: to print the tar header actually                                 */
/* Parameters: currentBuffer - buffer object for input/output                */
/*                             of information                                */
/*             fillFileSystem - if true, we won't list, we just fill         */
/*                              the contents of the TarFileSystem object           */
/*             ramFileSystem - must me not null if fillFileSystem is true    */
/*             allActions                                                    */
/* Returns:                                                                  */
/* Last modified: 08.08.99                                                   */
/* Author of the Java translation: Victor Klimov, 1999                       */
/*****************************************************************************/
    {
      int decimalUid;
      int decimalGid;
      DecimalFormat sizeNumberFormat;
      DecimalFormat userOrGroupNumberFormat;
      String decodedModes;
      String quotedName;
      StringBuffer modes = new StringBuffer( STRING_MODES_LENGTH );
      String timestamp;
      String user;
      String group;
      String size;                /* holds a formatted long or maj, min */
      long longie;
      int pad;
      String name;
      Integer majorInteger;
      Integer minorInteger;
      Integer tempInteger;
                                                       /* printHeader */
/*---------------------------------------------------------------------------*/

      if (allActions.blockNumberOption)
        {
          printBlockNumber( currentBuffer );
        }
      else
        ;
    /*end of if*/

      if (allActions.verbose.flag)          /* File type and modes.  */
        {
          modes.append( INIT_MODES_0_CHAR );
          switch( currentHeader.typeflag )
            {
            case GNUTYPE_VOLHDR:
              modes.setLength( 0 );
              modes.append( GNUTYPE_VOLHDR_MODES_0_CHAR );
              break;

            case GNUTYPE_MULTIVOL:
              modes.setLength( 0 );
              modes.append( GNUTYPE_MULTIVOL_MODES_0_CHAR );
              break;

            case GNUTYPE_NAMES:
              modes.setLength( 0 );
              modes.append( GNUTYPE_NAMES_MODES_0_CHAR );
              break;

            case GNUTYPE_LONGNAME:
            case GNUTYPE_LONGLINK:
              break;

            case GNUTYPE_SPARSE:
            case REGTYPE:
            case AREGTYPE:
            case LNKTYPE:
              modes.setLength( 0 );
              modes.append( GNUTYPE_SPARSE_MODES_0_CHAR );
              if ( (allActions.currentFileName != null) &&
                   (allActions.currentFileName.endsWith( "/" ) )
                 )
                {
                  modes.setLength( 0 );
                  modes.append( DIRTYPE_MODES_0_CHAR );
                }
              else
                ;
            /*end of if on slash*/
              break;

            case GNUTYPE_DUMPDIR:
              modes.setLength( 0 );
              modes.append( GNUTYPE_DUMPDIR_MODES_0_CHAR );
              break;

            case DIRTYPE:
              modes.setLength( 0 );
              modes.append( DIRTYPE_MODES_0_CHAR );
              break;

            case SYMTYPE:
              modes.setLength( 0 );
              modes.append( SYMTYPE_MODES_0_CHAR );
              break;

            case BLKTYPE:
              modes.setLength( 0 );
              modes.append( BLKTYPE_MODES_0_CHAR );
              break;

            case CHRTYPE:
              modes.setLength( 0 );
              modes.append( CHRTYPE_MODES_0_CHAR );
              break;

            case FIFOTYPE:
              modes.setLength( 0 );
              modes.append( FIFOTYPE_MODES_0_CHAR );
              break;

            case CONTTYPE:
              modes.setLength( 0 );
              modes.append( CONTTYPE_MODES_0_CHAR );
              break;

            }
        /*end of switch on typeflag*/

          decodedModes =
            decodeMode( allActions.currentStat.mode );

          modes.append( decodedModes );
                                                       /* timestamp */
          longie = allActions.currentStat.mtime;

          timestamp = util.DateTime.isotime( longie );
                                                    /* user and group names */
          if ( (currentHeader.uname[0] !=
                               general.Constants.NULL_BYTE) &&
              (this.format != V7_FORMAT)
             )
            user = new String( currentHeader.uname );
          else
            {
              userOrGroupNumberFormat =
                new DecimalFormat( "###########" );
              decimalUid = Octal.fromOct(
                             HEADER_FIELD_LENGTH_UID,
                             currentHeader.uid       );
              user = userOrGroupNumberFormat.
                        format( decimalUid );
            }
        /*end of if on uname*/

          if ( (currentHeader.gname[0] !=
                               general.Constants.NULL_BYTE) &&
               (this.format != V7_FORMAT)
             )
            group = new String( currentHeader.gname );
          else
            {
              userOrGroupNumberFormat =
                new DecimalFormat( "###########" );
              decimalGid =
                Octal.fromOct( HEADER_FIELD_LENGTH_GID,
                                currentHeader.gid       );
              group = userOrGroupNumberFormat.
                        format( decimalGid );
            }
        /*end of if on gname*/
                                                    /* format the file size  */
                                                    /* or major/minor device */
                                                    /* numbers               */
          switch(currentHeader.typeflag)
            {
            case CHRTYPE:
            case BLKTYPE:
              majorInteger = new
                Integer( util.Device.major(
                           allActions.currentStat.rdev) );
              minorInteger = new
                Integer( util.Device.minor(
                           allActions.currentStat.rdev) );
              size =
                majorInteger.toString() +
                MAJOR_MINOR_PRINTOUT_SEPARATOR +
                minorInteger.toString();
              break;

            case GNUTYPE_SPARSE:
              tempInteger = new
                Integer( Octal.fromOct(
                           OLDGNU_HEADER_FIELD_LENGTH_REALSIZE,
                           currentHeader.realsize));
              size = tempInteger.toString();
              break;

            default:
              sizeNumberFormat = new DecimalFormat( "000000" );
              size = sizeNumberFormat.format( 
                       allActions.currentStat.size );
            }
        /*end of switch on typeflag*/
                                                      /* figure out padding  */
                                                      /* and print the whole */
                                                      /* line                */
          pad = user.length() + group.length() +
                size.length() + 1;
          if (pad > ugswidth)
            ugswidth = pad;
          else
            ;
        /*end of if*/

          name = util.QuoteString.quoteCopyString(
                   allActions.currentFileName     );

          if ( (fillFileSystem)        &&
               (ramFileSystem != null)
             )
            {
              ramFileSystem.addElement(
                name,
                ( (currentHeader.typeflag == DIRTYPE) ?
                  io.Filable.THIS_IS_A_DIRECTORY :
                  io.Filable.THIS_IS_A_REGULAR_FILE ),
                allActions.currentStat.size            );
            }
          else                                        /* regular printout */
            {
               switch( currentHeader.typeflag )
                 {
                 case SYMTYPE:
                   name = util.QuoteString.quoteCopyString(
                            allActions.currentLinkName );
                   break;

                 case LNKTYPE:
                   name = util.QuoteString.quoteCopyString(
                            allActions.currentLinkName );
                   break;

                 default:
                   break;

                 case AREGTYPE:
                 case REGTYPE:
                 case GNUTYPE_SPARSE:
                 case CHRTYPE:
                 case BLKTYPE:
                 case DIRTYPE:
                 case FIFOTYPE:
                 case CONTTYPE:
                 case GNUTYPE_DUMPDIR:
                   break;

                 case GNUTYPE_VOLHDR:
                   break;

                 case GNUTYPE_MULTIVOL:
                   break;

                 case GNUTYPE_NAMES:
                   break;
                 }
             /*end of switch on typeflag*/
            }
        /*end of if on null*/
        }
      else /* concise */
        {
          quotedName = util.QuoteString.
                          quoteCopyString( 
                            allActions.currentFileName );
          if ( (fillFileSystem)        &&
               (ramFileSystem != null)
             )
            {
              ramFileSystem.addElement(
                quotedName,
                ( (currentHeader.typeflag == DIRTYPE) ?
                  io.Filable.THIS_IS_A_DIRECTORY :
                  io.Filable.THIS_IS_A_REGULAR_FILE ),
                allActions.currentStat.size            );
            }
          else                                        /* regular printout */
            {
            }
        /*end of if on null*/
        }
    /*end of if on verbose.flag*/

    }
/*end of printHeader*/



  void

  decodeHeader( boolean doUserGroup,
                AllActions allActions )
/*****************************************************************************/
/* Function name: decodeHeader                                               */
/* File name: Header.java                                                    */
/* Purpose: to decode things from a file header block into                   */
/*          allActions.currentStat, also setting this.format                 */
/*          depending on the header block format. If doUserGroup,            */
/*          decode the user/group information (this is useful                */
/*          for extraction, but waste time when merely listing).             */
/*                                                                           */
/*          readHeader() has already decoded the checksum and length, so     */
/*          we don't.                                                        */
/*                                                                           */
/*          This routine should *not* be called twice for the same block,    */
/*          since the two calls might use different doUserGroup values and   */
/*          thus might end up with different uid/gid for the two calls.      */
/*          If anybody wants the uid/gid they should decode it first, and    */
/*          other callers should decode it without uid/gid before calling    */
/*          a routine, e.g. printHeader, that assumes decoded data           */
/* Parameters: doUserGroup                                                   */
/*             allActions                                                    */
/* Returns:                                                                  */
/* Last modified: 26.07.99                                                   */
/* Author of the Java translation: Victor Klimov, 1998, 1999                 */
/*****************************************************************************/
    {
      String unameString;
      String gnameString;
                                                       /* decodeHeader */
/*---------------------------------------------------------------------------*/

      this.format = magicToFormat(
                           this.currentHeader );

      allActions.currentStat.mode = (int)
        Octal.fromOct( HEADER_FIELD_LENGTH_MODE,
                  this.currentHeader.mode);

      allActions.currentStat.mode &=
            MASK_TO_KEEP_ONLY_PERMISSIONS_IN_MODE;
    
      allActions.currentStat.mtime =
        Octal.fromOct( HEADER_FIELD_LENGTH_MTIME,
                  this.currentHeader.mtime );

      if ( this.format == OLDGNU_FORMAT       &&
           allActions.incremental.flag )
        {
          allActions.currentStat.atime =
            Octal.fromOct( OLDGNU_HEADER_FIELD_LENGTH_ATIME,
                      this.currentHeader.atime);
          allActions.currentStat.ctime =
            Octal.fromOct( OLDGNU_HEADER_FIELD_LENGTH_CTIME,
                      this.currentHeader.ctime);
        }
      else
        ;
    /*end of if on incremental*/

      if (this.format == V7_FORMAT)
        {
          allActions.currentStat.uid = (int)
            Octal.fromOct( HEADER_FIELD_LENGTH_UID,
                      this.currentHeader.uid);
          allActions.currentStat.gid = (int)
            Octal.fromOct( HEADER_FIELD_LENGTH_GID,
                      this.currentHeader.gid);
          allActions.currentStat.rdev = 0;
        }
      else /* not V7 */
        {
          if (doUserGroup)
            {
              unameString = new String(
                                  this.currentHeader.uname,
                                  0, /* from the beginning */
                                  this.currentHeader.uname.length
                                );
              if (allActions.numericOwner ||
                  (this.currentHeader.uname[0] ==
                   general.Constants.NULL_BYTE ) ||
                  ! Names.unameToUid( unameString,
                                        allActions.currentStat        )
                 )
                allActions.currentStat.uid = (int)
                  Octal.fromOct( HEADER_FIELD_LENGTH_UID,
                                  this.currentHeader.uid );
              else
                ;
            /*end of if on uname*/

              gnameString = new String(
                                  this.currentHeader.gname,
                                  0, /* from the beginning */
                                  this.currentHeader.gname.length
                                );
              if (allActions.numericOwner ||
                  (this.currentHeader.gname[0] ==
                   general.Constants.NULL_BYTE ) ||
                  !Names.gnameToGid( gnameString,
                                       allActions.currentStat        )
                 )
                allActions.currentStat.gid = (int)
                  Octal.fromOct( HEADER_FIELD_LENGTH_GID,
                                  this.currentHeader.gid );
              else
                ;
            /*end of if on gname*/

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

          switch (this.currentHeader.typeflag)
            {
            case BLKTYPE:
//          allActions.currentStat.rdev =
//              makedev( Octal.fromOct( HEADER_FIELD_LENGTH_DEVMAJOR,
//                                 this.currentHeader.devmajor),
//                       Octal.fromOct( HEADER_FIELD_LENGTH_DEVMINOR,
//                                 this.currentHeader.devminor));
//            break;

            case CHRTYPE:
//            allActions.currentStat.rdev =
//              makedev( Octal.fromOct( HEADER_FIELD_LENGTH_DEVMAJOR,
//                                 this.currentHeader.devmajor),
//                       Octal.fromOct( HEADER_FIELD_LENGTH_DEVMINOR,
//                                 this.currentHeader.devminor));
//            break;

            default:
              allActions.currentStat.rdev = 0;
            }
        /*end of switch*/
        }
    /*end of if on format*/

    }
/*end of decodeHeader*/


  int
  readHeader( BufferRead readBuffer,
              AllActions allActions  )
/*****************************************************************************/
/* Function name: readHeader                                                 */
/* File name: Header.java                                                    */
/* Purpose: to read a block that's supposed to be a header block.            */
/*          The block will be put into "currentHeader", and if it is good,   */
/*          the file's size will be in currentStat.size.                     */
/*          You must always permitToFillUpNewCurrentBlock() to skip          */
/*          past the header which this routine reads.                        */
/*          The standard BSD tar sources create the checksum by adding up    */
/*          the bytes in the header as type char. I think the type char was  */
/*          unsigned on the PDP-11, but it's signed on the Next and Sun.     */
/*          It looks like the sources to BSD tar were never changed          */
/*          to compute the checksum currectly, so both the Sun and Next add  */
/*          the bytes of the header as signed chars.                         */
/*          This doesn't cause a problem until you get a file with a name    */
/*          containing characters with the high bit set.  So readHeader      */
/*          computes two checksums -- signed and unsigned.                   */
/*                                                                           */
/*          FIXME: The signed checksum computation is broken on machines     */
/*          where char's are unsigned. It's uneasy to handle all cases       */
/*          correctly.                                                       */
/* Parameters: readBuffer - buffer object, containing methods to get Blocks  */
/*             allActions                                                    */
/* Returns: HEADER_SUCCESS for success                                       */
/*          HEADER_ZERO_BLOCK for a block full of zeros (EOF marker).        */
/*          HEADER_END_OF_FILE on eof                                        */
/*          HEADER_FAILURE if the checksum is bad                            */
/* Last modified: 26.07.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {

      int i = 0;
      Block header;
      Block dataBlock;
      int unsignedSum;            /* the POSIX one */
      int signedSum;              /* the Sun one   */
      int recordedSum;
      int size;
                                                         /* readHeader */
/*---------------------------------------------------------------------------*/

      while( true )
        {
          header = readBuffer.getCurrentDataBlock( allActions );
          currentHeader = header;

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

          recordedSum =
            Octal.fromOct( HEADER_FIELD_LENGTH_CHKSUM,
                            header.chksum               );

          unsignedSum = 0;
          signedSum = 0;

          for (i = BLOCKSIZE-1; i-- > 0;)
            {

                                        /* We can't use unsigned char here   */
                                        /* because of old compilers, e.g. V7 */
              unsignedSum += 0xFF & header.buffer[i];
              signedSum += header.buffer[i];
            }
        /*end of for-loop*/
                                            /* Adjust checksum to count     */
                                            /* the "chksum" field as blanks */
          for (i = HEADER_FIELD_LENGTH_CHKSUM; i-- > 0;)
            {
              unsignedSum -= 0xFF & header.chksum[i];
              signedSum -= header.chksum[i];
            }
        /*end of for-loop*/

          unsignedSum += ' ' * HEADER_FIELD_LENGTH_CHKSUM;
          signedSum += ' ' * HEADER_FIELD_LENGTH_CHKSUM;

          if (unsignedSum ==                   /* This is a zeroed block... */
              HEADER_FIELD_LENGTH_CHKSUM * ' ') /* whole block is 0's except */
                                                /* for the blanks we faked   */
                                                /* for the checksum field.   */

              return HEADER_ZERO_BLOCK;                /* return ! */
          else
            ;
        /*end of if*/

          if ( (unsignedSum != recordedSum) &&
               (signedSum != recordedSum))
            {
              if (allActions.verbose.flag)
                {
                  System.out.println(
                    TEXT_CHECKSUMS_NOT_EQUAL +
                              "recordedSum\t=" +
                              recordedSum      +
                              "\nunsignedSum\t=" +
                              unsignedSum        +
                              "\nsignedSum\t="   +
                              signedSum             );
                }
              else
                ;
            /*end of if on verbose*/


              return HEADER_FAILURE;                     /* return ! */
            }
          else
            ;
        /*end of if*/
                               /* Good block.  Decode file size and return.  */

          if (header.typeflag == LNKTYPE)
            allActions.currentStat.size = 0;    /* links 0 size on tape */
          else
            allActions.currentStat.size =
              Octal.fromOct( HEADER_FIELD_LENGTH_SIZE,
                        header.size                );
        /*end of if*/

          header.name[HEADER_FIELD_LENGTH_NAME - 1] =
            general.Constants.NULL_BYTE;

          if (header.typeflag == GNUTYPE_LONGNAME ||
              header.typeflag == GNUTYPE_LONGLINK)
            {
              readBuffer.permitToFillUpNewCurrentBlock();

              for (size = allActions.currentStat.size;
                   size > 0;
                   size -= BLOCKSIZE            )
                {
                  dataBlock = readBuffer.getCurrentDataBlock( allActions );

                  if (dataBlock == null)
                    {
                      System.out.println(
                        "Header.readHeader: " +
                        "unexpected EOF " +
                        "on archive file"  );
                      break;                                /* break ! */
                    }
                  else
                    ;
                /*end of if on null*/

                  if (header.typeflag == GNUTYPE_LONGNAME)
                    nextLongName = new
                      String( dataBlock.buffer );
                  else
                    nextLongLink = new
                      String( dataBlock.buffer );
                /*end of if on GNUTYPE_LONGNAME*/

                  readBuffer.permitToFillUpNewCurrentBlock();
                }
            /*end of for-loop*/
            }
          else /* other type, NOT gnulong */
            {
              if (nextLongName != null)
                allActions.currentFileName =
                  nextLongName;
              else
                {
                  if (currentHeader.name[             /* ignore '\0' */
                        HEADER_FIELD_LENGTH_NAME - 1] ==
                      general.Constants.NULL_BYTE)
                    allActions.currentFileName = new
                                String( currentHeader.name,
                                        0, /* from the beginning */
                                        HEADER_FIELD_LENGTH_NAME - 1 );
                  else
                    allActions.currentFileName = new
                                String( currentHeader.name );
                }
            /*end of if on null*/

              if (nextLongLink != null)
                allActions.currentLinkName =
                  nextLongLink;
              else
                allActions.currentLinkName = new
                  String( currentHeader.linkname );
            /*end of if on null*/

              nextLongLink = null;
              nextLongName = null;

              return HEADER_SUCCESS;                           /* return ! */
            }
        /*end of if on typeflag*/
        }
    /*end of while-loop*/
    }
/*end of readHeader*/

} /*end of class Header */

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