inih (INI Not Invented Here) is a simple .INI file parser written in C. It's only a couple of pages of code, and it was designed to be small and simple, so it's good for embedded systems. It's also more or less compatible with Python's ConfigParser style of .INI files, including RFC 822-style multi-line syntax and name: value entries.
To use it, just give ini_parse() an INI file, and it will call a callback for every name=value pair parsed, giving you strings for the section, name, and value. It's done this way ("SAX style") because it works well on low-memory embedded systems, but also because it makes for a KISS implementation.
You can also call ini_parse_file() to parse directly from a FILE* object, ini_parse_string() to parse data from a string, or ini_parse_stream() to parse using a custom fgets-style reader function for custom I/O.
Download a release, browse the source, or read about how to use inih in a DRY style with X-Macros.
You can control various aspects of inih using preprocessor defines:
-DINI_ALLOW_MULTILINE=0.-DINI_ALLOW_BOM=0.; character. To disable, add -DINI_ALLOW_INLINE_COMMENTS=0. You can also specify which character(s) start an inline comment using INI_INLINE_COMMENT_PREFIXES.; and # to start a comment at the beginning of a line. You can override this by changing INI_START_COMMENT_PREFIXES.= or : on the line) as an error. To allow names with no values, add -DINI_ALLOW_NO_VALUE=1, and inih will call your handler function with value set to NULL.-DINI_STOP_ON_FIRST_ERROR=1.ini_handler callback doesn't receive the line number as a parameter. If you need that, add -DINI_HANDLER_LINENO=1.name=value pair. To detect new sections (e.g., the INI file has multiple sections with the same name), add -DINI_CALL_HANDLER_ON_NEW_SECTION=1. Your handler function will then be called each time a new section is encountered, with section set to the new section name but name and value set to NULL.malloc instead, specify -DINI_USE_STACK=0.-DINI_MAX_LINE=1000. Note that INI_MAX_LINE must be 3 more than the longest line (due to r, n, and the NUL).INI_INITIAL_ALLOC specifies the initial malloc size when using the heap. It defaults to 200 bytes.-DINI_USE_STACK=0), inih allocates a fixed-sized buffer of INI_INITIAL_ALLOC bytes. To allow this to grow to INI_MAX_LINE bytes, doubling if needed, set -DINI_ALLOW_REALLOC=1.malloc, free, and realloc functions are used; to use a custom allocator, specify -DINI_CUSTOM_ALLOCATOR=1 (and -DINI_USE_STACK=0). You must define and link functions named ini_malloc, ini_free, and (if INI_ALLOW_REALLOC is set) ini_realloc, which must have the same signatures as the stdlib.h memory allocation functions.#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../ini.h"
typedef struct
{
int version;
const char* name;
const char* email;
} configuration;
static int handler(void* user, const char* section, const char* name,
const char* value)
{
configuration* pconfig = (configuration*)user;
#define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0
if (MATCH("protocol", "version")) {
pconfig->version = atoi(value);
} else if (MATCH("user", "name")) {
pconfig->name = strdup(value);
} else if (MATCH("user", "email")) {
pconfig->email = strdup(value);
} else {
return 0; /* unknown section/name, error */
}
return 1;
}
int main(int argc, char* argv[])
{
configuration config;
if (ini_parse("test.ini", handler, &config) < 0) {
printf("Can't load 'test.ini'n");
return 1;
}
printf("Config loaded from 'test.ini': version=%d, name=%s, email=%sn",
config.version, config.name, config.email);
return 0;
}If you're into C++ and the STL, there is also an easy-to-use INIReader class that stores values in a map and lets you Get() them:
#include <iostream>
#include "INIReader.h"
int main()
{
INIReader reader("../examples/test.ini");
if (reader.ParseError() < 0) {
std::cout << "Can't load 'test.ini'n";
return 1;
}
std::cout << "Config loaded from 'test.ini': version="
<< reader.GetInteger("protocol", "version", -1) << ", name="
<< reader.Get("user", "name", "UNKNOWN") << ", email="
<< reader.Get("user", "email", "UNKNOWN") << ", pi="
<< reader.GetReal("user", "pi", -1) << ", active="
<< reader.GetBoolean("user", "active", true) << "n";
return 0;
}This simple C++ API works fine, but it's not very fully-fledged. I'm not planning to work more on the C++ API at the moment, so if you want a bit more power (for example GetSections() and GetFields() functions), see these forks:
Some differences between inih and Python's ConfigParser standard library module:
_wfopen() to open a file and then ini_parse_file() to parse it; inih does not include wchar_t or Unicode handling.meson.build file is not required to use or compile inih, its main purpose is for distributions.-Ddefault_library=static static libraries are built.-Ddistro_install=false libraries, headers and pkg-config files won't be installed.-Dwith_INIReader=false you can disable building the C++ library.distro_install is set to true.inih and INIReader.inih_dep and INIReader_dep dependency variables. You might want to set default_library=static and distro_install=false for the subproject. An official Wrap is provided on WrapDB.version : '<version_as_int>', after the license tag in the project() function and version : meson.project_version(), after the soversion tag in both library() functions.inih can be easily used in tipi.build projects simply by adding the following entry to your .tipi/deps (replace r56 with the latest version tag):
{
"benhoyt/inih": { "@": "r56" }
}The required include path in your project is:
#include <ini.h>You can build and install inih using vcpkg dependency manager:
git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.sh
./vcpkg integrate install
./vcpkg install inih
The inih port in vcpkg is kept up to date by microsoft team members and community contributors. If the version is out of date, please create an issue or pull request on the vcpkg repository.