view libcommon/irccd/xdg.hpp @ 628:27587ff92a64

Misc: update copyrights
author David Demelier <markand@malikania.fr>
date Tue, 09 Jan 2018 14:06:40 +0100
parents 4f5f306d13ac
children
line wrap: on
line source

/*
 * xdg.hpp -- XDG directory specifications
 *
 * Copyright (c) 2013-2018 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 IRCCD_XDG_HPP
#define IRCCD_XDG_HPP

/**
 * \file xdg.hpp
 * \brief XDG directory specifications.
 * \author David Demelier <markand@malikana.fr>
 */

#include <cstdlib>
#include <sstream>
#include <stdexcept>
#include <string>
#include <vector>

namespace irccd {

/**
 * \brief XDG directory specifications.
 *
 * Read and get XDG directories.
 *
 * This file should compiles on Windows to facilitate portability but its
 * functions must not be used.
 */
class xdg {
private:
    std::string config_home_;
    std::string data_home_;
    std::string cache_home_;
    std::string runtime_dir_;
    std::vector<std::string> config_dirs_;
    std::vector<std::string> data_dirs_;

    inline bool is_absolute(const std::string& path) const noexcept
    {
        return path.length() > 0 && path[0] == '/';
    }

    std::vector<std::string> split(const std::string& arg) const
    {
        std::stringstream iss(arg);
        std::string item;
        std::vector<std::string> elems;

        while (std::getline(iss, item, ':')) {
            if (is_absolute(item))
                elems.push_back(item);
        }

        return elems;
    }

    std::string env_or_home(const std::string& var, const std::string& repl) const
    {
        auto value = std::getenv(var.c_str());

        if (value == nullptr || !is_absolute(value)) {
            auto home = std::getenv("HOME");

            if (home == nullptr)
                throw std::runtime_error("could not get home directory");

            return std::string(home) + "/" + repl;
        }

        return value;
    }

    std::vector<std::string> list_or_defaults(const std::string& var,
                                              const std::vector<std::string>& list) const
    {
        auto value = std::getenv(var.c_str());

        if (!value)
            return list;

        // No valid item at all? Use defaults.
        auto result = split(value);

        return (result.size() == 0) ? list : result;
    }

public:
    /**
     * Open an xdg instance and load directories.
     *
     * \throw std::runtime_error on failures
     */
    xdg()
    {
        config_home_    = env_or_home("XDG_CONFIG_HOME", ".config");
        data_home_      = env_or_home("XDG_DATA_HOME", ".local/share");
        cache_home_     = env_or_home("XDG_CACHE_HOME", ".cache");

        config_dirs_    = list_or_defaults("XDG_CONFIG_DIRS", { "/etc/xdg" });
        data_dirs_      = list_or_defaults("XDG_DATA_DIRS", { "/usr/local/share", "/usr/share" });

        /*
         * Runtime directory is a special case and does not have a replacement,
         * the application should manage this by itself.
         */
        auto runtime = std::getenv("XDG_RUNTIME_DIR");

        if (runtime && is_absolute(runtime))
            runtime_dir_ = runtime;
    }

    /**
     * Get the config directory. ${XDG_CONFIG_HOME} or ${HOME}/.config
     *
     * \return the config directory
     */
    inline const std::string& config_home() const noexcept
    {
        return config_home_;
    }

    /**
     * Get the data directory. ${XDG_DATA_HOME} or ${HOME}/.local/share
     *
     * \return the data directory
     */
    inline const std::string& data_home() const noexcept
    {
        return data_home_;
    }

    /**
     * Get the cache directory. ${XDG_CACHE_HOME} or ${HOME}/.cache
     *
     * \return the cache directory
     */
    inline const std::string& cache_home() const noexcept
    {
        return cache_home_;
    }

    /**
     * Get the runtime directory.
     *
     * There is no replacement for XDG_RUNTIME_DIR, if it is not set, an empty
     * value is returned and the user is responsible of using something else.
     *
     * \return the runtime directory
     */
    inline const std::string& runtime_dir() const noexcept
    {
        return runtime_dir_;
    }

    /**
     * Get the standard config directories. ${XDG_CONFIG_DIRS} or { "/etc/xdg" }
     *
     * \return the list of config directories
     */
    inline const std::vector<std::string>& config_dirs() const noexcept
    {
        return config_dirs_;
    }

    /**
     * Get the data directories. ${XDG_DATA_DIRS} or { "/usr/local/share",
     * "/usr/share" }
     *
     * \return the list of data directories
     */
    inline const std::vector<std::string>& data_dirs() const noexcept
    {
        return data_dirs_;
    }
};

} // !irccd

#endif // !IRCCD_XDG_HPP