view modules/fs/fs.hpp @ 565:a1841ba6eb61

Net: various documentation fixes
author David Demelier <markand@malikania.fr>
date Thu, 23 Jun 2016 13:50:27 +0200
parents 730358cb0648
children 67da1bacd884
line wrap: on
line source

/*
 * fs.hpp -- filesystem operations
 *
 * Copyright (c) 2013-2016 David Demelier <markand@malikania.fr>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#ifndef FS_HPP
#define FS_HPP

/**
 * \file fs.hpp
 * \brief Filesystem operations made easy.
 */

/**
 * \page filesystem Filesystem
 * \brief Filesystem support
 *
 * ## Options
 *
 * The following options can be set by the user:
 *
 *   - **FS_HAVE_STAT**: (bool) Set to true if sys/stat.h and stat function are available, automatically detected.
 *
 * ## Export macros
 *
 * You must define `FS_DLL` globally and `FS_BUILDING_DLL` when compiling the library if you want a DLL, alternatively you can provide
 * your own `FS_EXPORT` macro instead.
 */

/**
 * \cond FS_HIDDEN_SYMBOLS
 */

#if !defined(FS_HAVE_STAT)
#   if defined(_WIN32)
#       define FS_HAVE_STAT
#   elif defined(__linux__)
#       define FS_HAVE_STAT
#   elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__)
#       define FS_HAVE_STAT
#   elif defined(__APPLE__)
#       define FS_HAVE_STAT
#   endif
#endif

#if !defined(FS_EXPORT)
#   if defined(FS_DLL)
#       if defined(_WIN32)
#           if defined(FS_BUILDING_DLL)
#               define FS_EXPORT __declspec(dllexport)
#           else
#               define FS_EXPORT __declspec(dllimport)
#           endif
#       else
#           define FS_EXPORT
#       endif
#   else
#       define FS_EXPORT
#   endif
#endif

/**
 * \endcond
 */

#if defined(FS_HAVE_STAT)
#   include <sys/stat.h>
#endif

#include <regex>
#include <string>
#include <vector>

/**
 * \brief Filesystem namespace.
 */
namespace fs {

/**
 * \enum Flags
 * \brief Flags for readdir.
 */
enum Flags {
    Dot     = (1 << 0),         //!< if set, also lists "."
    DotDot  = (1 << 1)          //!< if set, also lists ".."
};

/**
 * \class Entry
 * \brief Entry in the directory list.
 */
class Entry {
public:
    /**
     * \brief Describe the type of an entry
     */
    enum Type : char {
        Unknown,                //!< File type is unknown,
        File,                   //!< File is regular type,
        Dir,                    //!< File is directory,
        Link                    //!< File is link
    };

    std::string name;           //!< name of entry (base name)
    Type type{Unknown};         //!< type of file
};

/**
 * Check if two entries are identical.
 *
 * \param e1 the first entry
 * \param e2 the second entry
 * \return true if they are identical
 */
inline bool operator==(const Entry &e1, const Entry &e2) noexcept
{
    return e1.name == e2.name && e1.type == e2.type;
}

/**
 * Check if two entries are different.
 *
 * \param e1 the first entry
 * \param e2 the second entry
 * \return true if they are different
 */
inline bool operator!=(const Entry &e1, const Entry &e2) noexcept
{
    return !(e1 == e2);
}

/**
 * Get the separator for that system.
 *
 * \return \ on Windows and / otherwise
 */
inline char separator() noexcept
{
#if defined(_WIN32)
    return '\\';
#else
    return '/';
#endif
}

/**
 * Clean a path by removing any extra / or \ and add a trailing one.
 *
 * \param path the path
 * \return the updated path
 */
FS_EXPORT std::string clean(std::string path);

/**
 * Get the base name from a path.
 *
 * Example, baseName("/etc/foo.conf") // foo.conf
 *
 * \param path the path
 * \return the base name
 */
FS_EXPORT std::string baseName(std::string path);

/**
 * Get the parent directory from a path.
 *
 * Example, dirName("/etc/foo.conf") // /etc
 *
 * \param path the path
 * \return the parent directory
 */
FS_EXPORT std::string dirName(std::string path);

#if defined(FS_HAVE_STAT)

/**
 * Get stat information.
 *
 * \param path the path
 * \return the stat information
 * \throw std::runtime_error on failure
 */
FS_EXPORT struct stat stat(const std::string &path);

#endif // !HAVE_STAT

/**
 * Check if a file exists.
 *
 * If HAVE_ACCESS is defined, the function access is used, otherwise stat is used.
 *
 * \param path the path to check
 * \return true if the path exists
 */
FS_EXPORT bool exists(const std::string &path) noexcept;

/**
 * Check if the path is absolute.
 *
 * \param path the path
 * \return true if the path is absolute
 */
FS_EXPORT bool isAbsolute(const std::string &path) noexcept;

/**
 * Check if the path is relative.
 *
 * \param path the path
 * \return true if the path is absolute
 */
FS_EXPORT bool isRelative(const std::string &path) noexcept;

/**
 * Check if the file is readable.
 *
 * \param path the path
 * \return true if has read access
 */
FS_EXPORT bool isReadable(const std::string &path) noexcept;

/**
 * Check if the file is writable.
 *
 * \param path the path
 * \return true if has write access
 */
FS_EXPORT bool isWritable(const std::string &path) noexcept;

/**
 * Check if the file is a regular file.
 *
 * \param path the path
 * \return true if it is a file and false if not or not readable
 * \throw std::runtime_error if the operation is not supported
 */
FS_EXPORT bool isFile(const std::string &path);

/**
 * Check if the file is a directory.
 *
 * \param path the path
 * \return true if it is a directory and false if not or not readable
 * \throw std::runtime_error if the operation is not supported
 */
FS_EXPORT bool isDirectory(const std::string &path);

/**
 * Check if the file is a symbolic link.
 *
 * \param path the path
 * \return true if it is a symbolic link and false if not or not readable
 * \throw std::runtime_error if the operation is not supported
 */
FS_EXPORT bool isSymlink(const std::string &path);

/**
 * Read a directory and return a list of entries (not recursive).
 *
 * \param path the directory path
 * \param flags the optional flags (see Flags)
 * \return the list of entries
 * \throw std::runtime_error on failure
 */
FS_EXPORT std::vector<Entry> readdir(const std::string &path, int flags = 0);

/**
 * Create a directory recursively.
 *
 * \param path the path
 * \param mode the optional mode (not always supported)
 * \throw std::runtime_error on failure
 * \post all intermediate directories are created
 */
FS_EXPORT void mkdir(const std::string &path, int mode = 0700);

/**
 * Remove a directory recursively.
 *
 * If errors happens, they are silently discarded to remove as much as possible.
 *
 * \param path the path
 */
FS_EXPORT void rmdir(const std::string &path) noexcept;

/**
 * Search an item recursively.
 *
 * The predicate must have the following signature:
 *  void f(const std::string &base, const Entry &entry)
 *
 * Where:
 *   - base is the current parent directory in the tree
 *   - entry is the current entry
 *
 * \param base the base directory
 * \param predicate the predicate
 * \return the full path name to the file or empty string if never found
 * \throw std::runtime_error on read errors
 */
template <typename Predicate>
std::string findIf(const std::string &base, Predicate &&predicate)
{
    /*
     * Do not go deeply to the tree before testing all files in the current directory for performances reasons, we iterate
     * this directory to search for the entry name and iterate again over all sub directories if not found.
     */
    std::string path;
    std::vector<Entry> entries = readdir(base);

    for (const auto &entry : entries) {
        if (predicate(base, entry)) {
            path = base + separator() + entry.name;
            break;
        }
    }

    if (!path.empty())
        return path;

    for (const auto &entry : entries) {
        if (entry.type != Entry::Dir)
            continue;

        path = findIf(base + separator() + entry.name, std::forward<Predicate>(predicate));

        if (!path.empty())
            break;
    }

    return path;
}

/**
 * Find a file by name recursively.
 *
 * \param base the base directory
 * \param name the file name
 * \return the full path name to the file or empty string if never found
 * \throw std::runtime_error on read errors
 */
inline std::string find(const std::string &base, const std::string &name)
{
    return findIf(base, [&] (const auto &, const auto &entry) { return entry.name == name; });
}

/**
 * Overload by regular expression.
 *
 * \param base the base directory
 * \param regex the regular expression
 * \return the full path name to the file or empty string if never found
 * \throw std::runtime_error on read errors
 */
inline std::string find(const std::string &base, const std::regex &regex)
{
    return findIf(base, [&] (const auto &, const auto &entry) { return std::regex_match(entry.name, regex); });
}

/**
 * Get the current working directory.
 *
 * \return the current working directory
 * \throw std::runtime_error on failure
 */
FS_EXPORT std::string cwd();

} // !fs

#endif // !FS_HPP