/*****************************************************************************/
/* File name: BufferWrite.java                                               */
/* Purpose: buffer write 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.*;

class

BufferWrite

         extends Buffer

             implements jtar.Constants, general.Constants

{

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

  private static TarWriteResource archive;
  private static TarWritable      TarRegularOutputStream;
  private static TarWritable      TarGzippedOutputStream;
  private static ByteFixedArrayOutputStream outputStreamToRecord;


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

      super( allActions );

    }
/*end of BufferWrite*/


  void
  writeEot( )
/*****************************************************************************/
/* Function name: writeEot                                                   */
/* File name: BufferWrite.java                                               */
/* Purpose: to write end of tape block or blocks into an archive             */
/*          We actually zero at least one block, through end of the record   */
/* Parameters:                                                               */
/* Returns:                                                                  */
/* Last modified: 12.12.98                                                   */
/* Author of the java version: Victor Klimov, 1998                           */
/*****************************************************************************/
    {
      Block nullsBlock;
      int i;
      int space;
                                                         /* writeEot */
/*---------------------------------------------------------------------------*/

      nullsBlock = new Block();
      writeBlock( nullsBlock );
      flushWrite();

    }
/*end of writeEot*/


  void
  write( int b )
/*****************************************************************************/
/* Function name: write                                                      */
/* File name: BufferWrite.java                                               */
/* Purpose: to write a byte into an archive                                  */
/* Parameters: b - the byte to write                                         */
/* Returns:                                                                  */
/* Last modified: 12.12.98                                                   */
/* Author of the java version: Victor Klimov, 1998                           */
/*****************************************************************************/
    {
                                                         /* write */
/*---------------------------------------------------------------------------*/

      try
        {
          outputStreamToRecord.writeF( b );
        }
      catch( BufferFullException e )
        {
          flushWrite();
          this.write( b );
        }
    /*end of try-catch*/

    }
/*end of write*/


  int
  availableSpaceAfter()
/*****************************************************************************/
/* Function name: availableSpaceAfter                                        */
/* File name: BufferWrite.java                                               */
/* Purpose: to return the number of bytes available to write into a record   */
/*          array to fill it up with data to be written into an archive file */
/* Parameters:                                                               */
/* Returns: number of bytes                                                  */
/* Last modified: 12.12.98                                                   */
/* Author of the java version: Victor Klimov, 1998                           */
/*****************************************************************************/
    {
                                              /* availableSpaceAfter */
/*---------------------------------------------------------------------------*/

      return outputStreamToRecord.numberAvailableBytes();

    }
/*end of availableSpaceAfter*/


  void
  flushWrite()
/*****************************************************************************/
/* Function name: flushWrite                                                 */
/* File name: BufferWrite.java                                               */
/* Purpose: to flush memory buffer (byte array)  into the archive file       */
/* Parameters:                                                               */
/* Returns:                                                                  */
/* Last modified: 16.07.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
                                                    /* flushWrite */
/*---------------------------------------------------------------------------*/

      try
        {
          archive.write(
            outputStreamToRecord.buffer(), 0,
            outputStreamToRecord.
              numberOfBytesInTheBuffer()       );
          outputStreamToRecord.reset();
        }
      catch( IOException e )
        {
          System.out.println(
            e.getMessage() +
            "\nerror writing to archive file, exiting" );
        }
    /*end of try-catch*/

    }
/*end of flushWrite*/


  void
  dumpBufferFieldOfABlock( Block blockToWrite,
                           int offset           )
/*****************************************************************************/
/* Function name: dumpBufferFieldOfABlock                                    */
/* File name: BufferWrite.java                                               */
/* Purpose: to dump contents of the buffer field (byte array)                */
/*          from a Block object into the archive file                        */
/* Parameters: blockToWrite                                                  */
/*             offset - from which to start to take bytes from the buffer    */
/* Returns:                                                                  */
/* Last modified: 12.12.98                                                   */
/* Author: Victor Klimov, 1998                                               */
/*****************************************************************************/
    {
      int nDumpedBytesSoFar;
                                              /* dumpBufferFieldOfABlock */
/*---------------------------------------------------------------------------*/

      try
        {
          outputStreamToRecord.
            writeF( blockToWrite.buffer, offset,
                    blockToWrite.buffer.length - offset );
        }
      catch( BufferFullException e )
        {
          flushWrite();
          nDumpedBytesSoFar =
            e.processedBytesNumber();
          dumpBufferFieldOfABlock(
            blockToWrite,
            offset + nDumpedBytesSoFar );
        }
    /*end of try-catch*/

    }
/*end of dumpBufferFieldOfABlock*/


  void
  writeBlock( Block blockToWrite )
/*****************************************************************************/
/* Function name: writeBlock                                                 */
/* File name: BufferWrite.java                                               */
/* Purpose: to write a Block into a memory buffer (byte array) and           */
/*          dump the buffer into the archive file when needed                */
/* Parameters: blockToWrite                                                  */
/* Returns:                                                                  */
/* Last modified: 16.07.99                                                   */
/* Author: Victor Klimov, 1999                                               */
/*****************************************************************************/
    {
                                                    /* writeBlock */
/*---------------------------------------------------------------------------*/

      try
        {
          blockToWrite.fillUpBufferPosix();
          outputStreamToRecord.
            writeF( blockToWrite.buffer, 0,
                    blockToWrite.buffer.length );
        }
      catch( BufferFullException e )
        {
          flushWrite();
          writeBlock( blockToWrite );
        }
      catch( NullPointerException e )
        {
          System.out.println(
            "BufferWrite, writeBlock: outputStreamToRecord=" +
                      outputStreamToRecord );
        }
    /*end of try-catch*/

    }
/*end of writeBlock*/


  void
  extend()
/*****************************************************************************/
/* Function name: extend                                                     */
/* File name: BufferWrite.java                                               */
/* Purpose: to process an extended case                                      */
/* Parameters:                                                               */
/* Returns:                                                                  */
/* Last modified: 03.12.98                                                   */
/* Author of the java version: Victor Klimov, 1998                           */
/*****************************************************************************/
    {
      Block exhdr;
                                                             /* extend */
/*---------------------------------------------------------------------------*/

    }
/*end of extend*/


  void
  padItOutAndCloseInputStream(
    int sizeleft,
    FileInputStream fileToDumpInputStream )
/*****************************************************************************/
/* Function name: padItOutAndCloseInputStream                                */
/* File name: BufferWrite.java                                               */
/* Purpose: to pad out the archive to match the size we specified            */
/*          in the header (file shrunk or gave error)                        */
/* Parameters: sizeleft                                                      */
/*             fileToDumpInputStream                                         */
/* Returns:                                                                  */
/* Last modified: 16.07.99                                                   */
/* Author of the java version: Victor Klimov, 1999                           */
/*****************************************************************************/
    {
      Block start;
                                          /* padItOutAndCloseInputStream */
/*---------------------------------------------------------------------------*/

      while( sizeleft > 0 )
        {
          saveSizeleft = sizeleft;
          start = new Block();
          dumpBufferFieldOfABlock( start, 0 );
          sizeleft -= BLOCKSIZE;
        }
    /*end of while-loop*/

      try
        {
          fileToDumpInputStream.close();
        }
      catch( Exception e )
        {
          System.out.println(
              "padItOutAndCloseInputStream: " +
              e.getMessage()                    );
        }
    /*end of try-catch*/
    }
/*end of padItOutAndCloseInputStream*/


  int
  finishSparseFile( FileInputStream fileToDumpInputStream,
                      int sizeleft,
                      int fullsize,
                      String fileToDumpName                   )
/*****************************************************************************/
/* Function name: finishSparseFile                                           */
/* File name: BufferWrite.java                                               */
/* Purpose: to finish a sparse file                                          */
/* Parameters: fileToDumpInputStream                                         */
/*             sizeleft                                                      */
/*             fullsize                                                      */
/*             fileToDumpName                                                */
/* Returns: sizeleft - should be 0, if finished OK, otherwise is geater      */
/* Last modified: 12.12.98                                                   */
/* Author of the java version: Victor Klimov, 1998                           */
/*****************************************************************************/
    {
                                                   /* finishSparseFile */
/*---------------------------------------------------------------------------*/

      return 1; /* that is temporary - to signify that nothing has been done */

    }
/*end of finishSparseFile*/


  void
  dumpRegularFile( String fileToDumpName,
                   c.Stat currentStat,
                   FileInputStream fileToDumpInputStream,
                   BufferWrite writeBuffer,
                   Header tarHeader,
                   AllActions allActions                 )
/*****************************************************************************/
/* Function name: dumpRegularFile                                            */
/* File name: BufferWrite.java                                               */
/* Purpose: to write a file (to dump it) into an archive                     */
/* Parameters: fileToDumpName                                                */
/*             currentStat                                                   */
/*             fileToDumpInputStream                                         */
/*             writeBuffer                                                   */
/*             tarHeader                                                     */
/*             allActions                                                    */
/* Returns:                                                                  */
/* Last modified: 26.07.99                                                   */
/* Author of the java version: Victor Klimov, 1999                           */
/*****************************************************************************/
    {
      int bufsize;
      int nBytesRead;
      int sizeleft;
      Block start;
      Block header;
      byte isextended = 0;
      byte  saveTypeflag;
                                                     /* dumpRegularFile */
/*---------------------------------------------------------------------------*/

      sizeleft = currentStat.size;
                                                 /* If the file is sparse,   */
                                                 /* we've already taken care */
                                                 /* of this                  */
      header =
        tarHeader.startHeader(
          fileToDumpName,
          currentStat,
          allActions           );
      if (header == null)
        {
          try
            {
              fileToDumpInputStream.close();
            }
          catch( Exception e )
            {
              System.out.println(
                  "dumpRegularFile: " +
                  e.getMessage()                    );
            }
        /*end of try-catch*/

          System.out.println(
              "Can\'t startHeader on " +
              fileToDumpName              );

          return;                                          /* return ! */
        }
      else
        ;
    /*end of if on null*/

      isextended = header.isextended;
      saveTypeflag = header.typeflag;
      tarHeader.finishHeader(
        header,
        writeBuffer,
        allActions           );

      if (isextended != 0)
        extend();
      else
        ;
    /*end of if on isextended*/

      if (saveTypeflag == GNUTYPE_SPARSE)
        {
          sizeleft =
            finishSparseFile(
              fileToDumpInputStream,
              sizeleft, currentStat.size, fileToDumpName);
          if (sizeleft > 0)
            {
              padItOutAndCloseInputStream(
                sizeleft,
                fileToDumpInputStream       );
              return;                                         /* return ! */
            }
          else
            ;
        /*end of if on sizeleft*/
        }
      else /* regular, not sparse */
        {
        while( sizeleft > 0 )
          {
            start = new Block();

            bufsize =
              writeBuffer.availableSpaceAfter();

            bufsize =
              java.lang.Math.min(
                  start.buffer.length,
                  bufsize               );

            if (bufsize == 0)
              bufsize = start.buffer.length;
            else
              ;
          /*end of if on 0*/

	    if (sizeleft < bufsize)                        /* last read */
              bufsize = sizeleft;
            else
              ;
          /*end of if on sizeleft*/

            try
              {
                nBytesRead =
                  fileToDumpInputStream.read(
                    start.buffer, 0, bufsize );
              }
            catch( IOException e )
              {
                System.out.println(
                    "Read error at byte " +
                    (currentStat.size - sizeleft) +
                    ", reading " + bufsize +
                    " bytes, in file " +
                    fileToDumpName                     );

                padItOutAndCloseInputStream(
                  sizeleft,
                  fileToDumpInputStream       );
                return;                                      /* return ! */
              }
            catch( Exception e )
              {
                nBytesRead = bufsize;
              }
          /*end of try-catch*/

            sizeleft -= nBytesRead;

            dumpBufferFieldOfABlock( start, 0 );

            if (nBytesRead == bufsize)
              continue;                                     /* continue ! */
            else
              ;
          /*end of if on bufsize*/

            padItOutAndCloseInputStream(
              sizeleft,
              fileToDumpInputStream       );
            return;                                         /* return ! */
          }
      /*end of while-loop on sizeleft*/
        }
    /*end of if on GNUTYPE_SPARSE*/

      try
        {
          fileToDumpInputStream.close();
        }
      catch( Exception e )
        {
          System.out.println(
              "dumpRegularFile: " +
              e.getMessage()        );
        }
    /*end of try-catch*/

      return;

    }
/*end of dumpRegularFile*/


  void
  dumpIncremental()
/*****************************************************************************/
/* Function name: dumpIncremental                                            */
/* File name: BufferWrite.java                                               */
/* Purpose: to perform incremental dump of a directory                       */
/* Parameters:                                                               */
/* Returns:                                                                  */
/* Last modified: 04.12.98                                                   */
/* Author of the java version: Victor Klimov, 1998                           */
/*****************************************************************************/
    {
                                                    /* dumpIncremental */
/*---------------------------------------------------------------------------*/

    }
/*end of dumpIncremental*/


  void

  dumpDirectory( String fileToDumpName,
                 c.Stat currentStat,
                 File fileToDump,
                 FileInputStream fileToDumpInputStream,
                 BufferWrite writeBuffer,
                 Header tarHeader,
                 AllActions allActions     )
/*****************************************************************************/
/* Function name: dumpDirectory                                              */
/* File name: BufferWrite.java                                               */
/* Purpose: to write contents of a directory (recursively to dump it)        */
/*          into an archive                                                  */
/* Parameters: fileToDumpName                                                */
/*             currentStat                                                   */
/*             fileToDumpInputStream                                         */
/*             writeBuffer                                                   */
/*             tarHeader                                                     */
/*             allActions                                                    */
/* Returns:                                                                  */
/* Last modified: 26.07.99                                                   */
/* Author of the java version: Victor Klimov, 1999                           */
/*****************************************************************************/
    {
      int i;
      boolean readable;
      String[] fileNamesList;
      String  prefixForFileNamesList;
      Block header;
                                                     /* dumpDirectory */
/*---------------------------------------------------------------------------*/

      try
        {
          readable = fileToDump.canRead();
          fileNamesList = fileToDump.list();
        }
      catch( SecurityException e )
        {
          System.out.println(
            "BufferWrite.dumpDirectory: " +
            "can\'t open directory for reading \"" +
            fileToDumpName + "\" - security exception" );

          return;                                          /* return ! */
        }
    /*end of try-catch*/
                                             /* FIXME: I have the feeling   */
                                             /* this test is done too early */
                                             /* Couldn't it just be bundled */
                                             /* in later actions? I guess   */
                                             /* that the proper support     */
                                             /* of --ignore-failed-read is  */
                                             /* the key of the current      */
                                             /* writing                     */
      if ( ! readable )
        {
          System.out.println(
            "Cannot add directory " +
            fileToDumpName            );
          return;                                      /* return ! */
        }
      else
        ;
    /*end of if on readable*/
                                            /* Build new prototype name    */
                                            /* Ensure exactly one trailing */
                                            /* path separator              */
      fileToDumpName =
        util.String.
          removeTrailingPathSeparator(
            fileToDumpName             );
      fileToDumpName =
        fileToDumpName + File.separator;

      prefixForFileNamesList = fileToDumpName;

      currentStat.size = 0;                     /* force 0 size on dir */

      header =
        tarHeader.startHeader( 
          fileToDumpName,
          currentStat,
          allActions          );
      if (header == null)
        {
          System.out.println(
              "Can\'t startHeader on " +
              fileToDumpName              );
          return;   /* eg name too long */                  /* return ! */
        }
      else
        ;
    /*end of if on null*/

      if (allActions.incremental.flag)
        header.typeflag = GNUTYPE_DUMPDIR;
      else                              /* standard */
        header.typeflag = DIRTYPE;
    /*end of if on incremental*/
                                                   /* If we're gnudumping,  */
                                                   /* we aren't done yet so */
                                                   /* don't close it        */
      if ( ! allActions.incremental.flag)
        tarHeader.finishHeader(
          header,
          writeBuffer,
          allActions           );           /* done with directory header */
      else
        dumpIncremental();
    /*end of if on incremental*/

                                  /* FIXME: Implement to see if we are crossing
                                     from one file system to another, and
                                     avoid doing so if the user only wants
                                     to dump one file system

                                     Victor Klimov, 04.12.1998
                                   */

                                               /* Now output all the files */
                                               /* in the directory         */
      for (i=0; i<fileNamesList.length; i++)
        {
          if (allActions.exclude &&
              Names.checkExclude( fileNamesList[i] )
             )
            ; /* try next one */
          else
            {
              dumpFile( prefixForFileNamesList +
                         fileNamesList[i],
                        false, /*not a top level call*/
                        writeBuffer,
                        tarHeader,
                        allActions                       );
            }
        /*end of if on exclude*/
        }
    /*end of for-loop on fileNamesList*/

      return;
    }
/*end of dumpDirectory*/


  void
  dumpFile( String fileToDumpName,
            boolean calledFromTopLevel,
            BufferWrite writeBuffer,
            Header tarHeader,
            AllActions allActions        )
/*****************************************************************************/
/* Function name: dumpFile                                                   */
/* File name: BufferWrite.java                                               */
/* Purpose: to write a file (to dump it) into an archive                     */
/*          recursive on directories                                         */
/* Parameters: fileToDumpName                                                */
/*             calledFromTopLevel                                            */
/*             writeBuffer                                                   */
/*             tarHeader                                                     */
/*             allActions                                                    */
/* Returns:                                                                  */
/* Last modified: 26.07.99                                                   */
/* Author of the java version: Victor Klimov, 1999                           */
/*****************************************************************************/
    {
      File fileToDump;
      FileInputStream fileToDumpInputStream = null;
      boolean thisIsADirectory = false;
      boolean thisIsANormalFile = true;
      c.Stat currentStat = null;

      Block header;
      byte  type;
                                                         /* dumpFile */
/*---------------------------------------------------------------------------*/

      if ( allActions.interactive &&
           ! io.Interaction.confirm(
               MESSAGE_ACTION_STRING_ADD,
               fileToDumpName            )
         )
        return;                                            /* return ! */
      else
        ;
    /*end of if on interactive*/

      try /* to open dumped file for reading */
        {
          fileToDump = new File( fileToDumpName );
          fileToDumpInputStream = new
            FileInputStream( fileToDump );
          thisIsADirectory =
            fileToDump.isDirectory();
          thisIsANormalFile =
            fileToDump.isFile();
          currentStat = new c.Stat();
          currentStat.size = (int)
            fileToDump.length();
          currentStat.mode = (int)
            MODE_MASK_USER_READ   |
            MODE_MASK_USER_WRITE;

          allActions.currentStat = currentStat;
        }
      catch( NullPointerException e )
        {
          System.out.println(
            "BufferWrite.dumpFile: " +
            "no dump file name specifed" );

          return;                                           /* return ! */
        }
      catch( FileNotFoundException e )
        {
          System.out.println(
            "BufferWrite.dumpFile: " +
            fileToDumpName +
            ",\nexception message: " +
            e.getMessage()               );

          return;                                          /* return ! */
        }
      catch( SecurityException e )
        {
          System.out.println(
            "BufferWrite.dumpFile: " +
            "can\'t open file for reading \"" +
            fileToDumpName + "\" - security exception" );

          return;                                          /* return ! */
        }
    /*end of try-catch*/
                               /* See if we are trying to dump the archive.  */
      if ( fileToDumpName.
             equals( allActions.archiveName ) )
        {
          return;                                      /* return ! */
        }
      else
        ;
    /*end of if*/

      if ( thisIsANormalFile )
        dumpRegularFile( fileToDumpName,
                         currentStat,
                         fileToDumpInputStream,
                         writeBuffer,
                         tarHeader,
                         allActions            );
      else /* this is NOT a normal file */
        {
          if (thisIsADirectory)
            dumpDirectory( fileToDumpName,
                           currentStat,
                           fileToDump,
                           fileToDumpInputStream,
                           writeBuffer,
                           tarHeader,
                           allActions    );
          else /* this is NOT a normal file and NOT a directory */
            {
              return;                                          /* return ! */
            }
        /*end of if on thisIsADirectory*/
        }
    /*end of if on thisIsANormalFile*/

      return;

    }
/*end of dumpFile*/


  boolean

  open( AllActions allActions )
/*****************************************************************************/
/* Function name: open                                                       */
/* File name: BufferWrite.java                                               */
/* Purpose: to open an archive for writing                                   */
/* Parameters: allActions                                                    */
/* Returns: true - if everything is all right, false - otherwise             */
/* Last modified: 26.07.99                                                   */
/* Author of the java version: Victor Klimov, 1999                           */
/*****************************************************************************/
    {
      TarRegularOutputStream tarRegularOutputStream;
      TarGzippedOutputStream tarGzippedOutputStream;
      FileOutputStream fileOutputStream;
      boolean backedUpFlag = false;
      boolean result;
                                                            /* open */
/*---------------------------------------------------------------------------*/

      try /* to open archive for writing */
        {
          if (allActions.backup)
            {
              io.Backup.checkPathMakeBackup(
                allActions.archiveName,
                true /* this is an archive */  );
              backedUpFlag = true;
            }
          else
            ;
        /*end of if on backup*/

          archiveFile = new File( allActions.
                                    archiveName );
          if (allActions.gzip.flag)                      /* gzip */
            {
              fileOutputStream = new
                FileOutputStream( archiveFile );
              tarGzippedOutputStream = new
                TarGzippedOutputStream( fileOutputStream );
              archive = new
                TarWriteResource( tarGzippedOutputStream );
            }
          else /* regular tar */
            {
              tarRegularOutputStream = new
                TarRegularOutputStream( archiveFile );
              archive = new
                TarWriteResource( tarRegularOutputStream );
            }
        /*end of if on gzip*/

          outputStreamToRecord = new
            ByteFixedArrayOutputStream(
              allActions.recordSize     );

        }
      catch (NullPointerException e)
        {
          if (backedUpFlag)
            io.Backup.undoLastBackup();
          else
            ;
        /*end of if*/

          System.out.println(
            "Buffer.open: no archive name specifed" );
          return false;                                /* return ! */
        }
      catch (IOException e)
        {
          if (backedUpFlag)
            io.Backup.undoLastBackup();
          else
            ;
        /*end of if*/

          System.out.println(
            "Buffer.open: can\'t open archive file \"" +
            allActions.archiveName + "\" for writing" );
          return false;                                /* return ! */
        }
      catch (SecurityException e)
        {
          if (backedUpFlag)
            io.Backup.undoLastBackup();
          else
            ;
        /*end of if*/

          System.out.println(
            "Buffer.open: can\'t open archive file \"" +
            allActions.archiveName + "\" - security exception" );
          return false;                                /* return ! */
        }
    /*end of try-catch*/

      return true;

    }
/*end of open*/



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

      super.close();

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

    }
/*end of close*/


} /*end of class BufferWrite */

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