Cracking an INI file with a jackhammer

INI File SnippetAll I was after was a simple .INI file reader in C or C++. You know, to parse [section] and name=value lines for config files. We needed it for an embedded Linux project, so it had to be small, portable and only dependent on the C and C++ standard libraries.

Maybe it’s my embedded background that makes me defensive about my mild case of Not-Invented-Here syndrome. Then again, perhaps I’m in good company.

But surely there are tons of INI parsing libraries around, right? Well, kind of.

I found no fewer than 15 in under an hour. But why is it that things like this are either way off the mark (bloated or non-portable) or they’re very close, but just not quite what you want?

Below is the list of INI file readers that I found, from bad to better. Some of the ones that weren’t for me might well suit your application, so treat this as something of a non-exhaustive list of INI file readers:

Not for me

Half way there

So very close

INI Not Invented Here (INIH)

Of course, in the time it took to investigate all these, I could have easily written my own. And, being unable to help myself, I did. :-) So I present you with my own offering: INI Not Invented Here, a.k.a. INIH or simply ini.h.

It contains a small C function, ini_parse(), that parses an INI file and executes the given callback function for each name=value pair parsed (think SAX).

The reason I used the callback style is so you don’t have to load the whole file into memory if you don’t need to — good for embedded systems. Plus, I wanted to be able to use the parser easily in C, but not implement a dictionary-like structure in C.

For a more user-friendly, lookup style API if you’re using C++, I’ve wrapped ini_parse() in a class called INIReader, which has Get() and GetInteger() methods (all I needed). And it’s easy to sub-class INIReader if you need fancier GetXYZ() functions.

Show us the code

UPDATE: I’ve moved the code from here to its own Google Code project at http://code.google.com/p/inih/ — enjoy!

26 February 2009 by Ben    44 comments

44 comments and pings (oldest first)

paavels 26 Feb 2009, 23:03 link

Thanks for inventing the bicycle.

Btw wxWidgets does it rather than perfect

http://docs.wxwidgets.org/stable/wx_wxconfigbase.html#wxconfigbase

danijel 27 Feb 2009, 04:34 link

However useless the above comment is, he does have a point. You see, you were complaining on how difficult it is to find the right library for your program out of so many available on the internet and then you go ahead and make another one yourself. It is very likely that someone else who stumbles upon your code will have the same sentiment as you did with the others…

Eoin 27 Feb 2009, 05:01 link

Seems quite simple, straighforward and portable. Bravo!

Alvaro 27 Feb 2009, 06:19 link

I my own little utils library I have support for this. Using it looks like this:

IniFile config("config.ini");
config.section("user");
String email = config["email"];

or with section/variable :

IniFile config("config.ini");
String username = config["user/name"];

or all in one line, without creating a config object:

int protocol = IniFile("config.ini")["protocol/version"];
FPM 27 Feb 2009, 07:20 link

I used crack INI files with jackhammer back when I was a homeless rodeo clown but not any more. Now I am a world class magician !

Ben 27 Feb 2009, 10:06 link

Hi Alvaro, that’s quite neat — great use of operator overloading. Out of interest, I don’t suppose you could post the code somewhere?

Danijel, you’re right about someone else not finding my code good for their purposes. I guess that’s part of the attraction of NIH — you get exactly what you want, no more, no less.

Ping: Small regex libraries 27 Feb 2009, 10:55 link

[…] I’m on the lookout for small libraries anyway, I thought I’d also post a list of a few regular expression libraries I’ve found […]

[…] The Brush Blog :: Cracking an INI file with a jackhammer (tags: cpp) […]

Ben 28 Feb 2009, 14:22 link

For reference, here’s a simple C-based example for using ini_parse() to parse your program’s config file:

/* Simple INIH example */

#include "ini.h"
#include <string.h>

typedef struct {
    char* name;
    int age;
} config;

int config_handler(void* user, const char* section,
                   const char* name, const char* value)
{
    config* cfg = (config*)user;

    if (stricmp(section, "user") == 0) {
        if (stricmp(name, "name") == 0)
            cfg->name = strdup(value);
        else if (stricmp(name, "age") == 0)
            cfg->age = atoi(value);
    }
}

void run_program(config* cfg)
{
    printf("%s is %d years old\n", cfg->name, cfg->age);
}

void main()
{
    config cfg = {"Anon Y Mous", 42};  /* Default config */

    ini_parse("config.ini", config_handler, &cfg);
    run_program(&cfg);
}
Ludvik Jerabek 28 Apr 2009, 02:06 link

C++ class, but uses loads of non-portable MFC helper classes. It’s funny you mention it’s non portable which is 100% correct. I wrote one which was in standard C++ and STL if there is still interest in this let me know.

Anon 6 May 2009, 00:59 link

I was looking for exactly this. If not you I would have written it. Extremely lightweight yet economic. Will use thx!

Ludvik Jerabek 20 May 2009, 08:17 link

Here is the text for that Ansi C++ code [snipped]. I will also update the code project page to have this available.

Updated project is located at:

http://www.codeproject.com/KB/cpp/CIniFile.aspx

Ri 22 May 2009, 22:42 link

To be honest, I’d rather re-invent the wheel as well (only way to know where your code’s been and who touched it :P ) but since I don’t have enough experience yet – thank you for writing code that even I can follow and use :)

jw 2 Apr 2011, 12:46 link

Thank you. Thank you. Thank you.

I too had the experience of wading through the swamp of INI-parsers out there. Then I found yours, and it was good (:

One thing that is handy and absolutely trivial to add is a callback for when the section changes.

jenya 24 May 2011, 02:13 link

Dear Sir, I found your ini file parser very useful. When i work with known section names it’s perfect. How can i get all section names in array, and corresponding values in another array? Best Regards. Evgeny.

Ben 25 May 2011, 01:27 link

Hi Jenya, there’s no way of doing this with what’s built into inih. However, what you’d do is make a data structure, something like an array of arrays, and have your handler() function store the section name and section values into this array.

Something like the below (without fleshing out the data structure functions):

static int handler(void* user, const char* section, const char* name,
                   const char* value)
{
    if (!has_section(section)) {
        add_section(section);
    }
    add_value(section, name, value);
}
Mark 16 Jun 2011, 12:55 link

I had a senior developer come down on me because I was calling minini from a heavily used function. I should’ve checked the minini code but who would have thought. Learned that lesson really quick.

ben 11 Oct 2012, 03:14 link

prob with the callback function approach is it requires a global variable if you want to ‘remember’ if you’re in the same section (or possibly a static variable) – both of which are a bit of a pain

Ben 11 Oct 2012, 07:58 link

A global is one way of doing it, but you could also use a “state struct” local to the function you’re calling ini_parse() from, and pass the address of that struct in as the “user” parameter. Actually, come to think of it, that’s what the main C example does on the inih project page.

But instead of just configuration the struct could contain any extra parsing state you need, like what section/name you processed last. Note that you’d have to copy or strdup() the section and name rather than just store pointers to them, because their lifetime is only as long as the handler function.

ben 11 Oct 2012, 21:40 link

yes – adding an extra member to the struct to handle state is probably the cleanest route. this issue makes me think of closures – not an option in C though, e.g. taken from wikipedia ‘A closure can be used to associate a function with a set of “private” variables, which persist over several invocations of the function. The scope of the variable encompasses only the closed-over function, so it cannot be accessed from other program code.’

[…] La meilleure ressource que j’ai pu trouver sur le sujet me semble être la page suivante : http://blog.brush.co.nz/2009/02/inih/ On y trouvera plusieurs projets dont le code est adapté à l’embarqué notamment à […]

Baris Evrim Demiroz 14 Aug 2013, 00:32 link

Thank you very much for this excellent, light-weight ini reader with C++ wrapper. I see the project is still somewhat active, do you plan to add INIReader::GetDouble() method? (I have already added that in my source code)

Ben 15 Aug 2013, 16:55 link

Hi Baris — I added GetReal() in r28. Good idea — thank you!

Alan 16 Sep 2014, 23:49 link

Read ini file funcina perfectly. However like to know how to create it.

Petr 19 Sep 2014, 04:12 link

Good!, senk’s. where is parse multilne and append to array? [test] testing = aaa testing = bbb testing = ccc …. etc

Ben 19 Sep 2014, 06:20 link

@Alan, inih is just for reading .ini files. No plans at this stage to make it write them.

@Petr, can you be a bit more specific? If you want to append to an array, you’ll have to do it yourself (C doesn’t have very high-level array support anyway). Or are you referring to the C++ API?

Petr 19 Sep 2014, 11:56 link

Thanks for answer. i’m not this moment of current project use C++ API :(

Petr 30 Oct 2014, 04:41 link

Hi, where is work for example?

typedef struct { int version; const char* name; const char* email; } configuration;

configuration * cnf = malloc(sizeof(configuration));

… other code… INI not update this config pointer from handle function

Ben 30 Oct 2014, 05:14 link

Hi Petr, there are some basic examples right on the project homepage: https://code.google.com/p/inih/

Petr 30 Oct 2014, 06:44 link

Yes, i’m ready. Where is correct use construction: configuration * cnf = malloc(sizeof(configuration));

?

Petr 30 Oct 2014, 06:45 link

if function handle not sccesing real link to config structure. Creates is new config instance..

Petr 30 Oct 2014, 08:11 link

Well, answer: pconfig = ((configuration *)(user));

int iniparse(const char* filename, int (handler)(void* confstruct, const char* section, const char* name, const char* value), void* confstruct); int iniparse_file(FILE* file, int (handler)(void* confstruct, const char* section, const char* name, const char* value), void** confstruct);

Petr 30 Oct 2014, 08:12 link

Sorry, wrong charscters, again:

configuration pconfig = ((configuration *)(user));

Petr 30 Oct 2014, 08:13 link

Web engine not correct ‘asteriks’ *user))

mahtias 6 Dec 2014, 22:15 link

So can I use this lib in a commercial project? I am not an expert in all this open source lic´s

thanks for your help

Ben 7 Dec 2014, 02:49 link

@mahtias, yes, it’s BSD-licensed, so commercial use is fine. See: https://inih.googlecode.com/svn/trunk/LICENSE.txtx

mathias 10 Dec 2014, 19:37 link

ok, thanks

Nal 21 Jan 2015, 02:35 link

Is there any way to use this inside a for loop, for when you want to read many .ini files?

Bob Smith 3 Mar 2015, 10:55 link

Hi,

I was wondering if this allows for multiple sections with the same name (so, something like multiple [user] sections).

I’m a little new to C, and it isn’t quite clear to me whether something like this might be possible from the examples.

Thanks!

Ben 3 Mar 2015, 12:39 link

@Bob, yes, the C API allows for multiple sections with the same name, as it just reads the file sequentially and hands the values to you. You should be able to test this with the examples/ini_dump.c program.

Bob Smith 3 Mar 2015, 16:31 link

Hmm, I managed to compile ini_dump.c (inih_r29) and tried this, and what I’m seeing is that everything is returned under one giant [user] section.

I’m thinking that this might just be how the “if” statement in the dumper function is handling another section with the same name.

So, if I’m understanding this correctly, inih will do what I want, assuming I use it properly (by passing it a proper handler / dumper), that does I’m looking for.

I think where I’m getting confused is how I would tell that a new “[user] section” has started, so as to not get the values for one [user] section confused with another.

Thanks!

Ben 4 Mar 2015, 00:30 link

@Bob, that’s a good question. Yes, I didn’t think about that — if the two same-named [user] sections are right next to each other you won’t be able to tell when one ends and the next starts, because the handler just gets passed the names.

You could use a “start_of_section=yes” type of marker as the first field in each section.

However, I wouldn’t recommend this use of inih, as not all INI parsers support sections with the same name etc. I’d probably go with the (dumber but more portable) approach where you use section [user1], then section [user2], etc. And the handler would do “if section name starts with ‘user’…”

Louis 30 Sep 2017, 10:06 link

I think this guy wrote not just an INI parser, but standardized even the idea of what an INI file is:

https://github.com/madmurphy/libconfini

rob 30 May 2018, 01:01 link

@Ben Thank you! Minimal but smooth!

@Louis I played a bit with libconfini and it does look like the ultimate INI parser. The only problem is that hacking the source code seems quite challanging (the text formatting functions are written in some obscure C full of bitwise operators). But if you accept the library as it is it just works like a charm.