//
// tardy - a tar post-processor
// Copyright (C) 1998, 1999, 2002, 2004, 2008, 2009, 2011 Peter Miller
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or (at
// your option) any later version.
//
// 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.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//

#ifndef LIBTARDY_TAR_INPUT_H
#define LIBTARDY_TAR_INPUT_H

#include <libtardy/config.h>
#include <boost/shared_ptr.hpp>

#include <libtardy/file/input.h>
#include <libtardy/file/output.h>
#include <libtardy/format_family.h>
#include <libtardy/tar/header.h>
#include <libtardy/tar/output.h>

/**
  * The tar_input class represents an abstract archive input source.
  * It was originally tar(5) formatted archives, but has since been
  * generalized to cpio(1) archives and even lists of file names.
  *
  * The expected sequence of operations is
  *                                                                     <pre>
  *                 |
  *     +-----------------------+
  *     |   read_archive_begin  |
  *     +-----------------------+
  *                 |
  *                 | ,------------------<---------------.
  *                 |/                                    \
  *                 V                                      |
  *                 |                                      |
  *     +-----------------------+                          |
  *     |      read_header      |                          ^
  *     +-----------------------+                          |
  *                 |   \                                  |
  *                 |    `--- false ----->-------------------.
  *                 |                                      |  \
  *     +-----------------------+                          |   |
  *     |  read_header_padding  |                          ^   v
  *     +-----------------------+                          |   |
  *                 |                                      |   |
  *                 *------->-------.                      |   |
  *                 |                \                     |   |
  *                 |                 |                    |   |
  *                 |                 | ,------<-----.     |   |
  *                 |                 |/              \    |   |
  *                 |                 V                |   |   |
  *                 |                 |                |   |   |
  *                 |     +-----------------------+    |   |   |
  *                 |     |       read_data       |    ^   ^   v
  *                 |     +-----------------------+    |   |   |
  *                 |                 |               /    |   |
  *                 |                 *-------->-----'     |   |
  *                 |                 |                    |   |
  *                 |                /                     |   |
  *                 | ,-----<-------'                      ^   v
  *                 |/                                     |   |
  *                 V                                      |   |
  *                 |                                      |   |
  *                 |                                      |   |
  *     +-----------------------+                          |   |
  *     |   read_data_padding   |                          |   |
  *     +-----------------------+                          |   |
  *                 |                                     /    |
  *                 `-------------------->---------------'     |
  *                                                           /
  *                 .----------------------------------------'
  *                 |
  *     +-----------------------+
  *     |    read_archive_end   |
  *     +-----------------------+
  *                 |
  *                                                                   </pre>
  */
class tar_input
{
public:
    /**
      * The pointer type should be used for all pointers to input
      * instances, as it does reference counting a resource clean up at
      * the appropriate time.
      */
    typedef boost::shared_ptr<tar_input> pointer;

    /**
      * The destructor.
      */
    virtual ~tar_input();

    /**
      * The factory class method is used to sniff at a file to determine
      * its format, and then create the appropriate instance.
      *
      * @param ifp
      *     The file to be read and parsed into archive members.
      * @returns
      *     A pointer to an open archive reader instance.
      */
    static pointer factory(const file_input::pointer &ifp);

    /**
      * The tar_output_factory method is used to manufactore a new
      * tar_output instance, based on the present input format, but to
      * the file specified.
      *
      * This will allow the tardy(1) command to write the same kind of
      * archive as seen on the input, by default.
      *
      * The default implementation returns a new tar_output_tar_posix
      * instance, for backwards compatibility and sanity.
      *
      * Derived classes may override this with something more specific,
      * but should strive to meet user perceptions and expectations as
      * the what "the same" means, when creating a <i>corresponding</i>
      * output format instance.
      */
    virtual tar_output::pointer tar_output_factory(
        const file_output::pointer &ofp) const;

    /**
      * The read_data method is used to read a block of data from
      * the input.  The buffer_length argument specifies the
      * maximum number of bytes in the buffer.  Returns the number
      * of bytes read, or 0 at end of file.
      *
      * @param data
      *     Pointer to the base of an array of char in which to place
      *     the data.
      * @param data_size
      *     The number of bytes opf data to read, maximum size of the
      *     data array.
      */
    virtual int read_data(void *data, int data_size) = 0;

    /**
      * The read_data_padding is used to read any extra data between
      * files.  By default, this method does nothing.
      */
    virtual void read_data_padding(void);

    /**
      * The read_header method is used to read file information from
      * the input.  Returns 0 at end of input.
      *
      * @param hdr
      *     The header variable to place the result into.
      * @returns
      *     true if something was read, false if we have reached the end
      *     of the input.
      */
    virtual bool read_header(tar_header &hdr) = 0;

    /**
      * The read_header_padding is used to read any extra data between
      * headers.  By default, this method does nothing.
      */
    virtual void read_header_padding(void);

    /**
      * The filename method is used to obtain the name of the
      * input file (not the current archiove member file name).
      */
    virtual rcstring filename(void) const = 0;

    /**
      * The fatal method is used to print a fatal error message.
      * This method does not return.
      *
      * @param fmt
      *     The format of the output, see <i>printf</i>(3) for more
      *     information.
      */
    void fatal(const char *fmt, ...) const ATTR_PRINTF(2, 3);

    /**
      * The warning method is used to print a warning message.
      *
      * @param fmt
      *     The format of the output, see <i>printf</i>(3) for more
      *     information.
      */
    void warning(const char *fmt, ...) const ATTR_PRINTF(2, 3);

    /**
      * The get_format_name method may be used to obtain the name of the
      * file format in use.
      *
      * Derived classes shall make every attempt to return strings that
      * can be used with the tardy -ifmt command line option.
      */
    virtual const char *get_format_name(void) const = 0;

    /**
      * The get_format_family method is used to obtain an indication of
      * which format family the format belongs to.  This may be used to
      * inform default output formt selection.
      */
    virtual format_family_t get_format_family(void) const = 0;

    /**
      * The read_archive_begin method is used to read in any magic
      * numbers or fixed archive header before the archive members
      * start.  The default implementation does nothing.
      */
    virtual void read_archive_begin(void);

    /**
      * The read_archive_end method is used to read in any magic
      * numbers or fixed archive footer after the archive members
      * are done.  The default implementation does nothing.
      */
    virtual void read_archive_end(void);

    /**
      * The get_maximum_name_length method may be used to obtain the
      * longest name that may be stored in the archive (as opposed to
      * the longest name actually present in the archive).
      *
      * @returns
      *     The longest filename that may be present in the archive in
      *     bytes (<i>not</i> characters).
      *     A value of zero means "effectively infinite", or more
      *     accurately "how much memory do you have?".
      */
    virtual size_t get_maximum_name_length(void) const;

protected:
    /**
      * The default constructor.
      * For use by derived classes only.
      */
    tar_input();

    /**
      * The type_from_mode class method is used to map cpio(5) and ar(5)
      * mode bits into tar_header::type_ty values.
      *
      * @param mode
      *     file permission modes
      */
    static tar_header::type_ty type_from_mode(int mode);

private:
    /**
      * The copy constructor.  Do not use.
      *
      * @param rhs
      *     The right hand side of the initialization.
      */
    tar_input(const tar_input &rhs);

    /**
      * The assignment operator.  Do not use.
      *
      * @param rhs
      *     The right hand side of the assignment.
      */
    tar_input &operator=(const tar_input &rhs);
};

#endif // LIBTARDY_TAR_INPUT_H
