/*  Inti: Integrated Foundation Classes
 *  Copyright (C) 2003 The Inti Development Team.
 *
 *  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 2 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 Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

//! @file inti/glib/fileutils.h
//! @brief C++ interface for GDir and the various GLib file-related functions.

#ifndef INTI_G_FILEUTILS_H
#define INTI_G_FILEUTILS_H

#ifndef INTI_STACK_OBJECT_H
#include <inti/stackobject.h>
#endif

#ifndef INTI_UTF_STRING_H
#include <inti/utf-string.h>
#endif

#ifndef __G_DIR_H__
#include <glib/gdir.h>
#endif

#ifndef __G_FILEUTILS_H__
#include <glib/gfileutils.h>
#endif

#ifndef _CPP_VECTOR
#include <vector>
#endif

namespace Inti {

namespace G {

class Error;

//! @enum FileTest
//! Specifies a test to perform on a file using G::file_test().

enum FileTest
{
	FILE_TEST_IS_REGULAR = G_FILE_TEST_IS_REGULAR,
	//!< Tests true if the file is a regular file (not a symlink or directory).

	FILE_TEST_IS_SYMLINK = G_FILE_TEST_IS_SYMLINK,
	//!< Tests true if the file is a symlink.

	FILE_TEST_IS_DIR = G_FILE_TEST_IS_DIR,
	//!< Tests true if the file is a directory.

	FILE_TEST_IS_EXECUTABLE = G_FILE_TEST_IS_EXECUTABLE,
	//!< Tests true if the file is executable.

	FILE_TEST_EXISTS = G_FILE_TEST_EXISTS
	//!< Tests true if the file exists. It may or may not be a regular file.
};

//! FileTestField holds one or more bit values from the G::FileTest enumeration.

typedef unsigned int FileTestField;

//! @name File Methods
//! @{

GFileError file_error_from_errno(int err_no);
//!< Gets a GFileError constant based on the passed-in errno, for example,
//!< if you pass in EEXIST this function returns G_FILE_ERROR_EXIST.
//!< @param err_no An "errno" value
//!< @return A GFileError corresponding to the given errno.
//!<
//!< <BR>Unlike errno values, you can portably assume that all GFileError values
//!< will exist. Normally a GFileError value goes into a G::Error returned from
//!< a method that manipulates files. So you would use G::file_error_from_errno()
//!< when constructing a G::Error.

bool file_test(const std::string& filename, FileTestField test);
//!< Determines whether any of the tests in the bitfield test are true.
//!< @param filename A filename to test.
//!< @param test A bitfield of G::FileTest flags.
//!< @return <EM>true</EM> if a test was true.
//!<
//!< <BR>For example, (G::FILE_TEST_EXISTS | G::FILE_TEST_IS_DIR) will return true
//!< if the file exists; the check whether it's a directory doesn't matter since
//!< the existence test is true. With the current set of available tests, there's
//!< no point passing in more than one test at a time.
//!<
//!< Apart from G::FILE_TEST_IS_SYMLINK all tests follow symbolic links, so for a
//!< symbolic link to a regular file file_test() will return true for both
//!< G::FILE_TEST_IS_SYMLINK and G::FILE_TEST_IS_REGULAR. Note, that for a dangling
//!< symbolic link file_test() will return true for G::FILE_TEST_IS_SYMLINK and
//!< false for all other flags.
//!<
//!< You should never use file_test() to test whether it is safe to perform an
//!< operaton, because there is always the possibility of the condition changing
//!< before you actually perform the operation. For example, you might think you
//!< could use G::FILE_TEST_IS_SYMLINK to know whether it is is safe to write to
//!< a file without being tricked into writing into a different location. It
//!< doesn't work!
//!<
//!< <B>Example:</B> DON'T DO THIS.
//!< @code
//!< if (!file_test(filename, FILE_TEST_IS_SYMLINK))
//!< {
//!<	TempFile file(filename);
//!< 	if (file.is_open())
//!< 	// write to file
//!< }
//!< @endcode
//!<
//!< Another thing to note is that G::FILE_TEST_EXISTS and G::FILE_TEST_IS_EXECUTABLE
//!< are implemented using the access() system call. This usually doesn't matter, but
//!< if your program is setuid or setgid it means that these tests will give you the
//!< answer for the real user ID and group ID , rather than the effective user ID and
//!< group ID.

bool file_get_contents(const std::string& filename, std::string& contents, G::Error *error);
//!< Reads an entire file into allocated memory, with good error checking.
//!< @param filename A file to read the contents from.
//!< @param contents The location to store the file contents.
//!< @param error The return location for a G::Error.
//!< @return <EM>true</EM> on success, <EM>false</EM> if error is set.
//!<
//!< <BR>If error is set, false is returned, and contents is set to an empty string. If true
//!< is returned, error will not be set, and contents will be set to the file contents. The
//!< error domain is G_FILE_ERROR. Possible error codes are those in the GFileError enumeration.

//! @}

//! @class TempFile fileutils.h inti/glib/fileutils.h
//! A C++ wrapper interface for the g_file_open_tmp() and g_mkstemp() functions.
//!
//! TempFile is a convenient wrapper class for the g_file_open_tmp() and
//! g_mkstemp() functions. It is a StackObject and must created on the stack.
//! You can't allocate a TempFile dynamically.
//!
//! <B>Example 1:</B>
//! You can construct an empty temporary file using the default constructor
//! and then call either one of the open() methods or mkstemp().
//! @code
//! G::TempFile file;
//! std::string filename("/home/testfile");
//! if (file.mkstemp(filename))
//! {
//! 	// write to filename
//! }
//! file.close();
//! @endcode
//!
//! <B>Example 2:</B>
//! Alternatively you can construct and open a temporary file in one step by
//! calling one of the parameterized constructors.
//! @code
//! std::string filename;
//! G::Error error;
//! G::TempFile file("testfile", filename, &error);
//! if (file.is_open())
//! {
//! 	// write to filename
//! }
//! file.close();
//! @endcode
//!
//! You don't have to explicitly call TempFile::close() as above, unless you want to.
//! When a TempFile object goes out of scope its destructor will check for an open
//! file and close it.

class TempFile : public StackObject
{
	TempFile(const TempFile&);
	TempFile& operator=(const TempFile&);

	int file_descriptor_;

public:
//! @name Constructors
//! @{

	TempFile();
	//!< Construct an empty temporary file.

	TempFile(std::string& template_filename);
	//!< Construct and open a temporary file with the template filename <EM>template_filename</EM>
	//!< @param template_filename The template for the filename.
	//!<
	//!< <BR>See mkstemp() for details.

	TempFile(const std::string& template_filename, std::string& actual_filename, G::Error *error);
	//!< Construct and open a temporary file with the template filename <EM>template_filename</EM>
	//!< @param template_filename The prefix template for the file name, but basename only.
	//!< @param actual_filename The location to store actual filename used, includes full path.
	//!< @param error The return location for a G::Error, or null.
	//!<
	//!< <BR>See open() for details.

	~TempFile();
	//!< Destructor; Closes the temporary file if it's open.

//! @}
//! @name Accessors
//! @{

	bool is_open() const;
	//!< Determines whether the temporary file is open.
	//!< @return <EM>true</EM> if the file is open.

//! @}
//! @name Methods
//! @{

	bool open(std::string& actual_filename, G::Error *error);
	//!< Opens a file for writing in the preferred directory for temporary files (as returned by g_get_tmp_dir()).
	//!< @param actual_filename The location to store actual filename used, includes full path.
	//!< @param error The return location for a G::Error, or null.
	//!< @return <EM>true</EM> if the file was opened for reading and writing.
	//!<
	//!< <BR>This version of open() uses a default template (see open(const std::string&, std::string&, G::Error*)).

	bool open(const std::string& template_filename, std::string& actual_filename, G::Error *error);
	//!< Opens a file for writing in the preferred directory for temporary files (as returned by g_get_tmp_dir()).
	//!< @param template_filename The prefix template for the file name, but basename only.
	//!< @param actual_filename The location to store actual filename used, includes full path.
	//!< @param error The return location for a G::Error, or null.
	//!< @return <EM>true</EM> if the file was opened for reading and writing.
	//!<
	//!< <BR>The file is opened in binary mode on platforms where there is a difference.
	//!< If the file is not explicitly closed with close() it will automatically be
	//!< closed when the file goes out of scope. If an error occurs false is returned
	//!< and <EM>error</EM> will be set. Otherwise true is returned.
	//!<
	//!< Do not suffix <EM>template_filename</EM> with any 'X' characters, this method
	//!< will do that for you. Unlike mkstemp() <EM>template_filename</EM> should only
	//!< be a basename, no directory components are allowed. Note that in contrast to
	//!< mkstemp() <EM>template_filename</EM> is not modified, and might thus be a
	//!< read-only literal string. On return the <EM>actual_filename</EM> holds the
	//!< full path and filename of the temporary file.

	bool mkstemp(std::string& template_filename);
	//!< Opens a temporary file.
	//!< @param template_filename The template file name including any directory paths.
	//!< @return <EM>true</EM> if the file was opened for reading and writing.
	//!<
	//!< <BR>The file is opened in binary mode on platforms where there is a difference.
	//!< If the file is not explicitly closed with close() it will automatically be
	//!< closed when the file goes out of scope. If an error occurs false is returned
	//!< and <EM>error</EM> will be set. Otherwise true is returned.
	//!<
	//!< Do not suffix <EM>template_filename</EM> with any 'X' characters, this method
	//!< will do that for you. On returning <EM>template_filename</EM> will have been
	//!< modified to form the name of a file that didn't exist.

	bool close();
	//!< Closes an open temporary file.

//! @}
};

//! @class Dir fileutils.h inti/glib/fileutils.h
//! @brief A GDir C++ wrapper interface.
//!
//! Dir is an object that represents a file directory. Dir is a
//! StackObject, that is it must created on the stack. You can't
//! allocate Dir dynamically.
//!
//! <B>Example 1:</B>
//! You can construct an empty directory object using the default constructor
//! and then call open().
//! @code
//! G::Dir dir;
//! std::string dirname("/home");
//! if (dir.open(dirname))
//! {
//! 	// read directory
//! }
//! dir.close();
//! @endcode
//!
//! <B>Example 2:</B>
//! Alternatively you can construct and open a directory object in one step
//! by calling one of the parameterized constructors.
//! @code
//! std::string dirname;
//! G::Error error;
//! G::Dir dir(dirname, &error);
//! if (dir.is_open())
//! {
//! 	// read directory
//! }
//! dir.close();
//! @endcode
//!
//! You don't have to explicitly call Dir::close() as above, unless you want to.
//! When a directory object goes out of scope its destructor will check for an
//! open directory and close it.
//!
//! For convenience the entries in a directory can be read individually by
//! calling read_name() or at once by calling read_names(). The latter method
//! fills a user-supplied 'std::vector of String' with the entry names.

class Dir : public StackObject
{
	Dir(const Dir&);
	Dir& operator=(const Dir&);

	GDir *dir_;

public:
//! @name Constructors
//! @{

	Dir();
	//!< Constructs an empty directory object.

	Dir(const char *path, G::Error *error);
	//!< Constructs a directory object and opens the directory <EM>path</EM>.
	//!< @param path The path to the directory you are interested in.
	//!< @param error The return location for a G::Error, or null.
	//!<
	//!< <BR>If the directory <EM>path</EM> is successfully opened is_open()
	//!< returns true. If an error occurs is_open() returns false and <EM>error</EM>
	//!< will contain information about the error.

	Dir(const std::string& path, G::Error *error);
	//!< Constructs a directory object and opens the directory <EM>path</EM>.
	//!< @param path The path to the directory you are interested in.
	//!< @param error The return location for a G::Error, or null.
	//!<
	//!< <BR>If the directory <EM>path</EM> is successfully opened is_open()
	//!< returns true. If an error occurs is_open() returns false and <EM>error</EM>
	//!< will contain information about the error.

	~Dir();
	//!< Destructor;
	//!< Closes the directory if it's open.

//! @}
//! @name Accessors
//! @{

	bool is_open() const;
	//!< Determines whether the directory is open.
	//!< @return <EM>true</EM> if the directory is open.

//! @}
//! @name Methods
//! @{

	bool open(const char *path, G::Error *error);
	//!< Opens a directory for reading.
	//!< @param path The path to the directory you are interested in.
	//!< @param error The return location for a G::Error, or null.
	//!< @return <EM>true</EM> if this method is successful, <EM>false</EM> otherwise.
	//!<
	//!< <BR>The names of the files in the directory can then be retrieved using read_name().
	//!< If <EM>error</EM> is non-null, an error will be set if and only if this method fails.
	//!< If successful you can either explicitly call close() or let the dir object call close()
	//!< when it goes out of scope.

	const char* read_name();
	//!< Retrieves the name of the next entry in the directory (the '.' and '..' entries
	//!< are omitted).
	//!< @return The entry's name or null if there are no more entries.
	//!<
	//!< <BR>The return value is owned by GLib and must not be modified or freed.

	int read_names(std::vector<String>& names);
	//!< Gets a list of the names of all the entries in the directory (the '.' and
	//!< '..' entries are omitted).
	//!< @param names A reference to a vector of String to hold the names.
	//!< @return -1 on failure, otherwise an integer value indicating the degree of success.
	//!<
	//!< <BR>This method retrieves the name of each entry and calls String::from_filename()
	//!< to convert the string from the encoding used for filenames into a UTF-8 String.
	//!< If this method successfully converts all entry names the return value is 0. If
	//!< any entry names couldn't be converted the return value is the number of failed
	//!< conversions. If you want to know the entry names of all failed conversions you
	//!< should call read_name() instead.

	void rewind();
	//!< Resets the given directory so that the next call to read_name()
	//!< will return the first entry again.

	void close();
	//!< Closes the directory and deallocates all related resources.

//! @}
};

} // namespace G

} // namespace Inti

#endif // INTI_G_FILEUTILS_H














