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

Overview

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

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]

    opened by carmiker 12
  • 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.

    opened by weltling 11
  • 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

    opened by Tachi107 9
  • 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.

    opened by ksdhans 9
  • 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.

    opened by AgostinoSturaro 9
  • 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

    Type-Defect Priority-Medium auto-migrated 
    opened by GoogleCodeExporter 9
  • 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?

    opened by chienhsinlin 8
  • 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

    Type-Defect Priority-Medium auto-migrated 
    opened by GoogleCodeExporter 8
  • 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:

    Type-Defect Priority-Medium auto-migrated 
    opened by GoogleCodeExporter 7
  • 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

    Type-Defect Priority-Medium auto-migrated 
    opened by GoogleCodeExporter 7
  • 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

    opened by pysco68 6
  • Fixed handling of long strings when using INI_USE_STACK

    Fixed handling of long strings when using INI_USE_STACK

    Undefined behavior occurred if the incoming .ini file contains a line that is longer than INI_MAX_LINE with the INI_USE_STACK option. In such a case, it is safe to abort parsing even if INI_STOP_ON_FIRST_ERROR is not set.

    See https://github.com/benhoyt/inih/issues/145 for the details

    Signed-off-by: Mikhail Khachayants [email protected]

    opened by tyler92 2
  • Seems to not discard the rest of a long line?

    Seems to not discard the rest of a long line?

    The problem:

    The issue assumes we are dealing with a long line in an ini file.

    In line 136, reader (fgets) will read max_line chars. If using a heap, then in line 139 and forward, the heap will resize and all the rest of the line will be read:

    https://github.com/benhoyt/inih/blob/5e1d9e2625842dddb3f9c086a50f22e4f45dfc2b/ini.c#L136-L141

    But, in the default case with a stack (fixed size array), fgets will read max_line and do nothing with the rest of the line, which I think will stay in the input stream. Then, in the next iteration of while on line 136, fgets will continue to read that rest of the line, but inih treats those text as the next line. The code will most likely find no : or = in those text, thus report an error on line #(line number of the long line + 1).

    Discussion:

    So, is this expected behavior, or should inih discard the rest of a lone line? The latter might be something like this:

        while (reader(line, (int)max_line, stream) != NULL) {
    #if INI_ALLOW_REALLOC && !INI_USE_STACK
    ... ...
    #else
    if (strchr(line, '\n') == NULL)
        while (fgetc(stream) != '\n')
            ;
    #endif
    
    opened by xlucn 4
Releases(r56)
Owner
Ben Hoyt
By day I’m a software engineer at Canonical, by night a Go hacker and husband/father.
Ben Hoyt
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

Thiadmer Riemersma 293 Dec 29, 2022
Simple and lightweight pathname parser for C. This module helps to parse dirname, basename, filename and file extension .

Path Module For C File name and extension parsing functionality are removed because it's difficult to distinguish between a hidden dir (ex: .git) and

Prajwal Chapagain 3 Feb 25, 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

null 419 Dec 14, 2022
A PE parser written as an exercise to study the PE file structure.

Description A PE parser written as an exercise to study the PE file structure. It parses the following parts of PE32 and PE32+ files: DOS Header Rich

Ahmed Hesham 22 Nov 18, 2022
A TreeSitter parser for the Neorg File Format

NFF TreeSitter Parser A TreeSitter grammar for Neorg. Available Commands Command Result yarn installs needed dependencies (only do if you don't have t

Neorg 63 Dec 7, 2022
MiniCalculator with a simple parser.

MiniCalculator with a simple parser. This is a homework-expanded project. To learn something about parser and basic theory of programmi

GZTime 8 Oct 9, 2021
A simple YAML parser which produces a Node Tree Object representation of YAML Documents

A simple YAML parser which produces a Node Tree Object representation of YAML Documents and includes a find method to locate individual Nodes within the parsed Node Tree.

Timothy Rule 2 Sep 18, 2022
convert elf file to single c/c++ header file

elf-to-c-header Split ELF to single C/C++ header file

Musa Ünal 2 Nov 4, 2021
A ring buffer designed to work with embedded devices, does not use heap allocations.

Embedded Ring Buffer This is a header only ring buffer that is designed to work on embedded devices, it is able to handle non-blocking ISR spooling

Richard Bamford 49 Dec 2, 2022
Edf is an event-driven framework for embedded system (e.g. FreeRTOS) with state machine and subscriber-publisher pattern.

Edf means event-driven framework. Event-driven programming is a common pattern in embedded systems. However, if you develop software directly on top o

Arrow89 7 Oct 16, 2022
Parser for argv that works similarly to getopt

About Most command-line programs have to parse options, so there are a lot of different solutions to this problem. Some offer many features, while oth

Jørgen Ibsen 157 Dec 22, 2022
tiny recursive descent expression parser, compiler, and evaluation engine for math expressions

TinyExpr TinyExpr is a very small recursive descent parser and evaluation engine for math expressions. It's handy when you want to add the ability to

Lewis Van Winkle 1.2k Dec 30, 2022
Universal configuration library parser

LIBUCL Table of Contents generated with DocToc Introduction Basic structure Improvements to the json notation General syntax sugar Automatic arrays cr

Vsevolod Stakhov 1.5k Dec 28, 2022
Very fast Markdown parser and HTML generator implemented in WebAssembly, based on md4c

Very fast Markdown parser and HTML generator implemented in WebAssembly, based on md4c

Rasmus 1.3k Dec 24, 2022
A markdown parser for tree-sitter

tree-sitter-markdown A markdown parser for tree-sitter Progress: Leaf blocks Thematic breaks ATX headings Setext headings Indented code blocks Fenced

Matthias Deiml 227 Jan 7, 2023
Dead simple C logging library contained in a single header (.h) file

Seethe Logging so simple, you only need to include a single header file. seethe supports 6 different log levels (DEBUG, INFO, NOTICE, WARNING, ERROR,

Jason Nguyen 28 Nov 24, 2022
Infocom Z-machine build environment for 25 retro computer systems, preconfigured for PunyInform

Puddle BuildTools (for PunyInform and other libraries and compilers targeting the Infocom Z-machine) If you're into classic 8-bit and 16-bit home comp

Stefan Vogt 44 Nov 25, 2022
Locate the current executable and the current module/library on the file system

Where Am I? A drop-in two files library to locate the current executable and the current module on the file system. Supported platforms: Windows Linux

Gregory Pakosz 382 Dec 27, 2022
Beacon Object File (BOF) for remote process injection via thread hijacking

cThreadHijack ___________.__ .______ ___ .__ __ __ ___\__ ___/| |_________ ____ _____

Connor McGarr 158 Dec 28, 2022