**Config**: Parsing configuration files
***************************************

*gnatcoll* provides a general framework for reading and manipulating
configuration files. These files are in general static configuration
for your application, and might be different from the preferences that
a user might change interactively. However, it is possible to use them
for both cases.

There are lots of possible formats for such configuration files: you
could chose to use an XML file (but these are in general hard to edit
manually), a binary file, or any other format. One format that is
found very often is the one used by a lot of Windows applications (the
".ini" file format).

*GNATCOLL.Config* is independent from the actual format you are using,
and you can add your own parsers compatible with the *GNATCOLL.Config*
API. Out of the box, support is provided for ".ini" files, so let's
detail this very simply format:

   # A single-line comment
   [Section1]
   key1 = value
   key2=value2

   [Section2]
   key1 = value3

Comments are (by default) started with *'#'* signs, but you can
configure that and use any prefix you want. The *(key, value)* pairs
are then organized into optional sections (if you do not start a
section before the first key, that key will be considered as part of
the *""* section). A section then extends until the start of the next
section.

The values associated with the various keys can be strings, integers
or booleans. Spaces on the left and right of the values and keys are
trimmed, and therefore irrelevant.

Support is providing for interpreting the values as file or directory
names. In such a case, if a relative name is specified in the
configuration file it will be assumed to be relative to the location
of the configuration file (by default, but you can also configure
that).

*GNATCOLL.Config* provides an abstract iterator over a config stream
(in general, that stream will be a file, but you could conceptually
read it from memory, a socket, or any other location). A specific
implementation is provided for file-based streams, which is further
specialized to parse ".ini" files.

Reading all the values from a configuration file is done with a loop
similar to:

   declare
      C : INI_Parser;
   begin
      Open (C, "settings.txt");
      while not At_End (C) loop
         Put_Line ("Found key " & Key (C) & " with value " & Value (C));
         Next (C);
      end loop;
   end;

This can be made slightly lighter by using the Ada05 dotted notation.

You would only use such a loop in your application if you intend to
store the values in various typed constants in your application. But
*GNATCOLL.Config* provides a slightly easier interface for this, in
the form of a *Config_Pool*. Such a pool is filled by reading a
configuration file, and then the values associated with each key can
be read at any point during the lifetime of your application. You can
also explicitely override the values when needed:

   Config : Config_Pool;   --  A global variable

   declare
      C : INI_Parser;
   begin
      Open (C, "settings.txt");
      Fill (Config, C);
   end;

   Put_Line (Config.Get ("section.key"));  --  Ada05 dotted notation

Again, the values are by default read as strings, but you can
interpret them as integers, booleans or files.

A third layer is provided in *GNATCOLL.Config*. This solves the issue
of possible typos in code: in the above example, we could have made a
typo when writting *"section.key"*. That would only be detected at run
time. Another issue is that we might decide to rename the key in the
configuration file. We would then have to go through all the
application code to find all the places where this key is references
(and that can't be based on cross-references generated by the
compiler, since that's inside a string).

To solve this issue, it is possible to declare a set of constants that
represent the keys, and then use these to access the values, solving
the two problems above:

   Section_Key1 : constant Config_Key := Create ("Key1", "Section");
   Section_Key2 : constant Config_Key := Create ("Key2", "Section");

   Put_Line (Section_Key1.Get);

You then access the value of the keys using the Ada05 dotted notation,
providing a very natural syntax. When and if the key is renamed, you
then have a single place to change.
