The Fossil Media INI library provides a simple and reliable way to read, edit, and write INI configuration files in pure C. It models INI data as sections containing key-value pairs and supports loading from disk or memory, querying values, updating or adding entries, and saving back to file. Designed with minimal dependencies and efficient memory usage, it is ideal for use in configuration-driven applications. The included C++ RAII wrapper further simplifies usage by offering an exception-safe, object-oriented interface, making it equally practical for both low-level C projects and high-level C++ systems.
Code reference for C and C++ APIs for the respective Fossil Logic library.
HEADER REFERENCE #
#ifndef FOSSIL_MEDIA_INI_H
#define FOSSIL_MEDIA_INI_H
#include <stddef.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief Represents a single key-value pair in an INI section.
*/
typedef struct fossil_media_ini_entry_t {
char *key;
char *value;
} fossil_media_ini_entry_t;
/**
* @brief Represents a section in an INI file.
*/
typedef struct fossil_media_ini_section_t {
char *name;
fossil_media_ini_entry_t *entries;
size_t entry_count;
} fossil_media_ini_section_t;
/**
* @brief Represents a loaded INI file.
*/
typedef struct fossil_media_ini_t {
fossil_media_ini_section_t *sections;
size_t section_count;
} fossil_media_ini_t;
/**
* @brief Load an INI file from disk.
*
* @param path Path to the .ini file.
* @param ini Output structure pointer.
* @return 0 on success, nonzero on failure.
*/
int fossil_media_ini_load_file(const char *path, fossil_media_ini_t *ini);
/**
* @brief Load an INI file from a string buffer.
*
* @param data INI data as null-terminated string.
* @param ini Output structure pointer.
* @return 0 on success, nonzero on failure.
*/
int fossil_media_ini_load_string(const char *data, fossil_media_ini_t *ini);
/**
* @brief Save an INI structure to disk.
*
* @param path Path to output file.
* @param ini INI data structure.
* @return 0 on success, nonzero on failure.
*/
int fossil_media_ini_save_file(const char *path, const fossil_media_ini_t *ini);
/**
* @brief Free all memory used by an INI structure.
*/
void fossil_media_ini_free(fossil_media_ini_t *ini);
/**
* @brief Get the value for a given section/key.
*
* @param ini INI data structure.
* @param section Section name.
* @param key Key name.
* @return Value string or NULL if not found.
*/
const char *fossil_media_ini_get(const fossil_media_ini_t *ini, const char *section, const char *key);
/**
* @brief Set the value for a given section/key.
* Creates section/key if they do not exist.
*
* @param ini INI data structure.
* @param section Section name.
* @param key Key name.
* @param value Value string.
* @return 0 on success, nonzero on failure.
*/
int fossil_media_ini_set(fossil_media_ini_t *ini, const char *section, const char *key, const char *value);
#ifdef __cplusplus
}
#include <string>
#include <stdexcept>
#include <utility>
namespace fossil {
namespace media {
/**
* @brief C++ wrapper class for handling INI files.
*/
class Ini {
public:
/**
* @brief Construct an empty INI object.
*/
Ini() {
ini_.sections = nullptr;
ini_.section_count = 0;
}
/**
* @brief Construct and load an INI file from disk.
* @param path Path to the .ini file.
* @throw std::runtime_error on failure.
*/
explicit Ini(const std::string& path) {
ini_.sections = nullptr;
ini_.section_count = 0;
if (fossil_media_ini_load_file(path.c_str(), &ini_) != 0)
throw std::runtime_error("Failed to load INI file: " + path);
}
/**
* @brief Construct and load an INI file from a string buffer.
* @param data INI data as null-terminated string.
* @throw std::runtime_error on failure.
*/
Ini(const char* data) {
ini_.sections = nullptr;
ini_.section_count = 0;
if (fossil_media_ini_load_string(data, &ini_) != 0)
throw std::runtime_error("Failed to load INI from string");
}
/**
* @brief Move constructor.
*/
Ini(Ini&& other) noexcept
: ini_{other.ini_} {
other.ini_.sections = nullptr;
other.ini_.section_count = 0;
}
/**
* @brief Move assignment operator.
*/
Ini& operator=(Ini&& other) noexcept {
if (this != &other) {
fossil_media_ini_free(&ini_);
ini_ = other.ini_;
other.ini_.sections = nullptr;
other.ini_.section_count = 0;
}
return *this;
}
/**
* @brief Destructor. Frees all memory used by the INI structure.
*/
~Ini() {
fossil_media_ini_free(&ini_);
}
// Non-copyable
Ini(const Ini&) = delete;
Ini& operator=(const Ini&) = delete;
/**
* @brief Load an INI file from disk.
* @param path Path to the .ini file.
* @return true on success, false on failure.
*/
bool load_file(const std::string& path) {
fossil_media_ini_free(&ini_);
ini_.sections = nullptr;
ini_.section_count = 0;
return fossil_media_ini_load_file(path.c_str(), &ini_) == 0;
}
/**
* @brief Load an INI file from a string buffer.
* @param data INI data as null-terminated string.
* @return true on success, false on failure.
*/
bool load_string(const char* data) {
fossil_media_ini_free(&ini_);
ini_.sections = nullptr;
ini_.section_count = 0;
return fossil_media_ini_load_string(data, &ini_) == 0;
}
/**
* @brief Save the INI structure to disk.
* @param path Path to output file.
* @return true on success, false on failure.
*/
bool save_file(const std::string& path) const {
return fossil_media_ini_save_file(path.c_str(), &ini_) == 0;
}
/**
* @brief Get the value for a given section/key.
* @param section Section name.
* @param key Key name.
* @return Value string or empty string if not found.
*/
std::string get(const std::string& section, const std::string& key) const {
const char* val = fossil_media_ini_get(&ini_, section.c_str(), key.c_str());
return val ? std::string(val) : std::string();
}
/**
* @brief Set the value for a given section/key. Creates section/key if they do not exist.
* @param section Section name.
* @param key Key name.
* @param value Value string.
* @return true on success, false on failure.
*/
bool set(const std::string& section, const std::string& key, const std::string& value) {
return fossil_media_ini_set(&ini_, section.c_str(), key.c_str(), value.c_str()) == 0;
}
/**
* @brief Get the underlying C structure (const).
*/
const fossil_media_ini_t* c_struct() const { return &ini_; }
/**
* @brief Get the underlying C structure (non-const).
*/
fossil_media_ini_t* c_struct() { return &ini_; }
private:
fossil_media_ini_t ini_;
};
} // namespace media
} // namespace fossil
#endif
#endif /* FOSSIL_MEDIA_INI_H */SAMPLE CODE C #
#include "fossil/media/ini.h"
#include <stdio.h>
int main(void) {
fossil_media_ini_t ini;
// Load from string
const char *data =
"[server]\n"
"host=localhost\n"
"port=8080\n\n"
"[user]\n"
"name=Alice\n"
"role=admin\n";
if (fossil_media_ini_load_string(data, &ini) != 0) {
fprintf(stderr, "Failed to load INI from string\n");
return 1;
}
// Access values
const char *host = fossil_media_ini_get(&ini, "server", "host");
const char *port = fossil_media_ini_get(&ini, "server", "port");
printf("Server: %s:%s\n", host ? host : "?", port ? port : "?");
// Modify value
fossil_media_ini_set(&ini, "server", "port", "9090");
// Save to file
if (fossil_media_ini_save_file("out.ini", &ini) == 0) {
printf("Saved INI to out.ini\n");
}
// Cleanup
fossil_media_ini_free(&ini);
return 0;
}
SAMPLE CODE C++ #
#include "fossil/media/ini.h"
#include <iostream>
int main() {
try {
// Load INI from string
const char *data =
"[database]\n"
"user=admin\n"
"password=secret\n"
"port=3306\n";
fossil::media::Ini ini(data);
// Read values
std::cout << "Database user: " << ini.get("database", "user") << "\n";
std::cout << "Database port: " << ini.get("database", "port") << "\n";
// Modify value
ini.set("database", "password", "supersecret");
// Save back to file
if (ini.save_file("config.ini")) {
std::cout << "INI saved to config.ini\n";
}
} catch (const std::runtime_error& e) {
std::cerr << "INI error: " << e.what() << "\n";
return 1;
}
return 0;
}