/* $Id: fileutils.hg,v 1.3 2004/01/22 18:38:12 murrayc Exp $ */

/* Copyright (C) 2002 The gtkmm Development Team
 *
 * 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; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

_DEFS(glibmm,glib)

#ifndef DOXYGEN_SHOULD_SKIP_THIS
extern "C" { typedef struct _GDir GDir; }
#endif

#include <glibmm/error.h>
#include <iterator>
#include <string>

namespace Glib
{

_WRAP_ENUM(FileTest, GFileTest, NO_GTYPE)

/** @defgroup FileUtils File Utilities
 * Various file-related classes and functions.
 */

/** Exception class for file-related errors.
 * @ingroup FileUtils
 */
_WRAP_GERROR(FileError, GFileError, G_FILE_ERROR, NO_GTYPE,
    s#^EXIST$#EXISTS#,
    s#^ISDIR$#IS_DIRECTORY#,
    s#^ACCES$#ACCESS_DENIED#,
    s#^NAMETOOLONG$#NAME_TOO_LONG#,
    s#^NOENT$#NO_SUCH_ENTITY#,
    s#^NOTDIR$#NOT_DIRECTORY#,
    s#^NXIO$#NO_SUCH_DEVICE#,
    s#^NODEV$#NOT_DEVICE#,
    s#^ROFS$#READONLY_FILESYSTEM#,
    s#^TXTBSY$#TEXT_FILE_BUSY#,
    s#^FAULT$#FAULTY_ADDRESS#,
    s#^LOOP$#SYMLINK_LOOP#,
    s#^NOSPC$#NO_SPACE_LEFT#,
    s#^NOMEM$#NOT_ENOUGH_MEMORY#,
    s#^MFILE$#TOO_MANY_OPEN_FILES#,
    s#^NFILE$#FILE_TABLE_OVERFLOW#,
    s#^BADF$#BAD_FILE_DESCRIPTOR#,
    s#^INVAL$#INVALID_ARGUMENT#,
    s#^PIPE$#BROKEN_PIPE#,
    s#^AGAIN$#TRYAGAIN#,
    s#^INTR$#INTERRUPTED#,
    s#^IO$#IO_ERROR#,
    s#^PERM$#NOT_OWNER#
)

/** @enum FileError::Code
 * Values corresponding to <tt>errno</tt> codes returned from file operations
 * on UNIX.
 * Unlike <tt>errno</tt> codes, FileError::Code values are available on all
 * systems, even Windows. The exact meaning of each code depends on what sort
 * of file operation you were performing; the UNIX documentation gives more
 * details.  The following error code descriptions come from the GNU C Library
 * manual, and are under the copyright of that manual.
 *
 * It's not very portable to make detailed assumptions about exactly which
 * errors will be returned from a given operation. Some errors don't occur on
 * some systems, etc., sometimes there are subtle differences in when a system
 * will report a given error, etc.
 */

/** @var FileError::Code FileError::EXISTS
 * <tt>(EEXIST)</tt> Operation not permitted; only the owner of the file (or
 * other resource) or processes with special privileges can perform the operation.
 * <br><br>
 */
/** @var FileError::Code FileError::IS_DIRECTORY
 * <tt>(EISDIR)</tt> File is a directory; you cannot open a directory for writing,
 * or create or remove hard links to it.
 * <br><br>
 */
/** @var FileError::Code FileError::ACCESS_DENIED
 * <tt>(EACCES)</tt> Permission denied; the file permissions do not allow the
 * attempted operation.
 * <br><br>
 */
/** @var FileError::Code FileError::NAME_TOO_LONG
 * <tt>(ENAMETOOLONG)</tt> Filename too long.
 * <br><br>
 */
/** @var FileError::Code FileError::NO_SUCH_ENTITY
 * <tt>(ENOENT)</tt> No such file or directory.  This is a "file doesn't exist"
 * error for ordinary files that are referenced in contexts where they are expected
 * to already exist.
 * <br><br>
 */
/** @var FileError::Code FileError::NOT_DIRECTORY
 * <tt>(ENOTDIR)</tt> A file that isn't a directory was specified when a directory
 * is required.
 * <br><br>
 */
/** @var FileError::Code FileError::NO_SUCH_DEVICE
 * <tt>(ENXIO)</tt> No such device or address.  The system tried to use the device
 * represented by a file you specified, and it couldn't find the device. This can
 * mean that the device file was installed incorrectly, or that the physical device
 * is missing or not correctly attached to the computer.
 * <br><br>
 */
/** @var FileError::Code FileError::NOT_DEVICE
 * <tt>(ENODEV)</tt> This file is of a type that doesn't support mapping.
 * <br><br>
 */
/** @var FileError::Code FileError::READONLY_FILESYSTEM
 * <tt>(EROFS)</tt> The directory containing the new link can't be modified
 * because it's on a read-only file system.
 * <br><br>
 */
/** @var FileError::Code FileError::TEXT_FILE_BUSY
 * <tt>(ETXTBSY)</tt> Text file busy.
 * <br><br>
 */
/** @var FileError::Code FileError::FAULTY_ADDRESS
 * <tt>(EFAULT)</tt> You passed in a pointer to bad memory.  (Glib won't
 * reliably return this, don't pass in pointers to bad memory.)
 * <br><br>
 */
/** @var FileError::Code FileError::SYMLINK_LOOP
 * <tt>(ELOOP)</tt> Too many levels of symbolic links were encountered in
 * looking up a file name.  This often indicates a cycle of symbolic links.
 * <br><br>
 */
/** @var FileError::Code FileError::NO_SPACE_LEFT
 * <tt>(ENOSPC)</tt> No space left on device; write operation on a file failed
 * because the disk is full.
 * <br><br>
 */
/** @var FileError::Code FileError::NOT_ENOUGH_MEMORY
 * <tt>(ENOMEM)</tt> No memory available.  The system cannot allocate more
 * virtual memory because its capacity is full.
 * <br><br>
 */
/** @var FileError::Code FileError::TOO_MANY_OPEN_FILES
 * <tt>(EMFILE)</tt> The current process has too many files open and can't
 * open any more.  Duplicate descriptors do count toward this limit.
 * <br><br>
 */
/** @var FileError::Code FileError::FILE_TABLE_OVERFLOW
 * <tt>(ENFILE)</tt> There are too many distinct file openings in the
 * entire system.
 * <br><br>
 */
/** @var FileError::Code FileError::BAD_FILE_DESCRIPTOR
 * <tt>(EBADF)</tt> Bad file descriptor; for example, I/O on a descriptor
 * that has been closed or reading from a descriptor open only for writing
 * (or vice versa).
 * <br><br>
 */
/** @var FileError::Code FileError::INVALID_ARGUMENT
 * <tt>(EINVAL)</tt> Invalid argument. This is used to indicate various kinds
 * of problems with passing the wrong argument to a library function.
 * <br><br>
 */
/** @var FileError::Code FileError::BROKEN_PIPE
 * <tt>(EPIPE)</tt> Broken pipe; there is no process reading from the other
 * end of a pipe.  Every library function that returns this error code also
 * generates a <tt>SIGPIPE</tt> signal; this signal terminates the program
 * if not handled or blocked.  Thus, your program will never actually see
 * this code unless it has handled or blocked <tt>SIGPIPE</tt>.
 * <br><br>
 */
/** @var FileError::Code FileError::TRYAGAIN
 * <tt>(EAGAIN)</tt> Resource temporarily unavailable; the call might work
 * if you try again later.
 * We used TRYAGAIN instead of TRY_AGAIN, because that is a defined as a macro by a Unix header.
 * <br><br>
 */
/** @var FileError::Code FileError::INTERRUPTED
 * <tt>(EINTR)</tt> Interrupted function call; an asynchronous signal occurred
 * and prevented completion of the call.  When this happens, you should try
 * the call again.
 * <br><br>
 */
/** @var FileError::Code FileError::IO_ERROR
 * <tt>(EIO)</tt> Input/output error; usually used for physical read or write
 * errors.  I.e. the disk or other physical device hardware is returning errors.
 * <br><br>
 */
/** @var FileError::Code FileError::NOT_OWNER
 * <tt>(EPERM)</tt> Operation not permitted; only the owner of the file (or other
 * resource) or processes with special privileges can perform the operation.
 * <br><br>
 */
/** @var FileError::Code FileError::FAILED
 * Does not correspond to a UNIX error code; this is the standard "failed for
 * unspecified reason" error code present in all Glib::Error error code
 * enumerations.  Returned if no specific code applies.
 */

class Dir;

/** The iterator type of Glib::Dir.
 * @ingroup FileUtils
 */
class DirIterator
{
public:
  typedef std::input_iterator_tag   iterator_category;
  typedef std::string               value_type;
  typedef int                       difference_type;
  typedef value_type                reference;
  typedef void                      pointer;

  DirIterator();

#ifndef DOXYGEN_SHOULD_SKIP_THIS
  DirIterator(GDir* gobject, const char* current);
#endif

  std::string  operator*() const;
  DirIterator& operator++();

  /** @note DirIterator has input iterator semantics, which means real
   * postfix increment is impossible. The return type is @c void to
   * prevent surprising behaviour.
   */
  void operator++(int);

  bool operator==(const DirIterator& rhs) const;
  bool operator!=(const DirIterator& rhs) const;

private:
  GDir*       gobject_;
  const char* current_;
};


/** Utility class representing an open directory.
 * @ingroup FileUtils
 * It's highly recommended to use the iterator interface.  With iterators,
 * reading an entire directory into a STL container is really easy:
 * @code
 * Glib::Dir dir (directory_path);
 * std::list<std::string> entries (dir.begin(), dir.end());
 * @endcode
 * @note The encoding of the directory entries isn't necessarily UTF-8.
 * Use Glib::filename_to_utf8() if you need to display them.
 */
class Dir
{
public:
  typedef DirIterator iterator;
  typedef DirIterator const_iterator;

  /** Opens a directory for reading. The names of the files in the
   * directory can then be retrieved using read_name().
   * @param path The path to the directory you are interested in.
   * @throw Glib::FileError
   */
  explicit Dir(const std::string& path);

#ifndef DOXYGEN_SHOULD_SKIP_THIS
  explicit Dir(GDir* gobject);
#endif

  /** Closes the directory and deallocates all related resources.
   */
  ~Dir();

  /** Retrieves the name of the next entry in the directory.
   * The <tt>'.'</tt> and <tt>'..'</tt> entries are omitted.
   * @return The entry's name or <tt>""</tt> if there are no more entries.
   * @see begin(), end()
   */
  std::string read_name();

  /** Resets the directory.  The next call to
   * read_name() will return the first entry again.
   */
  void rewind();

  /** Closes the directory and deallocates all related resources.
   * Note that close() is implicitely called by ~Dir().  Thus you don't
   * need to call close() yourself unless you want to close the directory
   * before the destructor runs.
   */
  void close();

  /** Get the begin of an input iterator sequence.
   * @return An input iterator pointing to the first directory entry.
   */
  DirIterator begin();

  /** Get the end of an input iterator sequence.
   * @return An input iterator pointing behind the last directory entry.
   */
  DirIterator end();

private:
  GDir* gobject_;

  // noncopyable
  Dir(const Dir&);
  Dir& operator=(const Dir&);
};


/** Returns @c true if any of the tests in the bitfield @a test are true.
 * @ingroup FileUtils
 * For example, <tt>(Glib::FILE_TEST_EXISTS | Glib::FILE_TEST_IS_DIR)</tt> will
 * return @c 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 <tt>Glib::FILE_TEST_IS_SYMLINK</tt> all tests follow symbolic
 * links, so for a symbolic link to a regular file file_test() will return
 * @c true for both <tt>Glib::FILE_TEST_IS_SYMLINK</tt> and
 * <tt>Glib::FILE_TEST_IS_REGULAR</tt>.
 *
 * @note For a dangling symbolic link file_test() will return @c true for
 * <tt>Glib::FILE_TEST_IS_SYMLINK</tt> and @c false for all other flags.
 *
 * @param filename A filename to test.
 * @param test Bitfield of Glib::FileTest flags.
 * @return Whether a test was true.
 */
bool file_test(const std::string& filename, FileTest test);

/** Opens a temporary file.
 * @ingroup FileUtils
 * See the %mkstemp() documentation on most UNIX-like systems. This is a
 * portability wrapper, which simply calls %mkstemp() on systems that have
 * it, and implements it in GLib otherwise.
 * @param filename_template A string that should match the rules for
 *   %mkstemp(), i.e. end in <tt>"XXXXXX"</tt>. The <tt>X</tt> string
 *   will be modified to form the name of a file that didn't exist.
 * @return A file handle (as from open()) to the file opened for reading
 *   and writing. The file is opened in binary mode on platforms where there
 *   is a difference. The file handle should be closed with close(). In
 *   case of errors, <tt>-1</tt> is returned.
 */
int mkstemp(std::string& filename_template);

/** Opens a file for writing in the preferred directory for temporary files
 * (as returned by Glib::get_tmp_dir()).
 * @ingroup FileUtils
 * @a prefix should a basename template; it'll be suffixed by 6 characters
 * in order to form a unique filename.  No directory components are allowed.
 *
 * The actual name used is returned in @a name_used.
 *
 * @param prefix Template for file name, basename only.
 * @retval name_used The actual name used.
 * @return A file handle (as from <tt>open()</tt>) to the file opened for reading
 * and writing. The file is opened in binary mode on platforms where there is a
 * difference. The file handle should be closed with <tt>close()</tt>.
 * @throw Glib::FileError
 */
int file_open_tmp(std::string& name_used, const std::string& prefix);

/** Opens a file for writing in the preferred directory for temporary files
 * (as returned by Glib::get_tmp_dir()).
 * @ingroup FileUtils
 * This function works like file_open_tmp(std::string&, const std::string&)
 * but uses a default basename prefix.
 *
 * @retval name_used The actual name used.
 * @return A file handle (as from <tt>open()</tt>) to the file opened for reading
 * and writing. The file is opened in binary mode on platforms where there is a
 * difference. The file handle should be closed with <tt>close()</tt>.
 * @throw Glib::FileError
 */
int file_open_tmp(std::string& name_used);

/** Reads an entire file into a string, with good error checking.
 * @ingroup FileUtils
 * @param filename A file to read contents from.
 * @return The file contents.
 * @throw Glib::FileError
 */
std::string file_get_contents(const std::string& filename);

/** Writes all of @a contents to a file named @a filename, with good error checking.
 *
 * @param filename name of a file to write @a contents to, in the GLib file name
 *   encoding
 * @param contents string to write to the file
 * @param length length of @a contents, or -1 if @a contents is a nul-terminated string
 *
 * If a file called @a filename already exists it will be overwritten.
 *
 * This write is atomic in the sense that it is first written to a temporary
 * file which is then renamed to the final name. Notes:
 * <ol>
 * <li>
 *    On Unix, if @a filename already exists hard links to @filename will break.
 *    Also since the file is recreated, existing permissions, access control
 *    lists, metadata etc. may be lost. If @filename is a symbolic link,
 *    the link itself will be replaced, not the linked file.
 * </li>
 * <li>
 *   On Windows renaming a file will not remove an existing file with the
 *   new name, so on Windows there is a race condition between the existing
 *   file being removed and the temporary file being renamed.
 * </li>
 * <li>
 *   On Windows there is no way to remove a file that is open to some
 *   process, or mapped into memory. Thus, this function will fail if
 *   @filename already exists and is open.
 * </li>
 * </ol>
 *
 * If the call was not successful, an exception is thrown.
 * Possible error codes are those in the FileError enumeration.
 *
 * @newin{2,22}
 **/
void file_set_contents (const std::string& filename, const gchar *contents, gssize length);
/** A variant of file_set_contents which accepts a standard C++ string
 *
 * @newin{2,22}
 * */
void file_set_contents (const std::string& filename, const std::string& contents);

// For some reason gmmproc thinks that g_iconv should be wrapped here.
_IGNORE(g_iconv)

} // namespace Glib
