• Stars
    star
    2,402
  • Rank 19,146 (Top 0.4 %)
  • Language
    C
  • License
    Other
  • Created over 9 years ago
  • Updated 4 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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

inih (INI Not Invented Here)

Tests

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.

Using inih with tipi.build

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>

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

More Repositories

1

goawk

A POSIX-compliant AWK interpreter written in Go, with CSV support
Go
1,920
star
2

scandir

Better directory iterator and faster os.walk(), now in the Python 3.5 stdlib
Python
529
star
3

pygit

Just enough git (written in Python) to create a repo and push to GitHub
Python
314
star
4

countwords

Playing with counting word frequencies (and performance) in various languages.
Rust
306
star
5

protothreads-cpp

Protothread.h, a C++ port of Adam Dunkels' protothreads library
C++
178
star
6

pybktree

Python BK-tree data structure to allow fast querying of "close" matches
Python
169
star
7

ht

Simple hash table implemented in C
C
148
star
8

pyast64

Compile a subset of the Python AST to x64-64 assembler
Python
136
star
9

go-routing

Different approaches to HTTP routing in Go
Go
120
star
10

loxlox

Lox interpreter written in Lox
Python
112
star
11

mugo

Mugo, a toy compiler for a subset of Go that can compile itself
HTML
108
star
12

littlelang

A little language interpreter written in Go
Go
92
star
13

third

Third, a small Forth compiler for 8086 DOS
Forth
75
star
14

go-1brc

My Go solutions to the One Billion Row Challenge
Go
74
star
15

prig

Prig is for Processing Records In Go. Like AWK, but snobbish.
Go
64
star
16

gogit

Just enough of a git client (in Go) to init a repo, commit, and push to GitHub
Go
51
star
17

cdnupload

Upload your site's static files to a directory or CDN, using content-based hashing
Python
50
star
18

web-service-stdlib

Rewrite of Go RESTful API tutorial using only the stdlib
Go
49
star
19

simplelists

Tiny to-do list web app written in Go
Go
48
star
20

pas2go

Pascal to Go converter (converts a subset of Turbo Pascal 5.5)
Pascal
42
star
21

symplate

Symplate, a simple and fast Python template language (NOTE: no longer maintained; use Jinja2 or Mako instead)
Python
30
star
22

nibbleforth

A very compact stack machine (Forth) bytecode
Python
29
star
23

zztgo

Port of ZZT to Go (using a Pascal-to-Go converter)
Go
26
star
24

gosnip

Run small snippets of Go code from the command line
Go
24
star
25

python-pentomino

Pentomino puzzle solver using Python code generation
Python
20
star
26

benhoyt.github.com

Source code for my website
HTML
18
star
27

betterwalk

BetterWalk, a better and faster os.walk() for Python -- DEPRECATED, see my "scandir" project
Python
17
star
28

fe

Bruce Hoyt's Forth Editor (Dad's editor that I grew up coding with)
Forth
16
star
29

namedmutex

namedmutex.py, a simple ctypes wrapper for Win32 named mutexes
Python
15
star
30

soft404

Soft 404 (dead page) detector in Python
Python
13
star
31

io-performance

Code repo for https://benhoyt.com/writings/io-is-no-longer-the-bottleneck/
Go
13
star
32

awkmake

Code to go with my article "The AWK book's 60-line version of Make"
Awk
13
star
33

repike

Rob Pike's simple regex matcher converted to Go
Go
11
star
34

benos

A tiny 32-bit Forth operating system I wrote when I was 16
Forth
8
star
35

counter

Fast hash table for counting short strings in Go
Go
7
star
36

false-forth

A False compiler and interpreter written in ANS Forth
Forth
7
star
37

py-1brc

Optimising the One Billion Row Challenge (1BRC) in Python
Python
5
star
38

mro

MRO is not an ORM - Map Rows to Objects with web.py
Python
2
star
39

snappass-test

Demo of Juju K8s sidecar charm with Pebble
Python
2
star
40

circle

Draw circles using the Bresenham Circle Algorithm in Go
Go
2
star
41

interpspeed

Test interpreter speed of various language VMs
Python
1
star
42

boggle

Boggle solver competition
Python
1
star