Git Product home page Git Product logo

libconfini's Introduction

libconfini {#readme}

libconfini is the ultimate and most consistent INI file parser library written in C. Originally designed for parsing configuration files written by other programs, it focuses on standardization and parsing exactness and is at ease with almost every type of file containing key/value pairs.

The library is fast and suitable for embedded systems. Its algorithms are written from scratch and do not depend on any external library, except for the C standard headers stdio.h, stdlib.h, stdbool.h and stdint.h (and for extreme platforms the code can be also compiled as “bare metal”, with few or no strings attached to the C Standard Library).

Rather than storing the parsed data, libconfini gives the developer the freedom to choose what to do with them through a custom callback invoked for each INI node read. The API has been designed to be powerful, flexible and simple to use.

With libconfini you will find in INI files the same serialization power that you would normally find in other heavily structured formats (such as JSON, YAML, TOML), but with the advantage of using the most human-friendly configuration format ever invented (thanks to their informal status, INI files are indeed more fluid, expressive and human-friendly than formats explicitly designed with the same purpose, such as YAML and TOML). The library's main goal is to be uncommonly powerful in the most tedious and error-prone task when parsing a text file in C: string handling. Thanks to this, the programmer is left free to organize the content parsed as (s)he pleases, assisted by a rich set of fine-tuning tools.

Features

  • Typed data support (each value can be parsed as a boolean, a number, a string, an array)
  • Single/double quotes support in Bash style (i.e. quotes can be opened and closed multiple times within the same value)
  • Multi-line support
  • Comment support
  • Disabled entry support
  • INI sectioning support (single-level sectioning, as in [foo]; absolute nesting, as in [foo.bar]; relative nesting, as in [.bar])
  • Automatic sanitization of values, key names and section paths
  • Comparison functions designed just for INI source code (capable, for example, to recognize the equality between "Hello world" and "He"l'lo' world, or between foo bar and foo    bar)
  • Callback pattern
  • Thread-safety (each parsing process is fully reentrant)
  • Highly optimized code (single memory allocation for each parsing, heuristic programming, optimization flags)
  • Function modularity (each public function is independent from all other public functions)
  • K.I.S.S. (no public functions are automatically invoked during the parsing -- for example, not even single/double quotes are automatically removed from values unless the developer explicitly decides to use the formatting functions provided by the API)
  • Robust and cross-platform file access (UTF-8 support; protection against null byte injection; support of all code representations of new lines -- i.e. ubiquitous support of Classic Mac OS' CR, Unix' LF, Windows' CR+LF, RISC OS Open's LF+CR)

Sample usage

log.ini:

[users]
have_visited = ronnie, lilly82, robrob

[last_update]
date = 12.03.2017

example.c:

#include <confini.h>

static int callback (IniDispatch * disp, void * v_other) {

  #define IS_KEY(SECTION, KEY) \
    (ini_array_match(SECTION, disp->append_to, '.', disp->format) && \
    ini_string_match_ii(KEY, disp->data, disp->format))

  if (disp->type == INI_KEY) {

    if (IS_KEY("users", "have_visited")) {

      /* No need to parse this field as an array right now */
      printf("People who have visited: %s\n", disp->value);

    } else if (IS_KEY("last_update", "date")) {

      printf("Last update: %s\n", disp->value);

    }

  }

  #undef IS_KEY

  return 0;

}

int main () {

  if (load_ini_path("log.ini", INI_DEFAULT_FORMAT, NULL, callback, NULL)) {

    fprintf(stderr, "Sorry, something went wrong :-(\n");
    return 1;

  }

  return 0;

}

Output:

People who have visited: ronnie, lilly82, robrob
Last update: 12.03.2017

If you are using C++, under examples/cplusplus/map.cpp you can find a snippet for storing an INI file into a class that relies on a std::unordered_map object.

For more details, please read the Library Functions Manual (man libconfini) -- a standalone HTML version is available here -- and the manual of the header file (man confini.h). The code is available on GitHub under madmurphy/libconfini).

Installation

Despite the small footprint, libconfini has been conceived as a shared library (but it can be used as a static library as well). An automatic list of the distributions that ship the library already compiled is available here.

If a pre-compiled package for your platform is not available, on most Unix-like systems it is possible to install libconfini using the following common steps:

./configure
make
make install-strip

If the strip utility is not available on your machine, use make install instead (it will produce larger binaries)

For a minimum installation without development files (i.e. static libraries, headers, documentation, examples, etc.) use ./configure --disable-devel.

If the configure script is missing you need to generate it by running the bootstrap script. By default, bootstrap will also run the configure script immediately after having generated it, so you may type the make command directly after bootstrap. To list different options use ./bootstrap --help.

If you are using Microsoft Windows, a batch script for compiling libconfini with MinGW without Bash is available (mgwmake.bat). If you are interested in using GNU Make for compiling libconfini, you can integrate MinGW with MSYS, or you can directly use MSYS2 and its official port of the library. Alternatively, an unofficial port of libconfini for Cygwin is available.

For further information, see INSTALL.

Bindings

Danilo Spinella maintains a D binding package of libconfini.

Free software

This library is free software. You can redistribute it and/or modify it under the terms of the GPL license version 3 or any later version. See COPYING for details.

libconfini's People

Contributors

joeux avatar madmurphy avatar ntapiam avatar sylveon avatar timgates42 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

libconfini's Issues

Re: TOML performance

I'm not sure where else to report this, but re: https://github.com/madmurphy/libconfini/wiki/An-INI-critique-of-TOML#18-performance

This is really a tomlc99 issue, not a TOML issue. The problem is that it allocates memory in 1,000 byte chucks, which takes quite a while (see start of toml_parse_file()), and after this it spends most of its time in check_key(), which loops over an array of keys, and is called every time it parses a new key, so ... yeah,

But these are not issues with TOML the file format. Other parsers are much faster; for example:

% ls -lh ~/50M.toml
-rw-r--r-- 1 martin martin 49M Sep 30 20:23 /home/martin/50M.toml

With C++:

% time ./tomlv++ ~/50M.toml
./tomlv++ ~/50M.toml  2.19s user 0.15s system 99% cpu 2.348 total

Go:

% time tomlv-go ~/50M.toml
tomlv-go ~/50M.toml  6.31s user 0.51s system 154% cpu 4.421 total

And Python:

% time ./tomlv.py ~/50M.toml
./tomlv.py ~/50M.toml  11.95s user 0.42s system 99% cpu 12.413 total

None are spectacularly fast as such, but orders of magnitudes faster than tomlc99, which is the outlier here. I also don't know how this compares to the 50M test-file you used (the TOML file lacks comments and is more "dense" in syntax than the big_file.ini that your script generates, so it's probably not a fair comparison).

Most of the other points in that article are more subjective, but I felt this one was clearly wrong.

Standalone manager program?

Is there, or is it planned somehow, a standalone program to handle ini-files from the shell?
I am looking for something like crudini but without the python overhead.

If this is not available/planned I could volunteer to write such a thing, if there's interest.

Question about licensing

I've compared some ini file parsers now, and libconfini really fits best to my needs: It is easy-to-use, compact and will also run on a very simple embedded system without considerable memory consumption or runtime overhead. Really a nice pice of software!

The only drawback is its license for me: Due to the roles of GPL v3.0, all the code using libconfini also has to be put under GPL v3.0. For open source projects this should be no problem, but for a commercial application like mine this is not possible.

Do you think it would be possible to put libconfini under a less restricted license like MIT, maybe under a dual-license? Thank you!

Update Installation Guide

You are not mentioning that ./autoconf.sh must be run. The ./configure file does not exist in the repository!

The installation guide should say to run:

./autogen.sh
make
make install

instead of:

./configure
make
make install

new interface function

My embedded program dynamic generate small config data frequently.
these conig data used by another part of the same program.
So use a file to transfer the config data seems strange and too slow.

Could you give a new interface func which get config data from a "const char *"

Both load_ini_path and load_ini_file method calls fail from C++ code

I have built libconfini and referenced the generated file libconfini\src\.libs\libconfini-0.dll in an empty C++ Managed Build project using Eclipse CDT on a Windows 7 host with mingw64 (gcc version 8.2).

Building my project TestIniReader works fine, but when calling my application the output is always:

The file content is:
[users]
have_visited = ronnie, lilly82, robrob

[last_update]
date = 12.03.2017
Sorry, something went wrong :-(
To quit, press 'q' then the 'Enter' key.

The source code (see TestIniReader.zip) is pretty much the same as mentioned in the documentation of libconfini, still the callback method does not seem to get called. The line "This is a message!" is never printed to console.

How can I find out what the couse for the failed method call to load_ini_path is?

Heap buffer overflow when parsing a simple test file with a multiline comment

I was testing libconfini v1.16.1 (latest, current master branch) with some randomly generated files when I found the following file causes an heap buffer overflow when parsed by the library with INI_MULTILINE_EVERYWHERE:

#\
# 'x'

The file is 9 bytes, though the trailing newline does not matter, the bug can be reproduced anyway.

Full test program to reproduce the bug (compile libconfini with CFLAGS='-O0 -g -fsanitize=address' and link statically):

#include <confini.h>

static const IniFormat my_ini_format = { 
	.delimiter_symbol         = INI_ANY_SPACE, 
	.case_sensitive           = false, 
	.semicolon_marker         = INI_IGNORE, 
	.hash_marker              = INI_DISABLED_OR_COMMENT, 
	.section_paths            = INI_ABSOLUTE_AND_RELATIVE, 
	.multiline_nodes          = INI_MULTILINE_EVERYWHERE, 
	.no_single_quotes         = false, 
	.no_double_quotes         = false, 
	.no_spaces_in_names       = false, 
	.implicit_is_not_empty    = false, 
	.do_not_collapse_values   = false, 
	.preserve_empty_quotes    = false, 
	.disabled_after_space     = false, 
	.disabled_can_be_implicit = false 
};

int main(void) {
	load_ini_path("test.ini", my_ini_format, NULL, NULL, NULL);
	return 0;
}

ASAN backtrace:

==21423==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000001a at pc 0x0000004027e7 bp 0x7ffde47d8fb0 sp 0x7ffde47d8fa8
READ of size 1 at 0x60200000001a thread T0
    #0 0x4027e6 in getn_metachar_pos /home/dummy/src/libconfini/src/confini.c:973
    #1 0x405dcc in get_type_as_active /home/dummy/src/libconfini/src/confini.c:1839
    #2 0x406c40 in further_cuts /home/dummy/src/libconfini/src/confini.c:2059
    #3 0x40820a in strip_ini_cache /home/dummy/src/libconfini/src/confini.c:2479
    #4 0x40a499 in load_ini_path /home/dummy/src/libconfini/src/confini.c:3136
    #5 0x401286 in main /home/dummy/src/main.c:21
    #6 0x7f67d8b5a09a in __libc_start_main ../csu/libc-start.c:308
    #7 0x4011a9 in _start (/home/dummy/src/test+0x4011a9)

0x60200000001a is located 0 bytes to the right of 10-byte region [0x602000000010,0x60200000001a)
allocated by thread T0 here:
    #0 0x7f67d8de0330 in __interceptor_malloc (/lib/x86_64-linux-gnu/libasan.so.5+0xe9330)
    #1 0x40a40b in load_ini_path /home/dummy/src/libconfini/src/confini.c:3116
    #2 0x401286 in main /home/dummy/src/main.c:21
    #3 0x7f67d8b5a09a in __libc_start_main ../csu/libc-start.c:308

I'm not entirely sure about the logical nature of the bug since the source code for the library seems quite... esoteric, but it seems to originate here:

libconfini/src/confini.c

Lines 2044 to 2075 in 409b9ee

focus_at = dqultrim_s(srcstr, search_at, format);
idx = ltrim_s(srcstr, idx + 1, _LIBCONFINI_WITH_EOL_);
if (
abcd & 2048 ?
!(
_LIBCONFINI_IS_DIS_MARKER_(srcstr[idx], format) &&
(abcd & 24) && (
(~abcd & 516) ||
!is_some_space(srcstr[idx + 1], _LIBCONFINI_NO_EOL_)
)
) && !(
_LIBCONFINI_IS_COM_MARKER_(srcstr[idx], format) &&
(abcd & 8) && (
((abcd ^ 512) & 8704) ||
!get_type_as_active(
srcstr + focus_at,
idx - focus_at,
format.disabled_can_be_implicit,
format
)
)
)
:
!_LIBCONFINI_IS_IGN_MARKER_(srcstr[idx], format)
) {
rtrim_h(srcstr, idx, _LIBCONFINI_WITH_EOL_);
search_at = qultrim_h(srcstr, idx, format);
goto search_for_cuts;
}

The function dqultrim_s() returns 5 while ltrim_s() returns 3, which later causes an unsigned integer overflow when calculating idx - focus_at at line 2061, causing get_type_as_active() to be called with len = 18446744073709551614, running past the end of the allocated buffer.

Write to .ini support

Hi there,

I liked a lot this library and I was thinking in adding support for writing .ini files. Is this feature been planned for a future release?

Best regards,
F.Borges

Question about calling load_ini_file() from inside the IniDispHandler

Is there a problem in calling load_ini_file() from inside iniDispHandler?.

e.g.
top.ini
[Global]
Include = child.ini

[Something]
else=here

my iniParse() function which processes the keeps its current state in the user_data.. But I seem to be unable to continue to parse the top file once the child.ini is done.

Entirely possible its my code ;-), could you confirm if load_ini_file() is reentrant or I'm barking up the wrong tree?.

Thanks in advance

Can not build in Debian Stretch

~$ cat /etc/debian_version
9.2
~$ uname -r
4.9.0-3-amd64

~$ git clone https://github.com/madmurphy/libconfini.git
.......
~$ cd libconfini
~/libconfini$ ./autogen.sh
.........
~/libconfini$ make
make  all-recursive
make[1]: Entering directory '/home/user1/libconfini'
Making all in src
make[2]: Entering directory '/home/user1/libconfini/src'
  CC       confini.lo
  CCLD     libconfini.la
ar: `u' modifier ignored since `D' is the default (see `U')
make[2]: Leaving directory '/home/user1/libconfini/src'
Making all in po
make[2]: Entering directory '/home/user1/libconfini/po'
make[2]: Nothing to be done for 'all'.
make[2]: Leaving directory '/home/user1/libconfini/po'
make[2]: Entering directory '/home/user1/libconfini'
make[2]: Leaving directory '/home/user1/libconfini'
make[1]: Leaving directory '/home/user1/libconfini'

Can not find binary libconfini libs. What's wrong?

Building on macOS

I was trying to build the library on macOS Sierra 10.12.4 and had to install GNU libtool and libtoolize from Homebrew. The thing is the commands are called glibtool and glibtoolize in order to avoid conflict with Apple's, which apparently is unrelated to GNU's, so I had to edit autogen.sh in order to reflect this.
Would you consider testing for this tools also in the script?

Issues with dispatch in spite of correct file location

Second Function:

static int configure_program(IniDispatch *dispatch, void *_program) {
    if (dispatch->type == INI_KEY) {
        PROGRAM *program = (PROGRAM *) _program;

        ini_unquote(dispatch->data, dispatch->format);
        ini_string_parse(dispatch->value, dispatch->format);

        if (ini_string_match_si("language_folder", dispatch->data, dispatch->format)) {
            program->language_directory = dispatch->value;
            return 0;

        } else if (ini_string_match_si("save_folder", dispatch->data, dispatch->format)) {
            if (strcmp("NONE", dispatch->value) == 0) {
                 program->save_directory = NULL;
            } else {
                program->save_directory == dispatch->value;
            }
            return 0;
                    
        } else if (ini_string_match_si("multithreading", dispatch->data, dispatch->format)) {
            if (strcmp("TRUE", dispatch->value) == 0) {
                program->multithreading_enabled = 1;
            } else {
                program->multithreading_enabled = 0;
            }
            return 0;

        } else {
            return 0;
        }
    }
}

First Function:

int load_settings(PROGRAM *program) {
    char program_directory[MAX_PATH];

    // returns 0 on failure; don't change
    if (!ExpandEnvironmentStrings("%SHIROI_INSTALL%", program_directory, MAX_PATH)) {
        puts("Failed to find program's directory. Make sure to define %SHIROI_INSTALL% or relaunch setup.bat");
        return 1;
    }

    strcat(program_directory, "\\shiroi-settings.ini");

    printf("Reading %s...\n", program_directory);

    FILE *settings_file = fopen(program_directory, "rb");

    if (settings_file == NULL) {
        puts("Couldn't find settings file");
        return 1;
    }
    
    if (load_ini_file(settings_file, INI_DEFAULT_FORMAT, NULL, configure_program, &program)) {
        puts("Couldn't load settings file. Perhaps invalid?");
        return 1;
    }

    fclose(settings_file);

    return 0;
}

examples fail in ubuntu 20.04 lts

==> Please choose a number to compile and run (q to exit): 1
-> Compiling "cplusplus/generic.cpp" with gcc
(g++ -Wall -pedantic -lconfini 'cplusplus/generic.cpp')...
/usr/bin/ld: /tmp/ccisF92m.o: in function callback(IniDispatch*, void*)': generic.cpp:(.text+0x43): undefined reference to ini_array_match'
/usr/bin/ld: generic.cpp:(.text+0x65): undefined reference to ini_string_match_ii' /usr/bin/ld: generic.cpp:(.text+0xd9): undefined reference to ini_array_match'
/usr/bin/ld: generic.cpp:(.text+0xfb): undefined reference to ini_string_match_ii' /usr/bin/ld: /tmp/ccisF92m.o: in function main':
generic.cpp:(.text+0x19a): undefined reference to `load_ini_path'
collect2: error: ld returned 1 exit status
-> Couldn't compile file "cplusplus/generic.cpp".

it compiles fine with this command
g++ -Wall -pedantic generic.cpp -lconfini

but then fails to run
./a.out: error while loading shared libraries: libconfini.so.0: cannot open shared object file: No such file or directory

but works when i do this
./configure --prefix=/usr

i can reproduce the error with the following...
lxc launch ubuntu:20.04 delme
lxc shell delme
git clone https://github.com/madmurphy/libconfini.git
apt update
apt install -y libtool-bin autoconf automake make gawk g++
cd libconfini/
./bootstrap
make install
/usr/local/share/doc/libconfini/examples/run-example.sh

Shebang in autogen.sh should be #!/bin/bash

Starting ./autoget.sh on an Ubuntu 18.04 machine, I'm getting
./autogen.sh: Syntax error: "(" unexpected
The reason is:

  • autogen.sh uses an array in line 7: CONFIGURE_ARGS=()
  • by default, autogen.sh is interpreted by sh
  • sh seems not to support arrays in this way

The solution for me was to replace !#/bin/sh by !#/bin/bash.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.