Skip to content
/ inih Public

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

License

Notifications You must be signed in to change notification settings

benhoyt/inih

Repository files navigation

inih (INI Not Invented Here)

Tests

inih (INI Not Invented Here)is a simple.INI fileparser written in C. It's only a couple of pages of code, and it was designed to besmall and simple,so it's good for embedded systems. It's also more or less compatible with Python'sConfigParserstyle of.INI files, including RFC 822-style multi-line syntax andname: valueentries.

To use it, just giveini_parse()an INI file, and it will call a callback for everyname=valuepair 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 callini_parse_file()to parse directly from aFILE*object,ini_parse_string()to parse data from a string, orini_parse_stream()to parse using a custom fgets-style reader function for custom I/O.

Download a release, browse the source, or read abouthow to use inih in a DRY stylewith 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 usingINI_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 changingINI_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, theini_handlercallback 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 eachname=valuepair. 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, withsectionset to the new section name butnameandvalueset to NULL.

Memory options

  • Stack vs heap:By default, inih creates a fixed-sized line buffer on the stack. To allocate on the heap usingmallocinstead, 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 thatINI_MAX_LINEmust be 3 more than the longest line (due to\r,\n,and the NUL).
  • Initial malloc size:INI_INITIAL_ALLOCspecifies 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 ofINI_INITIAL_ALLOCbytes. To allow this to grow toINI_MAX_LINEbytes, doubling if needed, set-DINI_ALLOW_REALLOC=1.
  • Custom allocator:By default when using the heap, the standard library'smalloc,free,andreallocfunctions are used; to use a custom allocator, specify-DINI_CUSTOM_ALLOCATOR=1(and-DINI_USE_STACK=0). You must define and link functions namedini_malloc,ini_free,and (ifINI_ALLOW_REALLOCis set)ini_realloc,which must have the same signatures as thestdlib.hmemory allocation functions.

Simple example in C

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include"../ini.h"

typedefstruct
{
intversion;
constchar*name;
constchar*email;
}configuration;

staticinthandler(void*user,constchar*section,constchar*name,
constchar*value)
{
configuration*pconfig=(configuration*)user;

#defineMATCH(s,n) strcmp(section, s) == 0 && strcmp(name, n) == 0
if(MATCH("protocol","version")) {
pconfig->version=atoi(value);
}elseif(MATCH("user","name")) {
pconfig->name=strdup(value);
}elseif(MATCH("user","email")) {
pconfig->email=strdup(value);
}else{
return0;/* unknown section/name, error */
}
return1;
}

intmain(intargc,char*argv[])
{
configurationconfig;

if(ini_parse("test.ini",handler,&config)<0) {
printf("Can't load 'test.ini'\n");
return1;
}
printf("Config loaded from 'test.ini': version=%d, name=%s, email=%s\n",
config.version,config.name,config.email);
return0;
}

C++ example

If you're into C++ and the STL, there is also an easy-to-useINIReader classthat stores values in amapand lets youGet()them:

#include<iostream>
#include"INIReader.h"

intmain()
{
INIReaderreader("../examples/test.ini");

if(reader.ParseError() <0) {
std::cout <<"Can't load 'test.ini'\n";
return1;
}
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";
return0;
}

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 exampleGetSections()andGetFields()functions), see these forks:

Differences from ConfigParser

Some differences between inih and Python'sConfigParserstandard 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 thenini_parse_file()to parse it; inih does not includewchar_tor Unicode handling.

Meson notes

  • Themeson.buildfile 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=staticstatic libraries are built.
    • with-Ddistro_install=falselibraries, headers and pkg-config files won't be installed.
    • with-Dwith_INIReader=falseyou can disable building the C++ library.
  • All compile-time options are implemented in Meson as well, you can take a look atmeson_options.txtfor their definition. These won't work ifdistro_installis set totrue.
  • 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 areinihandINIReader.
  • In case you use inih as a Meson subproject, you can use theinih_depandINIReader_depdependency variables. You might want to setdefault_library=staticanddistro_install=falsefor the subproject. An official Wrap is provided onWrapDB.
  • For packagers: if you want to tag the version in the pkg-config file, you will need to do this downstream. Addversion: '<version_as_int>',after thelicensetag in theproject()function andversion: meson.project_version(),after thesoversiontag in bothlibrary()functions.

Using inih with tipi.build

inihcan be easily used intipi.buildprojects simply by adding the following entry to your.tipi/deps(replacer56with 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 usingvcpkgdependency 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, pleasecreate an issue or pull requeston the vcpkg repository.

Related links