Skip to content

roman-neuhauser/iniphile

Repository files navigation

Iniphile

Ini file parsing library for C++

Author: Roman Neuhauser
Contact: neuhauser@sigpipe.cz
Copyright: This document is in the public domain.

Iniphile is a C++ library and commandline tool for parsing a dialect of the INI file format.

Iniphile is known to build and run on FreeBSD 9 with gcc >= 4.2.1, and on Windows Vista with gcc >= 4.6.2 and Windows 7 with Visual Studio 2008 (vc9).

The library uses a few header-only Boost [1] libraries in both its interface and implementation. The interface mentions Boost.Optional [2] and Boost.Variant [3], The most prominent implementation detail is Boost.Spirit [4], specifically v2.1 (Boost [1] 1.41) or higher. Unit tests require Boost.Test [5] to compile and run.

Important

For now, Iniphile supports linking only against Boost libraries built with --layout=system, -DBOOST_ALL_NO_LIB is used unconditionally. This means that if your Boost libraries have funny names like boost_regex-vc90-mt-1_45.dll, linking the test executables will fail. The Iniphile library depends on header-only Boost libraries and the linker failure does not affect it. You just won't be able to run its unit tests.

notify.ini

[notify]
enabled   = No
addresses = alice@example.com
            bob@example.com
            chris@example.com

notify.cpp

#include <iostream>
#include <sstream>
#include <fstream>
#include <string>

#include "input.hpp"
#include "ast.hpp"
#include "output.hpp"

bool
notify(std::vector<std::string> const& addrs)
{
    // whatever
}

int
main()
{
    namespace ini = iniphile;

    std::string fname("notify.ini");
    std::ifstream input(fname);
    std::ostringstream diag;

    ini::parse_result cfg(ini::parse(fname, input, diag));

    if (!cfg) {
        std::cerr << "garbled configuration\n";
        return EXIT_FAILURE;
    }

    ini::ast::node afg(ini::normalize(*cfg));

    typedef std::vector<std::string> values;

    bool enabled  = ini::get(afg, "notify.enabled", false);
    long attempts = ini::get(afg, "notify.attempts", 3L);
    values addrs  = ini::get(afg, "notify.addresses", values());

    while (enabled && attempts--) {
        if (notify(addrs))
            return EXIT_SUCCESS;
    }
    return EXIT_FAILURE;
}

There's no standard definition of INI file syntax and semantics. The ABNF [6] grammar below approximates the dialect supported by this library (only significant whitespace is part of the grammar). See the following prose for clarifications).

inifile      =  *section
                EOF
section      =  *comentline
                headerline
                sectionbody
headerline   =  "[" sectionname "]" comment
sectionname  =  1*LCHAR
                ; except "]"
sectionbody  =  *(assignment / comment)
assignment   =  optname "=" optval
optname      =  1*VCHAR
                ; except "="
optval       =  ovlinefirst
                *ovlinecont
ovlinefirst  =  [ovline] comment
ovlinecont   =  1*WSP ovline comment
ovline       =  *optvalpart
optvalpart   =  qstring / bareword
qstring      =  DQUOTE *CHAR DQUOTE
bareword     =  1*VCHAR
                ; except ";"
comment      =  *1(";" *LCHAR) EOL
EOL          =  CRLF / CR /  LF
LCHAR        =  VCHAR / WSP

An inifile is a flat, possibly empty, collection of sections. A section begins at a headerline and continues until the next headerline or EOF, whichever comes first. Each section contains zero or more assignments, optname/optval pairs. Whitespace allowed around the "=". optname must begin at the start of a line. optval is a list of strings (optvalpart, either a qstring or a bareword) separated by whitespace. It may span multiple lines: continuation lines begin with horizontal whitespace, and qstrings may also contain newlines.

The tool is documented in an accompanying man page, iniphile(1).

Each header #includes all its dependencies.

  • metagram.hpp

    typedef unspecified iniphile::config;
    
  • input.hpp

    #include <boost/optional.hpp>
    
    typedef boost::optional<iniphile::config> parse_result;
    
    iniphile::parse_result
    iniphile::parse(
        std::string const& fname
      , std::string const& input
      , std::ostream& diag
    );
    
    iniphile::parse_result
    iniphile::parse(
        std::string const& fname
      , std::istream& input
      , std::ostream& diag
    );
    
  • astfwd.hpp

    #include <boost/variant.hpp>
    
    typedef std::vector<std::string> iniphile::valpath;
    
    valpath
    to_valpath(std::string const & path);
    
    typedef unspecified iniphile::ast::node;
    
  • ast.hpp

    #include "astfwd.hpp"
    
    iniphile::ast::node
    iniphile::normalize(iniphile::config const & cst);
    
  • output.hpp

    template<class T>
    T
    iniphile::get(
        iniphile::ast::node const& cfg
      , iniphile::valpath const& path
      , T dflt
    );
    
    template<class T>
    T
    iniphile::get(
        iniphile::ast::node const& cfg
      , std::string const& path
      , T dflt
    );
    

The library defines these specializations:

  • std::vector<std::string>

    One optvalpart per std::string. DQUOTEs from qstrings are not maintained.

  • std::string

    The whole optval with whitespace between optvalparts normalized to single spaces. DQUOTEs from qstrings are maintained.

  • bool

    The first optvalpart interpreted as a bool according to these rules:

    true  <= "1" / "true"  / "yes" / "on"  ; case insensitive
    false <= "0" / "false" / "no"  / "off" ; case insensitive
    
  • long

    The first optvalpart interpreted as a long using the boost::spirit::qi::long_ [7] parser (decimal representation with optional leading sign).

  • double

    The first optvalpart interpreted as a double using the boost::spirit::qi::double_ [7] parser (decimal representation of ints and doubles, scientific representation (nnn.fff.Eeee) of doubles.

Git [8] repository: http://github.com/roman-neuhauser/iniphile

Packages [9] for several GNU/Linux distributions are in the openSUSE Build Service [10].

Iniphile is distributed with a set of Makefiles suitable for several environments. Currently supported are: GNU make with the GNU C++ compiler, MSVC nmake with the Microsoft compiler.

Let $make be your make command, and let $env be gnu or msvc as appropriate. Then

$make -f $env.mk libs (requires Boost.Spirit [4])
creates a static library and a dll plus import library
$make -f $env.mk check (requires Boost.Test [5])
creates the libraries and runs their unit tests

Main variables and their default values:

CXX:
g++$(GCCVER)
GCCVER:
<empty>
PREFIX:
/usr/local
LIBDIR:
$(PREFIX)/lib
INCDIR:
$(PREFIX)/include
PKGCONFIGDIR:
$(LIBDIR)/pkgconfig
_BOOST:
$(PREFIX)
SPIRIT:
$(_BOOST)/include
UTFINC:
$(_BOOST)/include
UTFLIB:
$(_BOOST)/lib
UTFRUN:
$(UTFLIB)
gmake -f gnu.mk <targets-and-variables>

pkg-config files are stored in a nonstandard path on this system. If you install into /usr/local, make sure to do so with PKGCONFIGDIR=/usr/local/libdata/pkgconfig.

make -f gnu.mk <targets-and-variables>

gnu.mk assumes the GNU gcc suite.

Main variables and their default values:

PREFIX:
.\stage
INCDIR
$(PREFIX)\include
LIBDIR
$(PREFIX)\lib
BOOST_INCDIR:
<empty>
BOOST_LIBDIR:
<empty>

msvc.mk assumes and relies on tools provided with MS Visual C++ Express exclusively. It's tested on Windows Vista and 7 with VC++ 2008/9.0 Express, and works in the Visual Studio Command Prompt and the Windows SDK CMD Shell.

nmake -f msvc.mk <targets-and-variables>

No attempt to guess or find the Boost prerequisities is made, you must supply BOOST_INCDIR and BOOST_LIBDIR or the compilation will fail.

nmake -f msvc.mk install puts built libraries in $(LIBDIR) (defaults to $(PREFIX)\lib) and header files in $(INCDIR) (defaults to $(PREFIX)\include).

[1](1, 2) http://boost.org/
[2]http://www.boost.org/doc/libs/1_42_0/libs/optional/index.html
[3]http://www.boost.org/doc/libs/1_42_0/libs/variant/index.html
[4](1, 2) http://www.boost.org/doc/libs/1_42_0/libs/spirit/index.html
[5](1, 2) http://www.boost.org/doc/libs/1_42_0/libs/test/index.html
[6]http://tools.ietf.org/html/std68
[7](1, 2) http://www.boost.org/doc/libs/1_41_0/libs/spirit/doc/html/spirit/qi/reference/numeric.html
[8]http://git-scm.org/
[9]http://software.opensuse.org/download.html?project=home:roman-neuhauser&package=iniphile
[10]https://build.opensuse.org/