Simple .INI file parser in C, good for embedded systems

inih (INI Not Invented Here)

TravisCI Build

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.

Compile-time options

You can control various aspects of inih using preprocessor defines:

Syntax options

  • Multi-line entries: By default, inih supports multi-line entries in the style of Python's ConfigParser. To disable, add -DINI_ALLOW_MULTILINE=0.
  • UTF-8 BOM: By default, inih allows a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of INI files. To disable, add -DINI_ALLOW_BOM=0.
  • Inline comments: By default, inih allows inline comments with the ; 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.
  • Start-of-line comments: By default, inih allows both ; and # to start a comment at the beginning of a line. You can override this by changing INI_START_COMMENT_PREFIXES.
  • Allow no value: By default, inih treats a name with no value (no = 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.

Parsing options

  • Stop on first error: By default, inih keeps parsing the rest of the file after an error. To stop parsing on the first error, add -DINI_STOP_ON_FIRST_ERROR=1.
  • Report line numbers: By default, the ini_handler callback doesn't receive the line number as a parameter. If you need that, add -DINI_HANDLER_LINENO=1.
  • Call handler on new section: By default, inih only calls the handler on each 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.

Memory options

  • Stack vs heap: By default, inih creates a fixed-sized line buffer on the stack. To allocate on the heap using malloc instead, specify -DINI_USE_STACK=0.
  • Maximum line length: The default maximum line length (for stack or heap) is 200 bytes. To override this, add something like -DINI_MAX_LINE=1000. Note that INI_MAX_LINE must be 3 more than the longest line (due to \r, \n, and the NUL).
  • Initial malloc size: INI_INITIAL_ALLOC specifies the initial malloc size when using the heap. It defaults to 200 bytes.
  • Allow realloc: By default when using the heap (-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.
  • Custom allocator: By default when using the heap, the standard library's 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.

Simple example in C

#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=%s\n",
        config.version, config.name, config.email);
    return 0;
}

C++ example

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:

Differences from ConfigParser

Some differences between inih and Python's ConfigParser standard library module:

  • INI name=value pairs given above any section headers are treated as valid items with no section (section name is an empty string). In ConfigParser having no section is an error.
  • Line continuations are handled with leading whitespace on continued lines (like ConfigParser). However, instead of concatenating continued lines together, they are treated as separate values for the same key (unlike ConfigParser).

Platform-specific notes

  • Windows/Win32 uses UTF-16 filenames natively, so to handle Unicode paths you need to call _wfopen() to open a file and then ini_parse_file() to parse it; inih does not include wchar_t or Unicode handling.

Meson notes

  • The meson.build file is not required to use or compile inih, its main purpose is for distributions.
  • By default Meson is set up for distro installation, but this behavior can be configured for embedded use cases:
    • with -Ddefault_library=static static libraries are built.
    • with -Ddistro_install=false libraries, headers and pkg-config files won't be installed.
    • with -Dwith_INIReader=false you can disable building the C++ library.
  • All compile-time options are implemented in Meson as well, you can take a look at meson_options.txt for their definition. These won't work if distro_install is set to true.
  • If you want to use inih for programs which may be shipped in a distro, consider linking against the shared libraries. The pkg-config entries are inih and INIReader.
  • In case you use inih as a Meson subproject, you can use the 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.
  • For packagers: if you want to tag the version in the pkg-config file, you will need to do this downstream. Add 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.

Building from vcpkg

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.

Related links

Owner
Ben Hoyt
By day I’m a software engineer at Canonical, by night a Go hacker and husband/father.
Ben Hoyt
Comments
  • Switch from strncpy to memcpy in strcpy0 function

    Switch from strncpy to memcpy in strcpy0 function

    This pull request fixes a stringop-truncation warning. ini.c:78:5: warning: ‘strncpy’ output may be truncated copying 49 bytes from a string of length 199 [-Wstringop-truncation]

  • Allow handler to be called on a key without '=' or ':'

    Allow handler to be called on a key without '=' or ':'

    The actual need on this is as follows. The INI file contains a special section containing all the sections in the file. That is used to validate the INI file itself. Eg

    [list all]
    section0
    section1
    
    [section0]
    key=val
    
    [section1]
    key=val
    

    For this to work, the keys in the list all section need to be passed to the handler, so they can be recorded and used later for the validation. This has been made configurable, so won't affect the default library behavior.

    Thanks.

  • Add visibility symbols

    Add visibility symbols

    They are required to properly build DLLs on Windows, and improve the quality of shared objects on Linux. See https://gcc.gnu.org/wiki/Visibility for details.

    This issue was first discovered here: https://github.com/mesonbuild/wrapdb/pull/340#issuecomment-1075102565

    CC @stephanlachnit and @eli-schwartz

  • Add option to call handler when a new section is encountered

    Add option to call handler when a new section is encountered

    Some programs have ini files with multiple sections that have the same name, which would make it very useful to know when a new section is encountered.

    This patch adds this ability as an option that's enabled by setting INI_CALL_HANDLER_ON_NEW_SECTION to 1.

    When enabled, the handler callback can check the name parameter. If it's NULL, then it was called because a new section was entered.

  • strncpy error in VS2013

    strncpy error in VS2013

    This library seems promising, with tests and all.

    When I tried your example in Visual Studio 2013, I got this error

    Error   1   error C4996: 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.    c:\users\sturaroa\downloads\inih-r30\ini.c  56  1   IniParserTest
    

    I'd rather not use have to set permissive flags just for this bit.

  • New  'GetSections' method

    New 'GetSections' method

    Hello,
    
    We've made a new method, which we use in our applications which link with inih 
    library, to list the sections of the ini file.
    
    Please see it at:
    
    https://github.com/OSSystems/inih/commit/182f1e19eb99e188bf2f7e71fdb7ee8f4318610
    2
    
    and if possible merge it so it easy us to keep our CMake addition easy to 
    maintain :D
    

    Original issue reported on code.google.com by [email protected] on 5 Jul 2014 at 1:27

  • array in ini file for ini parser stream to parse

    array in ini file for ini parser stream to parse

    Hi!:

    I have an array in a ini file like

    [section1]

    key1 = value1 key2 = value2 ..... lut_table = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ......

    In the structure, it is defined as struct { ... int lut_table[16]; } for(i=0; i<16; i++) pconfig.lut_table[i] = atoi[multi_value] something?

    Can I implement something in the ini_parse_stream to read values for this array? any suggestions? Or something similar like simpleIni to have a key whic has multivalues?

  • Possible bug: ambiguous handling of continuations

    Possible bug: ambiguous handling of continuations

    What steps will reproduce the problem?
    1. Compile examples/ini_dump.c program (gcc examples/ini_dump.c ini.c
    2. Run output program with the INI file below (./a.out test.in)
    
    Sample INI file:
    
        [testing]
        key = value
                  plus a continuation
        key2 = value2
    
    Expected output:
    
        [testing]
        key = value
        plus a continuation
        key2 = value2
    
    Actual output:
    
        [testing]
        key = value
        key = plus a continuation
        key2 = value2
    
    Python's ConfigParser handles line continuations by appending the continuation 
    string onto the option value (prefixed with a newline).
    
    inih's method of handling continuations makes these two files equivalent (I 
    don't think they should be):
    
    File 1:
    
        [testing]
        key = value
              plus a continuation
    
    File 2:
    
        [testing]
        key = value
        key = plus a continuation
    

    Original issue reported on code.google.com by [email protected] on 11 Mar 2012 at 11:33

  • Makefile for building inih as static library

    Makefile for building inih as static library

    Hello,
    
    I am packaging dunst software into Fedora and our rules does not allow 
    embedding libraries. Would you accept this Makefile so linux distributions 
    could make static packages of your library?
    
    It does not have to be called "Makefile", I would be fine with 
    "Makefile.static" or "Makefile.dist". It would be also possible to include the 
    file in the extra/ directory (but some paths would need to be changed).
    
    More details here: https://fedorahosted.org/fpc/ticket/216
    
    Thank you in advance. This is the Makefile (attaching it as well because of 
    TABs):
    
    # 
    # Simple makefile to build inih as static library.
    #
    
    SRC = ini.c
    OBJ = $(SRC:.c=.o)
    OUT = libinih.a
    INCLUDES = -I.
    CCFLAGS = -g -O2
    CCC = g++
    LDFLAGS = -g
    
    .SUFFIXES: .cpp
    
    default: $(OUT)
    
    .cpp.o:
        $(CCC) $(INCLUDES) $(CCFLAGS) $(EXTRACCFLAGS) -c $< -o [email protected]
    
    $(OUT): $(OBJ)
        ar rcs $(OUT) $(OBJ)
    
    clean:
        rm -f $(OBJ) $(OUT)
    

    Original issue reported on code.google.com by [email protected] on 8 Oct 2012 at 2:38

    Attachments:

  • BOM support

    BOM support

    Seems that ini files with BOM header is not currently supported. Could you 
    support this, please?
    
    
    Thanks!
    

    Original issue reported on code.google.com by xuhdev on 14 Jun 2012 at 9:24

  • Added tipi.build support and a simple usage example

    Added tipi.build support and a simple usage example

    Hello, Yannic from tipi.build here,

    I just added tipi support to your project and was wondering if you'd like to merge so everyone gets a new way of using your project.

    I tested the project (both for project dev and as dep) for Linux, MacOS and Windows (both clang and MSVC builds btw) and it seems to work nicely.

    If there's any issue or if you have any question with this I'll be happy to sort it out

    Best from Zürich, Yannic

  • MSVC throws C4244

    MSVC throws C4244

    MSVC 14.32.3132 throws C4244 when compiling INIReader.cpp.

    1>C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.32.31326\include\algorithm(3031,24): warning C4244: '=': conversion from 'int' to 'char', possible loss of data
    1>...\Lib\inih\cpp\INIReader.cpp(72): message : see reference to function template instantiation '_OutIt std::transform<std::_String_iterator<std::_String_val<std::_Simple_types<_Elem>>>,std::_String_iterator<std::_String_val<std::_Simple_types<_Elem>>>,int(__cdecl *)(int)>(const _InIt,const _InIt,_OutIt,_Fn)' being compiled
    1>        with
    1>        [
    1>            _OutIt=std::_String_iterator<std::_String_val<std::_Simple_types<char>>>,
    1>            _Elem=char,
    1>            _InIt=std::_String_iterator<std::_String_val<std::_Simple_types<char>>>,
    1>            _Fn=int (__cdecl *)(int)
    1>        ]
    

    This PR fixes this, by using a lambda which static casts the return value of ::tolower into an unsigned char.

    TLDR:

    • ::tolower returns int
    • MSVC throws the warning C4244: conversion from 'int' to 'char'
    • this lambda static casts the return value back to char
Cross-platform C++ library providing a simple API to read and write INI-style configuration files

simpleini A cross-platform library that provides a simple API to read and write INI-style configuration files. It supports data files in ASCII, MBCS a

Sep 24, 2022
C++20 single-header library for embedding INI configs

ini-config A single-header library that converts INI-formatted string literals to a key-value pair list at compile-time. Requires C++20; tested on gcc

Jun 21, 2022
Small configuration file parser library for C.

libConfuse Introduction Documentation Examples Build & Install Origin & References Introduction libConfuse is a configuration file parser library writ

Sep 20, 2022
Header-only TOML config file parser and serializer for C++17 (and later!).
Header-only TOML config file parser and serializer for C++17 (and later!).

toml++ homepage ✨ This README is fine, but the toml++ homepage is better. ✨ Library features Header-only Supports the latest TOML release (v1.0.0), pl

Oct 1, 2022
config-loader is a static reflection framework written in C++17 from parse configuration file to native data structure.

config-loader is a static reflection framework written in C++17 from parse configuration file to native data structure.

Sep 22, 2022
Simple .INI file parser in C, good for embedded systems

inih (INI Not Invented Here) 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 d

Sep 27, 2022
BLLIP reranking parser (also known as Charniak-Johnson parser, Charniak parser, Brown reranking parser) See http://pypi.python.org/pypi/bllipparser/ for Python module.

BLLIP Reranking Parser Copyright Mark Johnson, Eugene Charniak, 24th November 2005 --- August 2006 We request acknowledgement in any publications that

Aug 15, 2022
BLLIP reranking parser (also known as Charniak-Johnson parser, Charniak parser, Brown reranking parser)
BLLIP reranking parser (also known as Charniak-Johnson parser, Charniak parser, Brown reranking parser)

BLLIP reranking parser (also known as Charniak-Johnson parser, Charniak parser, Brown reranking parser)

Aug 15, 2022
ini file parser

Iniparser 4 I - Overview This modules offers parsing of ini files from the C level. See a complete documentation in HTML format, from this directory o

Sep 25, 2022
ini file parser

Iniparser 4 I - Overview This modules offers parsing of ini files from the C level. See a complete documentation in HTML format, from this directory o

Sep 25, 2022
Lab2: using a physical embedded systems to interact with virtual embedded systems.
Lab2: using a physical embedded systems to interact with virtual embedded systems.

Lab2: dotDevice EmSys Autumn 2021 In this lab you will use your TinyPico to interact with a virtual embedded system. Current Virtual Lab URL: [http://

Oct 20, 2021
A small and portable INI file library with read/write support

minIni minIni is a portable and configurable library for reading and writing ".INI" files. At just below 900 lines of commented source code, minIni tr

Sep 17, 2022
JSON parser and generator for C/C++ with scanf/printf like interface. Targeting embedded systems.

JSON parser and emitter for C/C++ Features ISO C and ISO C++ compliant portable code Very small footprint No dependencies json_scanf() scans a string

Sep 24, 2022
A place to collaborate on code for the Embedded.fm book club. Currently reading "STM32 ARM Programming for Embedded Systems".

Welcome to the Book Club Code site! This is a place for the Embedded.fm book club to collaborate and learn together. Repo Structure Guide Top-level fo

Jul 21, 2022
Low dependency(C++11 STL only), good portability, header-only, deep neural networks for embedded
Low dependency(C++11 STL only), good portability, header-only, deep neural networks for embedded

LKYDeepNN LKYDeepNN 可訓練的深度類神經網路 (Deep Neural Network) 函式庫。 輕量,核心部份只依賴 C++11 標準函式庫,低相依性、好移植,方便在嵌入式系統上使用。 Class diagram 附有訓練視覺化 demo 程式 訓練視覺化程式以 OpenCV

Sep 21, 2022
Cross-platform C++ library providing a simple API to read and write INI-style configuration files

simpleini A cross-platform library that provides a simple API to read and write INI-style configuration files. It supports data files in ASCII, MBCS a

Sep 24, 2022
Open source file system for small embedded systems

STORfs Open Source File System Release Version 1.0.2 Created by: KrauseGLOBAL Solutions, LLC What is STORfs? STORfs is an open source flash file syste

Jul 26, 2022
Generic single-file implementations of AVL tree in C and C++ suitable for deeply embedded systems
Generic single-file implementations of AVL tree in C and C++ suitable for deeply embedded systems

Cavl Generic single-file implementation of AVL tree suitable for deeply embedded systems. Simply copy cavl.h or cavl.hpp (depending on which language

Sep 20, 2022
C++20 single-header library for embedding INI configs

ini-config A single-header library that converts INI-formatted string literals to a key-value pair list at compile-time. Requires C++20; tested on gcc

Jun 21, 2022
Blog post on using a custom Bash builtin to parse INI config files

Writing a Bash Builtin in C to Parse INI Configs Why Not Just Parse INI Configs With Bash? Shell languages such as Bash excel at certain tasks, such a

Aug 12, 2022