Parsing a .ini File in C

Recently while workig on a project I had to parse the config from a .ini file. I found inih on github, a library for parsing .ini file in c which is very easy to use.

It contains just two files one is a .c file and the other one is a .h file. So it can be easily put into a project (as it does not have any other depencancy apart from c standard library).

To use inih, get a copy of those files

$ git clone https://github.com/benhoyt/inih
$ cd inih

You will find the two files in that folder, ini.c and ini.h those are the two files you will need for parsing an ini file.

The sample .ini file can be found in examples of inih. project on github.

Although I’ll be using a different sample file in this post which is as follows.

[Section 1]
string-value = string one ; a string value
int-value = 1             ; an integer value
float-value = 2.7182      ; float value

[Section 2]
string-value = string two ; a string value
int-value = 2             ; an integer value
float-value = 1.4142      ; float value

To parse this file the code looks like.

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


// define a structure for holding the values in "Section 1".
typedef struct{
    int int_val;
    float float_val;
    const char* string_val;
} section_one;

// define a structure for holding the values in "Section 2".
typedef struct{
    int int_val;
    float float_val;
    const char* string_val;
} section_two;

// define a structure for holding all of the config.
typedef struct
{
    section_one s1;
    section_two s2;
} configuration;

static int handler(void* config, const char* section, const char* name,
                   const char* value)
{
    // config instance for filling in the values.
    configuration* pconfig = (configuration*)config;

    // define a macro for checking Sections and keys under the sections.
    #define MATCH(s, n) strcmp(section, s) == 0 && strcmp(name, n) == 0

    // fill the values in config struct for Section 1.
    if(MATCH("Section 1", "string-value")){ 
        pconfig->s1.string_val = strdup(value);
    }else if(MATCH("Section 1", "int-value")){
        pconfig->s1.int_val = atoi(value);
    }else if(MATCH("Section 1", "float-value")){
        pconfig->s1.float_val = strtof(value, NULL);

    // fill the values in config struct for Section 2.
    }else if(MATCH("Section 2", "string-value")){
        pconfig->s2.string_val = strdup(value);
    }else if(MATCH("Section 2", "int-value")){
        pconfig->s2.int_val = atoi(value);
    }else if(MATCH("Section 2", "float-value")){
        pconfig->s2.float_val = strtof(value, NULL);
    }else{
        return 0;
    }

    return 1;
}

// function for pretty-printing.
void pp_config(configuration config){
    printf("\nCONFIG---\n\n");

    printf("[Section 1]\n");
    printf("string-value: %s\n", config.s1.string_val);
    printf("int-value: %d\n", config.s1.int_val);
    printf("float-value: %f\n", config.s1.float_val);

    printf("\n");

    printf("[Section 2]\n");
    printf("string-value: %s\n", config.s2.string_val);
    printf("int-value: %d\n", config.s2.int_val);
    printf("float-value: %f\n", config.s2.float_val);
}

int main(int argc, char* argv[])
{
    // config for holding values.
    configuration config;

    // parse the .ini file
    if (ini_parse("test.ini", handler, &config) < 0) {
        printf("Can't load 'test.ini'\n");
        return 1;
    }

    // print the config.
    pp_config(config);

    // free the memory before exiting.
    free((void*)config.s1.string_val);
    free((void*)config.s2.string_val);

    return 0;
}

Compile the above code.

$ gcc -o parse-ini ini.c parse-ini.c

Running parse-ini

$ ./parse-ini 

CONFIG---

[Section 1]
string-value: string one
int-value: 1
float-value: 2.718200

[Section 2]
string-value: string two
int-value: 2
float-value: 1.414200