<div dir="ltr"><div dir="ltr"><br></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Jun 3, 2020 at 3:22 AM 魏艳艳 <<a href="mailto:wyygrowing1224@163.com">wyygrowing1224@163.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div style="line-height:1.7;color:rgb(0,0,0);font-size:14px;font-family:Arial"><div style="margin:0px">Dear!</div><div style="margin:0px">    I've tried to write configurations using UCI API in my projects, but they often cause my processes to crash, I don't know why?I hope you can help me。</div><div style="margin:0px">1、platform:X86</div><div style="margin:0px">This is the information that my Program crashed</div><div style="margin:0px"><span style="color:rgb(46,48,51);font-family:Arial,"Microsoft YaHei",微软雅黑,宋体,"Malgun Gothic",Meiryo,sans-serif;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:20px;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(238,240,242);display:inline;float:none">(gdb) where<br>#0  0x00007fef72046277 in raise () from /lib64/libc.so.6<br>#1  0x00007fef72047968 in abort () from /lib64/libc.so.6<br>#2  0x00007fef72088d37 in __libc_message () from /lib64/libc.so.6<br>#3  0x00007fef72091499 in _int_free () from /lib64/libc.so.6<br>#4  0x00007fef723df3ef in uci_free_element (e=0xa63d50) at /home/uci/list.c:72<br>#5  0x00007fef723df573 in uci_free_package (package=package@entry=0xa63380) at /home/uci/list.c:283<br>#6  0x00007fef723dfb0d in uci_cleanup (ctx=0xa64300) at /home/uci/libuci.c:126<br>#7  0x00007fef723dfb6b in uci_free_context (ctx=0xa64300) at /home/uci/libuci.c:80<br>#8  0x0000000000402876 in </span><span style="font-family:Arial,"Microsoft YaHei",微软雅黑,宋体,"Malgun Gothic",Meiryo,sans-serif;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:20px;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(238,240,242);display:inline;float:none;color:rgb(221,64,50)">uci_set_value </span><span style="color:rgb(46,48,51);font-family:Arial,"Microsoft YaHei",微软雅黑,宋体,"Malgun Gothic",Meiryo,sans-serif;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:20px;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(238,240,242);display:inline;float:none">(path=0x40cf59 "/tmp/DataCollection/MODMAN", config=0x40cf51 "rundata", section=0x40cf43 "modmanruninfo", <br>    option=0x40cfbf "akMODMANVTxMuteOpenAMIP", value=0x61b780 <g_Mcastinfo> "0") at ft_uci.c:446<br>#9  0x0000000000403dec in Write_Mcast_Data (def=1) at ft_daq.c:166<br>#10 0x000000000040426e in main (argc=1, argv=0x7ffe84fa78c8) at ft_daq.c:264</span></div><div style="margin:0px"><span style="color:rgb(46,48,51);font-family:Arial,"Microsoft YaHei",微软雅黑,宋体,"Malgun Gothic",Meiryo,sans-serif;font-size:12px;font-style:normal;font-variant:normal;font-weight:normal;letter-spacing:normal;line-height:20px;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;background-color:rgb(238,240,242);display:inline;float:none"><br></span></div><div style="margin:0px">2、This is the function that writes the configuration file,My program calls uci_set_value function 20 times for 1s.<br>/*****************************************************************/<br>void uci_set_value(char *path, char *config, char *section, char *option, char *value)<br>{<br>    struct uci_context *ctx = NULL;<br>    struct uci_ptr ptr;<br>    int ret = UCI_OK;<br>    char str[128] = {0};<br>    char filepath[128] = {0};<br><br>    ctx = uci_alloc_context();<br>    if (!ctx)<br>    {<br>        ULOG_ERR("uci_alloc_context error, config=%s,section=%s,option=%s,value=%s\n", config, section, option, value);<br>        return;<br>    }<br>    if (NULL != path)<br>    {<br>        uci_set_confdir(ctx, path);<br>    }<br>    <br>    memset(&ptr, 0, sizeof(ptr));<br>    memset(str, 0 , sizeof(str));<br>    sprintf(str, "%s.%s.%s=%s", config, section, option, value);<br>    <br>    if (uci_lookup_ptr(ctx, &ptr, str, true) != 0) <br>    {<br>        ULOG_ERR("uci_lookup_ptr error, str=%s\n", str);<br>        uci_free_context(ctx);<br>        return;<br>    }<br>    memset(filepath, 0, sizeof(filepath));<br>    sprintf(filepath, "%s/%s", path, config);<br> <br>    if ((0 != strcmp(ctx->confdir, path)) || (0 != strcmp(filepath, ptr.p->path)))<br>    {<br>        if (ptr.p)<br>        {<br>            uci_unload(ctx, ptr.p);    <br>        }<br>        uci_free_context(ctx);<br>        return;<br>    }<br>    <br>    ret = uci_set(ctx, &ptr);<br>    if (0 == ret)<br>    {<br>        ret = uci_commit(ctx, &ptr.p, false);<br>    }<br>    else<br>    {<br>        ULOG_ERR("uci_set error, str=%s\n", str);<br>    }<br>    <br>    if (ptr.p)<br>    {<br>        uci_unload(ctx, ptr.p);    <br>    }</div><div style="margin:0px"><span style="color:rgb(221,64,50)">// Program terminated with signal 6, Aborted.<br></span></div><div style="margin:0px"><span style="color:rgb(221,64,50)">    </span><span style="color:rgb(0,0,0)">uci_free_context(ctx);</span></div><div style="margin:0px"><br>    return ;<br>}</div><div style="margin:0px">/*****************************************************************/</div><div style="margin:0px"><br></div><div style="margin:0px">3、My config file</div><div style="margin:0px">config modman 'modmanruninfo'<br>        option akTestDID 'unknown'<br>        option akTestUptime 'unknown'<br>        option akTestSatelliteNetworkID 'unknown'<br></div><div style="margin:0px">        akMODMANVTxMuteOpenAMIP 'unknown'</div></div></blockquote><div><br></div><div><br></div><div><br></div><div>I don't know what's wrong with your code above, and I haven't tried to analyze it for mistakes.</div><div><br></div><div>I'm replying to you because I wrote a C++ wrapper around UCI a while ago, that involved documenting some of the conventions of the libuci API.</div><div><br></div><div>I'm providing it here as use-at-your-own-risk, and will not provide updates, bug fixes, security fixes, or really anything, pertaining to this code. There are almost certainly bugs in this code, because I do not use it. I abandoned this approach to interacting with the UCI subsystem in favor of the rpcd UCI module.<br></div><div><br></div><div>Released under the same license as libUCI itself (LGPL 2.1).</div><div><br></div><div>It may help you understand the libuci c API. It may not.<br></div><div><br></div><div><pre>/** \file
 *
 */

#include <string>
#include <vector>
#include <utility>
#include <string_view>

#include "uci.h"

#ifndef LIBUCIXX_PARSE_UCI_CONFIG_FILE_H_A2CD8106_1003_4A6D_9A46_4A128EA83262
#define LIBUCIXX_PARSE_UCI_CONFIG_FILE_H_A2CD8106_1003_4A6D_9A46_4A128EA83262
#pragma once

namespace libucixx
{

namespace impl
{

/** \class ScopeGuard
 * The class \c ScopeGuard provides a mechanism to call \c operator() on the object
 * provided as the constructor parameter for the \c ScopeGuard object when \c ScopeGuard
 * has it's destructor called. This provides a guaranteed, exception safe, way of ensuring
 * that cleanup operations are called at end of scope.
 */
template<typename T>
struct ScopeGuard final
{
    /**
     * Does nothing more than store the parameter in a member variable.
     */
    ScopeGuard(T t)
     : m_t(std::move(t))
    { }

    /**
     * Calls the stored object's \c operator() function.
     */
    ~ScopeGuard()
    {
        m_t();
    }

private:
    T m_t; ///< The stored object to invoke on destruction.
}; // struct ScopeGuard

/**
 * Template deduction guide for \c ScopeGuard that avoids the need for a factory function.
 */
template<typename T> ScopeGuard(T t) -> ScopeGuard<T>;

//-----------------------------------------------------------------------------

/**
 * Extracts error string from the \c uci_context provided,
 * and throws a runtime exception with its contents.
 */
inline void throw_uci_error(uci_context & ctx)
{
    char * dest = nullptr;
    const ScopeGuard destGuard([&dest](void){ free(dest); });

    uci_get_errorstr(&ctx, &dest, nullptr);
    if(dest)
    {
        throw std::runtime_error(dest);
    }
    else
    {
        throw std::runtime_error("Unknown UCI error");
    }
} // throw_uci_error()

//-----------------------------------------------------------------------------

/**
 * Assert that the provided \c std::string_view is nul terminated.
 */
inline void assert_nul_terminated(std::string_view str)
{
    assert(str.data() + str.size() == '\0');
}

} // namespace impl

//-----------------------------------------------------------------------------

/**
 * Forward declare types.
 */
struct UciOption;
struct UciSection;
struct UciPackage;
struct UciContext;

//-----------------------------------------------------------------------------

/** \class UciOption
 *
 */
struct UciOption
{
private:
    friend struct UciSection;
    struct constructor_access{}; ///< Provides access to the constructor for friends.

public:
    /**
     * Constructs \c UciOption. Use \c UciSection::get_option() to acquire a \c UciOption.
     *
     * \param key     - The \c constructor_access type that's only visible to friend classes.
     *                  Used to guard against improper use of \c UciOption.
     * \param pSec    - The \c UciSection that this \c UciOption belongs to.
     * \param pUciOpt - The \c uci_option from libuci.
     */
    UciOption(constructor_access key, std::shared_ptr<UciSection> pSec, uci_option * pUciOpt);

    /**
     * \note The returned \c std::string_view's lifetime is the same as the
     *       lifetime of the \c UciPackage that this \c UciOption belongs to.
     *
     * \return The name of the this option.
     */
    std::string_view get_name(void);

    /**
     * \return TRUE  - This option represents a list of values
     *         FALSE - This option represents a scalar value
     */
    bool is_list(void);

    /**
     * \note The returned \c std::string_view's lifetime is the same as the
     *       lifetime of the \c UciPackage that this option belongs to.
     *
     * \return The value of the this option.
     */
    std::string_view get_value(void);

    /**
     * \param value - Nul terminated value to set this option to.
     */
    void set_value(std::string_view value);

    /**
     * \note The returned \c std::string_view's lifetime is the same as the
     *       lifetime of the \c UciPackage that this option belongs to.
     *
     * \return The value of the this option as a list of values.
     */
    std::vector<std::string_view> get_list(void);

    /**
     * \param list - vector of nul terminated \c std::string_views to
     *               replace the current list of option values with.
     */
    void set_list(std::vector<std::string_view> list);

    /**
     * \param value - Nul terminated value to add to this option's list.
     */
    void add_to_list(std::string_view);

    /**
     * \param value - Nul terminated value to remove from this option's list.
     */
    void remove_from_list(std::string_view);

private:
    /**
     * \return The \c uci_context held by the \c UciContext class
     * used to acquire this UciOption via the \c UciSection and
     * \c UciPackage shared pointers.
     */
    uci_context * get_uci_context(void);

private:
    uci_option *                m_pUciOpt; ///< The \c struct uci_option from libuci. Lifetime managed by the associated \c UciPackage object.
    std::shared_ptr<UciSection> m_pSec;    ///< Shared pointer to \c UciSection that this option belongs to. Keeps data retrieved from libuci alive.
}; // UciOption

//-----------------------------------------------------------------------------

/** \class UciSection
 *
 */
struct UciSection : public std::enable_shared_from_this<UciSection>
{
private:
    friend struct UciPackage;
    struct constructor_access{}; ///< Provides access to the constructor for friends.

public:
    /**
     * Constructs \c UciSection. Use \c UciPackage::get_section() to acquire a \c UciSection.
     *
     * \param key     - The \c constructor_access type that's only visible to friend classes.
     *                  Used to guard against improper use of \c UciSection.
     * \param pSec    - The \c UciPackage that this \c UciSection belongs to.
     * \param pUciOpt - The \c uci_section from libuci.
     */
    UciSection(constructor_access key, std::shared_ptr<UciPackage> pPkg, uci_section * pUciSec);

    /**
     * \note The returned \c std::string_view's lifetime is the same as the
     *       lifetime of the \c UciPackage that this \c UciSection belongs to.
     *
     * \return The name of the this section.
     */
    std::string_view get_name(void);

    /**
     * \note The returned \c std::string_view's lifetime is the same as the
     *       lifetime of the \c UciPackage that this \c UciSection belongs to.
     *
     * \return The type of this section.
     */
    std::string_view get_type(void);

    /**
     * \note The returned \c std::string_view's lifetime is the same as the
     *       lifetime of the \c UciPackage that this \c UciSection belongs to.
     *
     * \return The list of options that this section contains.
     */
    std::vector<std::string_view> list_options(void);

    /**
     * Looks up the UciOption associated with the provided nul-terminated name.
     *
     * \param name - the name of the option.
     */
    std::shared_ptr<UciOption> get_option(std::string_view name);

    /**
     * Creates and inserts a new string-type option with the provided value.
     *
     * \param name  - the name of the option.
     * \param value - the value of the option.
     */
    std::shared_ptr<UciOption> add_option(std::string_view name, std::string_view value);

    /**
     * Creates and inserts a new list-type option with the provided value.
     *
     * \param name  - the name of the option.
     * \param value - the values of the option.
     */
    std::shared_ptr<UciOption> add_option(std::string_view name, std::list<std::string_view> value);

    /**
     * Deletes the option with the provided name.
     *
     * \param name - the option to delete.
     */
    void delete_option(std::string_view name);

    /**
     * Change the index of this section.
     *
     * \param the index to change this section to.
     */
    void set_section_index(int idx);

private:
    friend struct UciOption;
    /**
     * \return The \c uci_context held by the \c UciContext class used
     * to acquire this \c UciSection via the \c UciPackage shared pointer.
     */
    uci_context * get_uci_context(void);

private:
    uci_section *               m_pUciSec; ///<
    std::shared_ptr<UciPackage> m_pPkg;    ///<
}; // struct UciSection

//-----------------------------------------------------------------------------

/** \class UciPackage
 *
 */
struct UciPackage : public std::enable_shared_from_this<UciPackage>
{
private:
    friend struct UciContext;
    struct constructor_access{}; ///< Provides access to the constructor for friends.

public:
    /**
     * \param key     - The \c constructor_access type that's only visible to friend classes.
     *                  Used to guard against improper use of \c UciPackage.
     * \param pSec    - The \c UciContext that this UciPackage belongs to.
     * \param pUciOpt - The \c uci_package from libuci.
     */
    UciPackage(constructor_access key, std::shared_ptr<UciContext> pCtx, uci_package * pUciPkg);

    /**
     * Ensures that this UciPackage, and everything that's associated with it, is unloaded from libuci.
     */
    ~UciPackage(void);

    /**
     * \note The returned \c std::string_view's lifetime is the same as the
     *       lifetime of this \c UciPackage.
     *
     * \return The name of this package.
     */
    std::string_view get_name(void);

    /**
     * \note The returned \c std::string_view's lifetime is the same as the
     *       lifetime of the \c UciPackage that this \c UciSection belongs to.
     *
     * \return A collection of section names.
     */
    std::vector<std::string_view> list_sections(void);

    /**
     * \param name - A nul-terminated string indicating the desired section name.
     * \return a UciSection representing the section indicated by name.
     */
    std::shared_ptr<UciSection> get_section(std::string_view name);

    /**
     * \param name - A nul-terminated string indicating the desired section name.
     * \return a UciSection representing the newly added section.
     */
    std::shared_ptr<UciSection> add_section(std::string_view type, std::string_view name = std::string_view());

    /**
     * Delete a section by name.
     *
     * \param name - A nul-terminated string indicating the desired section name.
     */
    void delete_section(std::string_view name);

    /**
     * Saves the changes that have been made to this UciPackage to disk as a delta.
     * Use \c UciPackage::commit() to apply these changes.
     */
    void save(void);

    /**
     * Applies all deltas saved to disk to this package'S config file.
     *
     * \note Warning! Invalidates all UciSections and UciOptions derived from this UciPackage
     *
     * \param overwrite - ???
     */
    void commit(bool overwrite = true);

private:
    friend struct UciSection;

    /**
     * \return The \c uci_context held by the \c UciContext class
     * used to acquire this \c UciPackage shared pointer.
     */
    uci_context * get_uci_context(void);

private:
    uci_package *               m_pUciPkg; ///<
    std::shared_ptr<UciContext> m_pCtx;    ///<
}; // struct UciPackage

//-----------------------------------------------------------------------------

/** \class UciContext
 *
 */
struct UciContext : public std::enable_shared_from_this<UciContext>
{
    /**
     * Allocates a libuci \c struct uci_context.
     */
    UciContext(void);

    /**
     * Frees the libuci \c struct uci_context.
     */
    ~UciContext(void);

    /**
     * \return List of UCI packages available in the configured config directory.
     */
    std::vector<std::string> list_packages(void);

    /**
     * \param name - the desired package name.
     *
     * \note When the UciPackage's shared pointer is deleted, the named package will
     *       be unloaded from libuci.
     *
     * \return A shared pointer to a UciPackage representing the indicated package name.
     */
    std::shared_ptr<UciPackage> load_package(std::string_view name);

    /**
     * Sets the directory that libuci will use to save deltas.
     *
     * \param dir - the indicated, nul-terminatred, directory.
     */
    void set_save_dir(std::string_view dir);

    /**
     * Sets the directory that libuci will search from config files.
     *
     * \param dir - the indicated, nul-terminatred, directory.
     */
    void set_config_dir(std::string_view dir);

    /**
     * Adds an additional directory that libuci will use to search for deltas.
     *
     * \param dir - the indicated, nul-terminatred, directory.
     */
    void add_delta_path(std::string_view dir);

    /**
     * Sets the backend that libuci will use for storage.
     *
     * \param backend - the indicated, nul-terminatred, backend.
     */
    void set_backend(std::string_view backend);

private:
    friend struct UciPackage;

    /**
     * \return the libuci \c struct uci_context.
     */
    uci_context * get_uci_context(void);

private:
    uci_context * m_pUciCtx; ///<
}; // struct UciContext

//-----------------------------------------------------------------------------
// Inline functions
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Inlines of UciOption
//-----------------------------------------------------------------------------
inline UciOption::UciOption(constructor_access, std::shared_ptr<UciSection> pSec, uci_option * pUciOpt)
 : m_pSec(std::move(pSec))
 , m_pUciOpt(pUciOpt)
{
    assert(nullptr != m_pSec);
    assert(nullptr != m_pUciOpt);
}

inline std::string_view UciOption::get_name(void)
{
    return m_pUciOpt-><a href="http://e.name">e.name</a>;
}

inline bool UciOption::is_list(void)
{
    return UCI_TYPE_LIST == m_pUciOpt->type;
}

inline std::string_view UciOption::get_value(void)
{
    return m_pUciOpt->v.string;
}

inline void UciOption::set_value(std::string_view value)
{
    assert(false);

    uci_ptr ptr;
    // Set values to ... something?
    if(UCI_OK != uci_add_list(get_uci_context(), &ptr))
    {
        impl::throw_uci_error(*get_uci_context());
    }
}

inline std::vector<std::string_view> UciOption::get_list(void)
{
    std::vector<std::string_view> list;
    {
        /*
         * List is a list of uci_elements, and in the context of
         * a uci_option with type list, each such element's name
         * is a value in the list.
         */
        uci_element *e = nullptr;
        uci_foreach_element(&(m_pUciOpt->v.list), e)
        {
            list.emplace_back(e->name);
        }
    }
    return list;
}

inline void UciOption::set_list(std::vector<std::string_view> list)
{
    assert(false);

    //TODO:
    // Delete all items and add all the new ones? No clear API option
}

inline void UciOption::add_to_list(std::string_view)
{
    assert(false);

    uci_ptr ptr;
    // Set values to ... something?
    if(UCI_OK != uci_add_list(get_uci_context(), &ptr))
    {
        impl::throw_uci_error(*get_uci_context());
    }
}

inline void UciOption::remove_from_list(std::string_view)
{
    assert(false);

    uci_ptr ptr;
    // Set values to ... something?
    if(UCI_OK != uci_del_list(get_uci_context(), &ptr))
    {
        impl::throw_uci_error(*get_uci_context());
    }
}

inline uci_context * UciOption::get_uci_context(void)
{
    return m_pSec->get_uci_context();
}

//-----------------------------------------------------------------------------
// Inlines of UciSection
//-----------------------------------------------------------------------------
inline UciSection::UciSection(constructor_access, std::shared_ptr<UciPackage> pPkg, uci_section * pUciSec)
 : m_pPkg(std::move(pPkg))
 , m_pUciSec(pUciSec)
{
    assert(nullptr != m_pPkg);
    assert(nullptr != m_pUciSec);
}

inline std::string_view UciSection::get_name(void)
{
    return m_pUciSec-><a href="http://e.name">e.name</a>;
}

inline std::string_view UciSection::get_type(void)
{
    return m_pUciSec->type;
}

inline std::vector<std::string_view> UciSection::list_options(void)
{
    std::vector<std::string_view> sections;
    {
        uci_element *e = nullptr;
        uci_foreach_element(&(m_pUciSec->options), e)
        {
            uci_option *o = uci_to_option(e);
            sections.emplace_back(o-><a href="http://e.name">e.name</a>);
        }
    }
    return sections;
}

inline std::shared_ptr<UciOption> UciSection::get_option(std::string_view name)
{
    impl::assert_nul_terminated(name);

    if(uci_option * pOpt = uci_lookup_option(get_uci_context(), m_pUciSec, name.data()); pOpt != nullptr)
    {
        return std::make_shared<UciOption>(UciOption::constructor_access(), this->shared_from_this(), pOpt);
    }
    else
    {
        return {};
    }
}

inline std::shared_ptr<UciOption> UciSection::add_option(std::string_view name, std::string_view value)
{
    assert(false);

    uci_ptr ptr;
    // Set values to ... something?
    if(UCI_OK != uci_set(get_uci_context(), &ptr))
    {
        impl::throw_uci_error(*get_uci_context());
    }
}

inline std::shared_ptr<UciOption> UciSection::add_option(std::string_view name, std::list<std::string_view> value)
{
    assert(false);

    uci_ptr ptr;
    // Set values to ... something?
    if(UCI_OK != uci_set(get_uci_context(), &ptr))
    {
        impl::throw_uci_error(*get_uci_context());
    }
}

inline void UciSection::delete_option(std::string_view name)
{
    assert(false);

    uci_ptr ptr;
    // Set values to ... something?
    if(UCI_OK != uci_delete(get_uci_context(), &ptr))
    {
        impl::throw_uci_error(*get_uci_context());
    }
}

inline void UciSection::set_section_index(int idx)
{
    if(UCI_OK != uci_reorder_section(get_uci_context(), m_pUciSec, idx))
    {
        impl::throw_uci_error(*get_uci_context());
    }
}

inline uci_context * UciSection::get_uci_context(void)
{
    return m_pPkg->get_uci_context();
}

//-----------------------------------------------------------------------------
// Inlines of UciPackage
//-----------------------------------------------------------------------------
inline UciPackage::UciPackage(constructor_access, std::shared_ptr<UciContext> pCtx, uci_package * pUciPkg)
 : m_pCtx(std::move(pCtx))
 , m_pUciPkg(pUciPkg)
{
    assert(nullptr != m_pCtx);
    assert(nullptr != m_pUciPkg);
}

inline UciPackage::~UciPackage()
{
    if(UCI_OK != uci_unload(get_uci_context(), m_pUciPkg))
    {
        impl::throw_uci_error(*get_uci_context());
    }
}

inline std::string_view UciPackage::get_name(void)
{
    return m_pUciPkg-><a href="http://e.name">e.name</a>;
}

inline std::vector<std::string_view> UciPackage::list_sections(void)
{
    std::vector<std::string_view> sections;

    {
        uci_element *e = nullptr;
        uci_foreach_element( &m_pUciPkg->sections, e)
        {
            uci_section *s = uci_to_section(e);
            sections.emplace_back(s-><a href="http://e.name">e.name</a>);
        }
    }

    return sections;
}

inline std::shared_ptr<UciSection> UciPackage::get_section(std::string_view name)
{
    impl::assert_nul_terminated(name);

    if(uci_section * pSec = uci_lookup_section(get_uci_context(), m_pUciPkg, name.data()); pSec != nullptr)
    {
        return std::make_shared<UciSection>(UciSection::constructor_access(), this->shared_from_this(), pSec);
    }
    else
    {
        return {};
    }
}

inline std::shared_ptr<UciSection> UciPackage::add_section(std::string_view type, std::string_view name)
{
    uci_section * pSec = nullptr;
    if(UCI_OK != uci_add_section(get_uci_context(), m_pUciPkg, type.data(), &pSec))
    {
        impl::throw_uci_error(*get_uci_context());
    }

    if( ! name.empty())
    {
        uci_ptr ptr;
        // Set values to ... something?
        if(UCI_OK != uci_rename(get_uci_context(), &ptr))
        {
            impl::throw_uci_error(*get_uci_context());
        }
    }
    return std::make_shared<UciSection>(UciSection::constructor_access(), this->shared_from_this(), pSec);
}

inline void UciPackage::delete_section(std::string_view name)
{
    assert(false);

    uci_ptr ptr;
    // Set values to ... something?
    if(UCI_OK != uci_delete(get_uci_context(), &ptr))
    {
        impl::throw_uci_error(*get_uci_context());
    }
}

inline void UciPackage::save(void)
{
    if(UCI_OK != uci_save(get_uci_context(), m_pUciPkg))
    {
        impl::throw_uci_error(*get_uci_context());
    }
}

inline void UciPackage::commit(bool overwrite)
{
    if(UCI_OK != uci_commit(get_uci_context(), &m_pUciPkg, overwrite))
    {
        impl::throw_uci_error(*get_uci_context());
    }
}

inline uci_context * UciPackage::UciPackage::get_uci_context(void)
{
    return m_pCtx->get_uci_context();
}

//-----------------------------------------------------------------------------
// Inlines of UciContext
//-----------------------------------------------------------------------------
inline UciContext::UciContext(void)
 : m_pUciCtx(uci_alloc_context())
{
    assert(nullptr != m_pUciCtx);
}

inline UciContext::~UciContext(void)
{
    uci_free_context(m_pUciCtx);
}

inline std::vector<std::string> UciContext::list_packages(void)
{
    char **configs = nullptr;
    const impl::ScopeGuard configGuard([&configs](void){ free(configs); });

    if((UCI_OK != uci_list_configs(m_pUciCtx, &configs)) || ! configs)
    {
        impl::throw_uci_error(*m_pUciCtx);
    }

    std::vector<std::string> configvect;
    for(char ** p = configs; *p; ++p)
    {
        configvect.emplace_back(*p);
    }

    return configvect;
}

inline std::shared_ptr<UciPackage> UciContext::load_package(std::string_view name)
{
    // TODO: It would be ideal if there was a way to ensure a UciPackage loaded twice had the
    //       same lifetime. probably need a std::map<std::string, std::weak_ptr<UciPackage>>;
    impl::assert_nul_terminated(name);

    uci_package * pUciPkg = nullptr;
    if(UCI_OK != uci_load(m_pUciCtx, name.data(), &pUciPkg) || ! pUciPkg)
    {
        impl::throw_uci_error(*m_pUciCtx);
    }
    return std::make_shared<UciPackage>(UciPackage::constructor_access(), this->shared_from_this(), pUciPkg);
}

inline void UciContext::set_save_dir(std::string_view dir)
{
    impl::assert_nul_terminated(dir);

    if(UCI_OK != uci_set_savedir(m_pUciCtx, dir.data()))
    {
        impl::throw_uci_error(*m_pUciCtx);
    }
}

inline void UciContext::set_config_dir(std::string_view dir)
{
    impl::assert_nul_terminated(dir);

    if(UCI_OK != uci_set_confdir(m_pUciCtx, dir.data()))
    {
        impl::throw_uci_error(*m_pUciCtx);
    }
}

inline void UciContext::add_delta_path(std::string_view dir)
{
    impl::assert_nul_terminated(dir);

    if(UCI_OK != uci_add_delta_path(m_pUciCtx, dir.data()))
    {
        impl::throw_uci_error(*m_pUciCtx);
    }
}

inline void UciContext::set_backend(std::string_view backend)
{
    impl::assert_nul_terminated(backend);

    if(UCI_OK != uci_set_backend(m_pUciCtx, backend.data()))
    {
        impl::throw_uci_error(*m_pUciCtx);
    }
}

inline uci_context * UciContext::get_uci_context(void)
{
    return m_pUciCtx;
}

} // namespace libucixx

#endif // LIBUCIXX_PARSE_UCI_CONFIG_FILE_H_A2CD8106_1003_4A6D_9A46_4A128EA83262

</pre></div></div></div>