/** 
 * @file llfile.h
 * @author Michael Schlachter
 * @date 2006-03-23
 * @brief Declaration of cross-platform POSIX file buffer and c++
 * stream classes.
 *
 * $LicenseInfo:firstyear=2006&license=viewerlgpl$
 * Second Life Viewer Source Code
 * Copyright (C) 2010, Linden Research, Inc.
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License only.
 * 
 * This library 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
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 * 
 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 * $/LicenseInfo$
 */

#ifndef LL_LLFILE_H
#define LL_LLFILE_H

/**
 * This class provides a cross platform interface to the filesystem.
 * Attempts to mostly mirror the POSIX style IO functions.
 */

typedef FILE	LLFILE;

#include <fstream>
#include <sys/stat.h>

#if LL_WINDOWS
// windows version of stat function and stat data structure are called _stat
typedef struct _stat	llstat;
#else
typedef struct stat		llstat;
#include <ext/stdio_filebuf.h>
#include <bits/postypes.h>
#endif

#ifndef S_ISREG
# define S_ISREG(x) (((x) & S_IFMT) == S_IFREG)
#endif

#ifndef S_ISDIR
# define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
#endif

#include "llstring.h" // safe char* -> std::string conversion

class LL_COMMON_API LLFile
{
public:
	// All these functions take UTF8 path/filenames.
	static	LLFILE*	fopen(const std::string& filename,const char* accessmode);	/* Flawfinder: ignore */
	static	LLFILE*	_fsopen(const std::string& filename,const char* accessmode,int	sharingFlag);

	static	int		close(LLFILE * file);

	// perms is a permissions mask like 0777 or 0700.  In most cases it will
	// be overridden by the user's umask.  It is ignored on Windows.
	static	int		mkdir(const std::string& filename, int perms = 0700);

	static	int		rmdir(const std::string& filename);
	static	int		remove(const std::string& filename);
	static	int		rename(const std::string& filename,const std::string&	newname);
	static  bool	copy(const std::string from, const std::string to);

	static	int		stat(const std::string&	filename,llstat*	file_status);
	static	bool	isdir(const std::string&	filename);
	static	bool	isfile(const std::string&	filename);
	static	LLFILE *	_Fiopen(const std::string& filename, 
			std::ios::openmode mode);

	static  const char * tmpdir();
};

/**
 *  @brief Provides a layer of compatibility for C/POSIX.
 *
 *  This is taken from both the GNU __gnu_cxx::stdio_filebuf extension and 
 *  VC's basic_filebuf implementation.
 *  This file buffer provides extensions for working with standard C FILE*'s 
 *  and POSIX file descriptors for platforms that support this.
*/
namespace
{
#if LL_WINDOWS
typedef std::filebuf						_Myfb;
#else
typedef  __gnu_cxx::stdio_filebuf< char >	_Myfb;
typedef std::__c_file						_Filet;
#endif /* LL_WINDOWS */
}

class LL_COMMON_API llstdio_filebuf : public _Myfb
{
public:
	/**
	 * deferred initialization / destruction
	*/
	llstdio_filebuf() : _Myfb() {}
	virtual ~llstdio_filebuf() {} 

	/**
	 *  @param  f  An open @c FILE*.
	 *  @param  mode  Same meaning as in a standard filebuf.
	 *  @param  size  Optimal or preferred size of internal buffer, in chars.
	 *                Defaults to system's @c BUFSIZ.
	 *
	 *  This constructor associates a file stream buffer with an open
	 *  C @c FILE*.  The @c FILE* will not be automatically closed when the
	 *  stdio_filebuf is closed/destroyed.
	*/
	llstdio_filebuf(_Filet* __f, std::ios_base::openmode __mode,
		    //size_t __size = static_cast<size_t>(BUFSIZ)) :
		    size_t __size = static_cast<size_t>(1)) :
#if LL_WINDOWS
		_Myfb(__f) {}
#else
		_Myfb(__f, __mode, __size) {}
#endif

	/**
	 *  @brief  Opens an external file.
	 *  @param  s  The name of the file.
	 *  @param  mode  The open mode flags.
	 *  @return  @c this on success, NULL on failure
	 *
	 *  If a file is already open, this function immediately fails.
	 *  Otherwise it tries to open the file named @a s using the flags
	 *  given in @a mode.
	*/
	//llstdio_filebuf* open(const char *_Filename,
	//		std::ios_base::openmode _Mode);

	/**
	 *  @param  fd  An open file descriptor.
	 *  @param  mode  Same meaning as in a standard filebuf.
	 *  @param  size  Optimal or preferred size of internal buffer, in chars.
	 *
	 *  This constructor associates a file stream buffer with an open
	 *  POSIX file descriptor. The file descriptor will be automatically
	 *  closed when the stdio_filebuf is closed/destroyed.
	*/
#if !LL_WINDOWS
	llstdio_filebuf(int __fd, std::ios_base::openmode __mode,
		//size_t __size = static_cast<size_t>(BUFSIZ)) :
		size_t __size = static_cast<size_t>(1)) :
		_Myfb(__fd, __mode, __size) {}
#endif

// *TODO: Seek the underlying c stream for better cross-platform compatibility?
#if !LL_WINDOWS
protected:
	/** underflow() and uflow() functions are called to get the next
	 *  character from the real input source when the buffer is empty.
	 *  Buffered input uses underflow()
	*/
	/*virtual*/ int_type underflow();

	/*  Convert internal byte sequence to external, char-based
	 * sequence via codecvt.
	*/
	bool _convert_to_external(char_type*, std::streamsize);

	/** The overflow() function is called to transfer characters to the
	 *  real output destination when the buffer is full. A call to
	 *  overflow(c) outputs the contents of the buffer plus the
	 *  character c.
	 *  Consume some sequence of the characters in the pending sequence.
	*/
	/*virtual*/ int_type overflow(int_type __c = traits_type::eof());

	/** sync() flushes the underlying @c FILE* stream.
	*/
	/*virtual*/ int sync();

	std::streamsize xsgetn(char_type*, std::streamsize);
	std::streamsize xsputn(char_type*, std::streamsize);
#endif
};


/**
 *  @brief  Controlling input for files.
 *
 *  This class supports reading from named files, using the inherited
 *  functions from std::basic_istream.  To control the associated
 *  sequence, an instance of std::basic_filebuf (or a platform-specific derivative)
 *  which allows construction using a pre-exisintg file stream buffer. 
 *  We refer to this std::basic_filebuf (or derivative) as @c sb.
*/
class LL_COMMON_API llifstream	:	public	std::istream
{
	// input stream associated with a C stream
public:
	// Constructors:
	/**
	 *  @brief  Default constructor.
	 *
	 *  Initializes @c sb using its default constructor, and passes
	 *  @c &sb to the base class initializer.  Does not open any files
	 *  (you haven't given it a filename to open).
	*/
	llifstream();

	/**
	 *  @brief  Create an input file stream.
	 *  @param  Filename  String specifying the filename.
	 *  @param  Mode  Open file in specified mode (see std::ios_base).
	 *
     *  @c ios_base::in is automatically included in @a mode.
	*/
	explicit llifstream(const std::string& _Filename,
			ios_base::openmode _Mode = ios_base::in);
	explicit llifstream(const char* _Filename,
			ios_base::openmode _Mode = ios_base::in);

	/**
	 *  @brief  Create a stream using an open c file stream.
	 *  @param  File  An open @c FILE*.
        @param  Mode  Same meaning as in a standard filebuf.
        @param  Size  Optimal or preferred size of internal buffer, in chars.
                      Defaults to system's @c BUFSIZ.
	*/
	explicit llifstream(_Filet *_File,
			ios_base::openmode _Mode = ios_base::in,
			//size_t _Size = static_cast<size_t>(BUFSIZ));
			size_t _Size = static_cast<size_t>(1));
	
	/**
	 *  @brief  Create a stream using an open file descriptor.
	 *  @param  fd    An open file descriptor.
        @param  Mode  Same meaning as in a standard filebuf.
        @param  Size  Optimal or preferred size of internal buffer, in chars.
                      Defaults to system's @c BUFSIZ.
	*/
#if !LL_WINDOWS
	explicit llifstream(int __fd,
			ios_base::openmode _Mode = ios_base::in,
			//size_t _Size = static_cast<size_t>(BUFSIZ));
			size_t _Size = static_cast<size_t>(1));
#endif

	/**
	 *  @brief  The destructor does nothing.
	 *
	 *  The file is closed by the filebuf object, not the formatting
	 *  stream.
	*/
	virtual ~llifstream() {}

	// Members:
	/**
	 *  @brief  Accessing the underlying buffer.
	 *  @return  The current basic_filebuf buffer.
	 *
	 *  This hides both signatures of std::basic_ios::rdbuf().
	*/
	llstdio_filebuf* rdbuf() const
	{ return const_cast<llstdio_filebuf*>(&_M_filebuf); }

	/**
	 *  @brief  Wrapper to test for an open file.
	 *  @return  @c rdbuf()->is_open()
	*/
	bool is_open() const;

	/**
	 *  @brief  Opens an external file.
	 *  @param  Filename  The name of the file.
	 *  @param  Node  The open mode flags.
	 *
	 *  Calls @c llstdio_filebuf::open(s,mode|in).  If that function
	 *  fails, @c failbit is set in the stream's error state.
	*/
	void open(const std::string& _Filename,
			ios_base::openmode _Mode = ios_base::in)
	{ open(_Filename.c_str(), _Mode); }
	void open(const char* _Filename,
			ios_base::openmode _Mode = ios_base::in);

	/**
	 *  @brief  Close the file.
	 *
	 *  Calls @c llstdio_filebuf::close().  If that function
	 *  fails, @c failbit is set in the stream's error state.
	*/
	void close();

private:
	llstdio_filebuf _M_filebuf;
};


/**
 *  @brief  Controlling output for files.
 *
 *  This class supports writing to named files, using the inherited
 *  functions from std::basic_ostream.  To control the associated
 *  sequence, an instance of std::basic_filebuf (or a platform-specific derivative)
 *  which allows construction using a pre-exisintg file stream buffer. 
 *  We refer to this std::basic_filebuf (or derivative) as @c sb.
*/
class LL_COMMON_API llofstream	:	public	std::ostream
{
public:
	// Constructors:
	/**
	 *  @brief  Default constructor.
	 *
	 *  Initializes @c sb using its default constructor, and passes
	 *  @c &sb to the base class initializer.  Does not open any files
	 *  (you haven't given it a filename to open).
	*/
	llofstream();

	/**
	 *  @brief  Create an output file stream.
	 *  @param  Filename  String specifying the filename.
	 *  @param  Mode  Open file in specified mode (see std::ios_base).
	 *
	 *  @c ios_base::out|ios_base::trunc is automatically included in
	 *  @a mode.
	*/
	explicit llofstream(const std::string& _Filename,
			ios_base::openmode _Mode = ios_base::out|ios_base::trunc);
	explicit llofstream(const char* _Filename,
			ios_base::openmode _Mode = ios_base::out|ios_base::trunc);

	/**
	 *  @brief  Create a stream using an open c file stream.
	 *  @param  File  An open @c FILE*.
        @param  Mode  Same meaning as in a standard filebuf.
        @param  Size  Optimal or preferred size of internal buffer, in chars.
                      Defaults to system's @c BUFSIZ.
	*/
	explicit llofstream(_Filet *_File,
			ios_base::openmode _Mode = ios_base::out,
			//size_t _Size = static_cast<size_t>(BUFSIZ));
			size_t _Size = static_cast<size_t>(1));

	/**
	 *  @brief  Create a stream using an open file descriptor.
	 *  @param  fd    An open file descriptor.
        @param  Mode  Same meaning as in a standard filebuf.
        @param  Size  Optimal or preferred size of internal buffer, in chars.
                      Defaults to system's @c BUFSIZ.
	*/
#if !LL_WINDOWS
	explicit llofstream(int __fd,
			ios_base::openmode _Mode = ios_base::out,
			//size_t _Size = static_cast<size_t>(BUFSIZ));
			size_t _Size = static_cast<size_t>(1));
#endif

	/**
	 *  @brief  The destructor does nothing.
	 *
	 *  The file is closed by the filebuf object, not the formatting
	 *  stream.
	*/
	virtual ~llofstream() {}

	// Members:
	/**
	 *  @brief  Accessing the underlying buffer.
	 *  @return  The current basic_filebuf buffer.
	 *
	 *  This hides both signatures of std::basic_ios::rdbuf().
	*/
	llstdio_filebuf* rdbuf() const
	{ return const_cast<llstdio_filebuf*>(&_M_filebuf); }

	/**
	 *  @brief  Wrapper to test for an open file.
	 *  @return  @c rdbuf()->is_open()
	*/
	bool is_open() const;

	/**
	 *  @brief  Opens an external file.
	 *  @param  Filename  The name of the file.
	 *  @param  Node  The open mode flags.
	 *
	 *  Calls @c llstdio_filebuf::open(s,mode|out).  If that function
	 *  fails, @c failbit is set in the stream's error state.
	*/
	void open(const std::string& _Filename,
			ios_base::openmode _Mode = ios_base::out|ios_base::trunc)
	{ open(_Filename.c_str(), _Mode); }
	void open(const char* _Filename,
			ios_base::openmode _Mode = ios_base::out|ios_base::trunc);

	/**
	 *  @brief  Close the file.
	 *
	 *  Calls @c llstdio_filebuf::close().  If that function
	 *  fails, @c failbit is set in the stream's error state.
	*/
	void close();

private:
	llstdio_filebuf _M_filebuf;
};


/**
 * @breif filesize helpers.
 *
 * The file size helpers are not considered particularly efficient,
 * and should only be used for config files and the like -- not in a
 * loop.
 */
std::streamsize LL_COMMON_API llifstream_size(llifstream& fstr);
std::streamsize LL_COMMON_API llofstream_size(llofstream& fstr);

#endif // not LL_LLFILE_H