-------------------------------------------------------------------------------
--
--  <STRONG>Copyright &copy; 2001, 2002 by Thomas Wolf.</STRONG>
--  <BLOCKQUOTE>
--    This piece of software is free software; you can redistribute it and/or
--    modify it under the terms of the  GNU General Public License as published
--    by the Free Software  Foundation; either version 2, or (at your option)
--    any later version. This software is distributed in the hope that it will
--    be useful, but <EM>without any warranty</EM>; without even the implied
--    warranty of <EM>merchantability or fitness for a particular purpose.</EM>
--    See the GNU General Public License for  more details. You should have
--    received a copy of the GNU General Public License with this distribution,
--    see file "<A HREF="GPL.txt">GPL.txt</A>". If not, write to the Free
--    Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
--    USA.
--  </BLOCKQUOTE>
--  <BLOCKQUOTE>
--    As a special exception from the GPL, if other files instantiate generics
--    from this unit, or you link this unit with other files to produce an
--    executable, this unit does not by itself cause the resulting executable
--    to be covered by the GPL. This exception does not however invalidate any
--    other reasons why the executable file might be covered by the GPL.
--  </BLOCKQUOTE>
--
--  <AUTHOR>
--    Thomas Wolf  (TW) <E_MAIL>
--  </AUTHOR>
--
--  <PURPOSE>
--    Simple configuration reader/writer. A configuration file consists of
--    lines of the format key [= value].
--
--    The file format allows for line comments (starting with '#' and
--    extending up to the end of the line) and line continuations using
--    the backslash notation.
--  </PURPOSE>
--
--  <NOT_TASK_SAFE>
--
--  <STORAGE>
--    Dynamic storage allocation in the default pool. @Configuration@s are
--    controlled types.
--  </STORAGE>
--
--  <HISTORY>
--    19-JUN-2002   TW  Initial version.
--    08-JUN-2003   TW  Added 'Full_Name' to 'Set_File_Name'.
--  </HISTORY>
-------------------------------------------------------------------------------

pragma License (Modified_GPL);

with Ada.Finalization;
with Ada.Strings.Maps;

package Util.Files.Config is

   type Reader is abstract
     new Ada.Finalization.Limited_Controlled with private;

   procedure Set_File_Name
     (Self      : in out Reader;
      Name      : in     String;
      Full_Name : in     String);
   --  Called when a file has been opened. Default does nothing.

   function Parse_Key
     (Self : in Reader;
      Line : in String)
     return Natural;
   --  Assuming that @Line@ starts with a valid key, parse a key and
   --  return the index of its last character. Return zero if no
   --  legal key is found.
   --
   --  Default recognizes <CODE>Identifier {"." Identifier}</CODE>

   procedure Parse_Operator
     (Self     : in     Reader;
      Line     : in     String;
      From, To :    out Natural);
   --  If an operator is found, return in @From@ and @To@ the starting
   --  and ending index, otherwise set @To@ to zero and -- if white space
   --  was skipped -- @From@ to the index one beyond the last skipped
   --  character.
   --
   --  Default skips white space and then recognizes a '='.

   function Delimiters
     (Self : in Reader)
     return Ada.Strings.Maps.Character_Set;
   --  Return a set containing all legal string delimiter characters.
   --  Default returns @Util.Strings.String_Quotes@.

   function Skip_String
     (Self  : in Reader;
      Line  : in String;
      Delim : in Character)
     return Natural;
   --  Called by @Read_From_File@ if Line (Line'First) in Delimiters (Self).
   --  Shall return the index of the string closing character or
   --  @Line'First@ if strings are not to be recognized.
   --
   --  The default uses
   --  <CODE>Util.Strings.Skip_String (Line, Delim,Delim)</CODE>, i.e. handles
   --  enclosed delimiters as in Ada (must be doubled).

   procedure New_Key
     (Self     : in out Reader;
      Key      : in     String;
      Operator : in     String;
      Value    : in     String)
      is abstract;
   --  Called after a successful parse of a line. Note that @Operator@ and
   --  @Value@ may both be empty. If @New_Key@ wants to signal an error, it
   --  should <EM>not</EM> use @Invalid_Configuration@ but some other
   --  exception. (@Read@ passes on @Invalid_Configuration@ unchanged, but
   --  adds the file name and the offending line to the message of any
   --  other exception.)

   Invalid_Configuration : exception;

   procedure Read
     (File_Name : in     String;
      R         : in out Reader'Class);
   --  Read lines from the file (handling line continuations and comments)
   --  and parse the lines using the @Reader@. Raises @Invalid_Configuration@
   --  with a descriptive message if a parse error occurs.
   --
   --  The @Reader@ may rely on the particular sequence of parsing operations
   --  use by @Read@: for each line, it first calls @Parse_Key@, then
   --  @Parse_Operator@, and finally @New_Key@. The value of a key is the
   --  rest of the line beyond the operator, with leading and trailing white
   --  space trimmed.
   --
   --  This routine is smart enough to handle recursive calls, where the
   --  @Reader@'s @New_Key@ operation calls @Read_From_File@ again. If
   --  a recursive inclusion of an already included file is detected,
   --  @Invalid_Configuration@ is raised.
   --
   --  Warning: this recursion detection can be subverted if an original
   --  @Reader@'s @New_Key@ operation calls @Read_From_File@ passing another
   --  @Reader@!

private

   type String_Ptr is access all String;

   type Files is array (Positive range <>) of String_Ptr;
   type Stack_Ptr is access all Files;

   type Reader is abstract new Ada.Finalization.Limited_Controlled with
      record
         Stack : Stack_Ptr;
      end record;

   procedure Finalize
     (Self : in out Reader);

end Util.Files.Config;
