-------------------------------------------------------------------------------
--
--  <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>
--    A complete replacement for @Ada.Strings.Unbounded@, with conversion
--    routines. The problem with the standard unbounded strings package is
--    that it lacks certain operations (such as I/O operations, or a way to
--    append a slice of an @Unbounded_String@ to another @Unbounded_String@
--    without having an intermediary conversion to @String@).
--
--    This implementation is fully portable; it doesn't use any GNAT-specific
--    pragmas like @Stream_Convert@. The price to pay is that this package
--    cannot be preelaborated (since @Null_Unbounded_String@ is a constant of
--    a controlled type). I wonder how the language designers did envision that
--    this be done...
--
--    @Unbounded_String@s are streamable; however, the format is not compatible
--    with the one used by GNAT's implementation of @Ada.Strings.Unbounded@.
--
--    The rules for new operations are as follows:
--
--    If there is a function that returns a @String@ from an
--    @Unbounded_String@, a new one returning an @Unbounded_String@ is added
--    (@Slice@).
--
--    For functions returning an @Unbounded_String@, add a procedural variant
--    that has an "<CODE>in out</CODE>" parameter (@To_Unbounded_String@,
--    @Slice@).
--
--    If there is a procedure that has a second parameter of type @String@, new
--    ones using @Unbounded_String@ or even a slice of an @Unbounded_String@
--    are added (@Append@, @Insert@, @Overwrite@, @Replace_Slice@).
--
--    Add Operations on slices of unbounded strings where appropriate
--    (@Transform@).
--
--    Operations involving character mappings cause new operations hard-wired
--    not to use any mapping to be added. For such operations, <EM>no</EM>
--    variants with a second parameter of type @Unbounded_String@ is added,
--    for that second parameter might be large anyway, and it is in such cases
--    better to explicitly use @Transform@ and then one of the "no mapping"
--    operations.
--
--    As a result, this interface should allow efficient use of
--    @Unbounded_String@ without having to convert to and from standard
--    @String@ all the time.
--
--    The implementation takes into account that any @Unbounded_String@ may
--    be large. If necessary, it therefore re-implements algorithms rather
--    than just call the corresponding operation from package
--    @Ada.Strings.Fixed@ to avoid unnecessary extra copies of the string data.
--  </PURPOSE>
--
--  <NOT_TASK_SAFE>
--
--  <STORAGE>
--    Dynamic storage allocation in the default pool.
--  </STORAGE>
--
--  <HISTORY>
--    07-JUN-2002   TW  Initial version.
--    27-JUN-2002   TW  Added two 'Find_Token' variants: one with a single
--                      'From' parameter, and one with 'Low' and 'High'
--                      parameters.
--    08-AUG-2002   TW  Many new variants.
--  </HISTORY>
-------------------------------------------------------------------------------

pragma License (Modified_GPL);

with Ada.Finalization;
with Ada.Streams;

with Ada.Strings.Maps;
with Ada.Strings.Unbounded;

package Util.Text is

   pragma Elaborate_Body;

   type Unbounded_String is private;

   Null_Unbounded_String : constant Unbounded_String;
   --  Represents a null @Unbounded_String@, i.e.
   --  <CODE>To_String (Null_Unbounded_String) = ""</CODE>.

   ----------------------------------------------------------------------------

   function To_Standard
     (S : in Unbounded_String)
     return Ada.Strings.Unbounded.Unbounded_String;
   --  Convert a @Util.Text.Unbounded_String@ to a standard @Unbounded_String@.
   --
   --  <STRONG>Warning:</STRONG> Needs an intermediary copy of the string data.

   function From_Standard
     (S : in Ada.Strings.Unbounded.Unbounded_String)
     return Unbounded_String;
   --  Convert a standard @Unbounded_String@ to a @Util.Text.Unbounded_String@.
   --
   --  <STRONG>Warning:</STRONG> Needs an intermediary copy of the string data.

   procedure To_Standard
     (Source : in     Unbounded_String;
      Target :    out Ada.Strings.Unbounded.Unbounded_String);
   --  Equivalent to <CODE>Target := To_Standard (Source)</CODE>.

   procedure From_Standard
     (Source : in     Ada.Strings.Unbounded.Unbounded_String;
      Target :    out Unbounded_String);
   --  Equivalent to <CODE>Target := From_Standard (Source)</CODE>.

   function To_String
     (S : in Unbounded_String)
     return String;
   --  Returns the string data of @S@ as a standard fixed string.

   function From_String
     (S : in String)
     return Unbounded_String;
   --  Returns an @Unbounded_String@ containing the contents of @S@.

   function To_Unbounded_String
     (S : in String)
     return Unbounded_String
     renames From_String;
   --  A renaming for convenience.

   function To_Unbounded_String
     (Length : in Natural)
     return Unbounded_String;
   --  Returns an @Unbounded_String@ of the given @Length@ whose content is
   --  uninitialized.

   ----------------------------------------------------------------------------
   --  Standard operations

   function Length
     (Source : in Unbounded_String)
     return Natural;
   --  Returns the length of @Source@'s string data.

   procedure Append
     (Source   : in out Unbounded_String;
      New_Item : in     Unbounded_String);
   --  Equivalent to <CODE>Source := Source & New_Item</CODE>.

   procedure Append
     (Source   : in out Unbounded_String;
      New_Item : in     String);
   --  Equivalent to <CODE>Source := Source & New_Item</CODE>.

   procedure Append
     (Source   : in out Unbounded_String;
      New_Item : in     Character);
   --  Equivalent to <CODE>Source := Source & New_Item</CODE>.

   function "&"
     (Left, Right : Unbounded_String)
     return Unbounded_String;
   --  Concatenates @Left@ and @Right@ and returns the resulting
   --  @Unbounded_String@.

   function "&"
     (Left  : in Unbounded_String;
      Right : in String)
     return Unbounded_String;
   --  Concatenates @Left@ and @Right@ and returns the resulting
   --  @Unbounded_String@.

   function "&"
     (Left  : in String;
      Right : in Unbounded_String)
     return Unbounded_String;
   --  Concatenates @Left@ and @Right@ and returns the resulting
   --  @Unbounded_String@.

   function "&"
     (Left  : in Unbounded_String;
      Right : in Character)
     return Unbounded_String;
   --  Concatenates @Left@ and @Right@ and returns the resulting
   --  @Unbounded_String@.

   function "&"
     (Left  : in Character;
      Right : in Unbounded_String)
     return Unbounded_String;
   --  Concatenates @Left@ and @Right@ and returns the resulting
   --  @Unbounded_String@.

   function Element
     (Source : in Unbounded_String;
      Index  : in Positive)
     return Character;
   --  Equivalent to <CODE>To_String (Source) (Index)</CODE>. Raises
   --  <CODE>Ada.Strings.Index_Error</CODE> if
   --  <CODE>Index > Length (Source)</CODE>.

   procedure Replace_Element
     (Source : in out Unbounded_String;
      Index  : in     Positive;
      By     : in     Character);
   --  Equivalent to <CODE>To_String (Source) (Index)</CODE>. Raises
   --  <CODE>Ada.Strings.Index_Error</CODE> if
   --  <CODE>Index > Length (Source)</CODE>.

   function Slice
     (Source : in Unbounded_String;
      Low    : in Positive;
      High   : in Natural)
     return String;
   --  Equivalent to <CODE>To_String (Source) (Low .. High)</CODE>. Raises
   --  <CODE>Ada.Strings.Index_Error</CODE> if
   --  <CODE>High > Length (Source)</CODE>.

   function "="
     (Left, Right : in Unbounded_String)
     return Boolean;
   --  Equivalent to <CODE>To_String (Left) = To_String (Right)</CODE>.

   function "="
     (Left  : in Unbounded_String;
      Right : in String)
     return Boolean;
   --  Equivalent to <CODE>To_String (Left) = Right</CODE>.

   function "="
     (Left  : in String;
      Right : in Unbounded_String)
     return Boolean;
   --  Equivalent to <CODE>Left = To_String (Right)</CODE>.

   function "<"
     (Left, Right : in Unbounded_String)
     return Boolean;
   --  Equivalent to <CODE>To_String (Left) < To_String (Right)</CODE>.

   function "<"
     (Left  : in Unbounded_String;
      Right : in String)
     return  Boolean;
   --  Equivalent to <CODE>To_String (Left) < Right</CODE>.

   function "<"
     (Left  : in String;
      Right : in Unbounded_String)
     return  Boolean;
   --  Equivalent to <CODE>Left < To_String (Right)</CODE>.

   function "<="
     (Left, Right : in Unbounded_String)
     return Boolean;
   --  Equivalent to <CODE>To_String (Left) <= To_String (Right)</CODE>.

   function "<="
     (Left  : in Unbounded_String;
      Right : in String)
     return Boolean;
   --  Equivalent to <CODE>To_String (Left) <= Right</CODE>.

   function "<="
     (Left  : in String;
      Right : in Unbounded_String)
     return Boolean;
   --  Equivalent to <CODE>Left <= To_String (Right)</CODE>.

   function ">"
     (Left, Right : in Unbounded_String)
     return Boolean;
   --  Equivalent to <CODE>To_String (Left) > To_String (Right)</CODE>.

   function ">"
     (Left  : in Unbounded_String;
      Right : in String)
     return Boolean;
   --  Equivalent to <CODE>To_String (Left) > Right</CODE>.

   function ">"
     (Left  : in String;
      Right : in Unbounded_String)
     return Boolean;
   --  Equivalent to <CODE>Left > To_String (Right)</CODE>.

   function ">="
     (Left, Right : in Unbounded_String)
     return Boolean;
   --  Equivalent to <CODE>To_String (Left) >= To_String (Right)</CODE>.

   function ">="
     (Left  : in Unbounded_String;
      Right : in String)
     return Boolean;
   --  Equivalent to <CODE>To_String (Left) >= Right</CODE>.

   function ">="
     (Left  : in String;
      Right : in Unbounded_String)
     return Boolean;
   --  Equivalent to <CODE>Left >= To_String (Right)</CODE>.

   function Index
     (Source  : in Unbounded_String;
      Pattern : in String;
      Going   : in Ada.Strings.Direction              := Ada.Strings.Forward;
      Mapping : in Ada.Strings.Maps.Character_Mapping :=
                     Ada.Strings.Maps.Identity)
     return Natural;
   --  Equivalent to
   --  <CODE>Ada.Strings.Unbounded.Index
   --  (To_Standard (Source), Pattern, Going, Mapping);</CODE>

   function Index
     (Source  : in Unbounded_String;
      Pattern : in String;
      Going   : in Ada.Strings.Direction := Ada.Strings.Forward;
      Mapping : in Ada.Strings.Maps.Character_Mapping_Function)
     return Natural;
   --  Equivalent to
   --  <CODE>Ada.Strings.Unbounded.Index
   --  (To_Standard (Source), Pattern, Going, Mapping);</CODE>

   function Index
     (Source : in Unbounded_String;
      Set    : in Ada.Strings.Maps.Character_Set;
      Test   : in Ada.Strings.Membership := Ada.Strings.Inside;
      Going  : in Ada.Strings.Direction  := Ada.Strings.Forward)
     return Natural;
   --  Equivalent to
   --  <CODE>Ada.Strings.Unbounded.Index
   --  (To_Standard (Source), Set, Test, Going);</CODE>

   function Index_Non_Blank
     (Source : in Unbounded_String;
      Going  : in Ada.Strings.Direction := Ada.Strings.Forward)
     return Natural;
   --  Equivalent to
   --  <CODE>Ada.Strings.Unbounded.Index_Non_Blank
   --  (To_Standard (Source), Going);</CODE>

   function Count
     (Source  : in Unbounded_String;
      Pattern : in String;
      Mapping : in Ada.Strings.Maps.Character_Mapping :=
                     Ada.Strings.Maps.Identity)
     return Natural;
   --  Equivalent to
   --  <CODE>Ada.Strings.Unbounded.Count
   --  (To_Standard (Source), Pattern, Mapping);</CODE>

   function Count
     (Source   : in Unbounded_String;
      Pattern  : in String;
      Mapping  : in Ada.Strings.Maps.Character_Mapping_Function)
     return Natural;
   --  Equivalent to
   --  <CODE>Ada.Strings.Unbounded.Count
   --  (To_Standard (Source), Pattern, Mapping);</CODE>

   function Count
     (Source : in Unbounded_String;
      Set    : in Ada.Strings.Maps.Character_Set)
     return Natural;
   --  Equivalent to
   --  <CODE>Ada.Strings.Unbounded.Count (To_Standard (Source), Set);</CODE>

   procedure Find_Token
     (Source : in     Unbounded_String;
      Set    : in     Ada.Strings.Maps.Character_Set;
      Test   : in     Ada.Strings.Membership;
      First  :    out Positive;
      Last   :    out Natural);
   --  Equivalent to
   --  <CODE>Ada.Strings.Unbounded.Find_Token
   --  (To_Standard (Source), Set, Test, First, Last);</CODE>

   function Translate
     (Source  : in Unbounded_String;
      Mapping : in Ada.Strings.Maps.Character_Mapping)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>From_Standard (Ada.Strings.Unbounded.Translate
   --  (To_Standard (Source), Mapping));</CODE>

   procedure Translate
     (Source  : in out Unbounded_String;
      Mapping : in     Ada.Strings.Maps.Character_Mapping);
   --  Equivalent to
   --  <CODE>Source := Translate (Source, Mapping);</CODE>

   function Translate
     (Source  : in Unbounded_String;
      From    : in Positive;
      Through : in Natural;
      Mapping : in Ada.Strings.Maps.Character_Mapping)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>Translate (Slice (Source, From, Through), Mapping);</CODE>

   procedure Translate
     (Source  : in out Unbounded_String;
      From    : in     Positive;
      Through : in     Natural;
      Mapping : in     Ada.Strings.Maps.Character_Mapping);
   --  Equivalent to
   --  <CODE>Translate (Slice (Source, From, Through), Mapping);</CODE>

   function Translate
     (Source  : in Unbounded_String;
      Mapping : in Ada.Strings.Maps.Character_Mapping_Function)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>From_Standard (Ada.Strings.Unbounded.Translate
   --  (To_Standard (Source), Mapping));</CODE>

   procedure Translate
     (Source  : in out Unbounded_String;
      Mapping : in     Ada.Strings.Maps.Character_Mapping_Function);
   --  Equivalent to
   --  <CODE>Source := Translate (Source, Mapping);</CODE>

   function Translate
     (Source  : in Unbounded_String;
      From    : in Positive;
      Through : in Natural;
      Mapping : in Ada.Strings.Maps.Character_Mapping_Function)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>Translate (Slice (Source, From, Through), Mapping);</CODE>

   procedure Translate
     (Source  : in out Unbounded_String;
      From    : in     Positive;
      Through : in     Natural;
      Mapping : in     Ada.Strings.Maps.Character_Mapping_Function);
   --  Equivalent to
   --  <CODE>Translate (Slice (Source, From, Through), Mapping);</CODE>

   function Replace_Slice
     (Source : in Unbounded_String;
      Low    : in Positive;
      High   : in Natural;
      By     : in String)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>From_Standard (Ada.Strings.Unbounded.Replace_Slice
   --  (To_Standard (Source), Low, High, By);</CODE>

   procedure Replace_Slice
     (Source : in out Unbounded_String;
      Low    : in     Positive;
      High   : in     Natural;
      By     : in     String);
   --  Equivalent to
   --  <CODE>Source := Replace_Slice (Source, Low, High, By);</CODE>

   function Insert
     (Source   : in Unbounded_String;
      Before   : in Positive;
      New_Item : in String)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>From_Standard (Ada.Strings.Unbounded.Insert
   --  (To_Standard (Source), Before, New_Item);</CODE>

   procedure Insert
     (Source   : in out Unbounded_String;
      Before   : in     Positive;
      New_Item : in     String);
   --  Equivalent to
   --  <CODE>Source := Insert (Source, Before, New_Item);</CODE>

   function Overwrite
     (Source   : in Unbounded_String;
      Position : in Positive;
      New_Item : in String)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>From_Standard (Ada.Strings.Unbounded.Overwrite
   --  (To_Standard (Source), Position, New_Item);</CODE>

   procedure Overwrite
     (Source   : in out Unbounded_String;
      Position : in     Positive;
      New_Item : in     String);
   --  Equivalent to
   --  <CODE>Source := Overwrite (Source, Position, New_Item);</CODE>

   function Delete
     (Source  : in Unbounded_String;
      From    : in Positive;
      Through : in Natural)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>From_Standard (Ada.Strings.Unbounded.Delete
   --  (To_Standard (Source), From, Through);</CODE>

   procedure Delete
     (Source  : in out Unbounded_String;
      From    : in     Positive;
      Through : in     Natural);
   --  Equivalent to
   --  <CODE>Source := Delete (Source, From, Through);</CODE>

   function Trim
     (Source : in Unbounded_String;
      Side   : in Ada.Strings.Trim_End)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>From_Standard (Ada.Strings.Unbounded.Trim
   --  (To_Standard (Source), Side);</CODE>

   procedure Trim
     (Source : in out Unbounded_String;
      Side   : in     Ada.Strings.Trim_End);
   --  Equivalent to
   --  <CODE>Source := Trim (Source, Side);</CODE>

   function Trim
     (Source : in Unbounded_String;
      Left   : in Ada.Strings.Maps.Character_Set;
      Right  : in Ada.Strings.Maps.Character_Set)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>From_Standard (Ada.Strings.Unbounded.Trim
   --  (To_Standard (Source), Left, Right);</CODE>

   procedure Trim
     (Source : in out Unbounded_String;
      Left   : in     Ada.Strings.Maps.Character_Set;
      Right  : in     Ada.Strings.Maps.Character_Set);
   --  Equivalent to
   --  <CODE>Source := Trim (Source, Left, Right);</CODE>

   function Head
     (Source : in Unbounded_String;
      Count  : in Natural;
      Pad    : in Character := Ada.Strings.Space)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>From_Standard (Ada.Strings.Unbounded.Head
   --  (To_Standard (Source), Count, Pad);</CODE>

   procedure Head
     (Source : in out Unbounded_String;
      Count  : in     Natural;
      Pad    : in     Character := Ada.Strings.Space);
   --  Equivalent to
   --  <CODE>Source := Head (Source, Count, Pad);</CODE>

   function Tail
     (Source : in Unbounded_String;
      Count  : in Natural;
      Pad    : in Character := Ada.Strings.Space)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>From_Standard (Ada.Strings.Unbounded.Tail
   --  (To_Standard (Source), Count, Pad);</CODE>

   procedure Tail
     (Source : in out Unbounded_String;
      Count  : in     Natural;
      Pad    : in     Character := Ada.Strings.Space);
   --  Equivalent to
   --  <CODE>Source := Tail (Source, Count, Pad);</CODE>

   function "*"
     (Left  : in Natural;
      Right : in Character)
     return Unbounded_String;
   --  Returns an @Unbounded_String@ of length @Left@ with all characters
   --  set to @Right@.

   function "*"
     (Left  : in Natural;
      Right : in String)
     return Unbounded_String;
   --  Returns an @Unbounded_String@ whose content is obtained by concatenating
   --  @Right@ @Left@ times.

   function "*"
     (Left  : in Natural;
      Right : in Unbounded_String)
     return Unbounded_String;
   --  Returns an @Unbounded_String@ whose content is obtained by concatenating
   --  @Right@'s string data  @Left@ times.

   ----------------------------------------------------------------------------
   --  Additional operations:

   procedure Set
     (Target : in out Unbounded_String;
      Str    : in     String);
   --  Equivalent to <CODE>Target := To_Unbounded_String (Str);</CODE>.
   --  However, @Set@ may be more efficient than @To_Unbounded_String@
   --  because @Target@ is passed by reference, and we thus can save
   --  some finalizations.

   procedure To_Unbounded_String
     (Target : in out Unbounded_String;
      Str    : in     String)
     renames Set;

   function Slice
     (Source : in Unbounded_String;
      Low    : in Positive;
      High   : in Natural)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>To_Unbounded_String (To_String (Source) (Low .. High))</CODE>.
   --  Raises <CODE>Ada.Strings.Index_Error</CODE> if
   --  <CODE>High > Length (Source)</CODE>.

   procedure Slice
     (Target : in out Unbounded_String;
      Source : in     Unbounded_String;
      Low    : in     Positive;
      High   : in     Natural);
   --  Equivalent to <CODE>Target := Slice (Source, Low, High);</CODE>.

   ----------------------------------------------------------------------------
   --  Index operations with a @From@ parameter giving the place where the
   --  search should start.

   function Find
     (Source  : in Unbounded_String;
      Pattern : in Character;
      Going   : in Ada.Strings.Direction := Ada.Strings.Forward)
     return Natural;
   --  As @Index@, but not using any mappings. Equivalent to
   --  <CODE>Index (Source, Pattern, Going);</CODE>.

   function Find
     (Source  : in Unbounded_String;
      Pattern : in String;
      Going   : in Ada.Strings.Direction := Ada.Strings.Forward)
     return Natural;
   --  As @Index@, but not using any mappings. Equivalent to
   --  <CODE>Index (Source, Pattern, Going);</CODE>.

   function Find
     (Source  : in Unbounded_String;
      Pattern : in Unbounded_String;
      Going   : in Ada.Strings.Direction := Ada.Strings.Forward)
     return Natural;
   --  As @Index@, but not using any mappings. Equivalent to
   --  <CODE>Index (Source, To_String (Pattern), Going);</CODE>.

   function Find
     (Source  : in Unbounded_String;
      Pattern : in Character;
      From    : in Positive;
      Going   : in Ada.Strings.Direction := Ada.Strings.Forward)
     return Natural;
   --  As @Index@, but not using any mappings. If
   --  <CODE>Going = Forward</CODE>, equivalent to
   --
   --  <CODE>Index (Slice (Source, From, Length (Source)), Pattern)</CODE>,
   --
   --  otherwise equivalent to
   --
   --  <CODE>Index (Slice (Source, 1, From), Pattern, Backward)</CODE>

   function Find
     (Source  : in Unbounded_String;
      Pattern : in String;
      From    : in Positive;
      Going   : in Ada.Strings.Direction := Ada.Strings.Forward)
     return Natural;
   --  As @Index@, but not using any mappings. If
   --  <CODE>Going = Forward</CODE>, equivalent to
   --
   --  <CODE>Index (Slice (Source, From, Length (Source)), Pattern)</CODE>,
   --
   --  otherwise equivalent to
   --
   --  <CODE>Index (Slice (Source, 1, From), Pattern, Backward)</CODE>

   function Find
     (Source  : in Unbounded_String;
      Pattern : in Unbounded_String;
      From    : in Positive;
      Going   : in Ada.Strings.Direction := Ada.Strings.Forward)
     return Natural;
   --  Equivalent to
   --  <CODE>Find (Source, To_String (Pattern), From, Going)</CODE>.

   function First_Index
     (Source : in Unbounded_String;
      Ch     : in Character)
     return Natural;
   --  Returns the index of the first occurrence of @Ch@ in @Source@, or
   --  zero if @Ch@ doesn't occur in @Source@.

   function First_Index
     (Source : in Unbounded_String;
      Ch     : in Character;
      From   : in Positive)
     return Natural;
   --  Returns the index of the first occurrence of @Ch@ in
   --  <CODE>Slice (Source, From, Length (Source))</CODE>, or zero if @Ch@
   --  doesn't occur in this slice of @Source@.

   function Last_Index
     (Source : in Unbounded_String;
      Ch     : in Character)
     return Natural;
   --  Returns the index of the last occurrence of @Ch@ in @Source@, or zero
   --  @Ch@ doesn't occur in @Source@.

   function Last_Index
     (Source : in Unbounded_String;
      Ch     : in Character;
      Limit  : in Positive)
     return Natural;
   --  Equivalent to <CODE>Last_Index (Slice (Source, 1, Limit), Ch)</CODE>.

   function First_Index
     (Source  : in Unbounded_String;
      Pattern : in String)
     return Natural;
   --  Equivalent to
   --  <CODE>Ada.String.Fixed.Index (To_String (Source), Pattern);</CODE>

   function First_Index
     (Source  : in Unbounded_String;
      Pattern : in String;
      From    : in Positive)
     return Natural;
   --  Equivalent to
   --  <CODE>
   --  Ada.String.Fixed.Index (Slice (Source, From, Length (Source)), Pattern);
   --  </CODE>

   function Last_Index
     (Source  : in Unbounded_String;
      Pattern : in String)
     return Natural;
   --  Equivalent to
   --  <CODE>
   --  Ada.Strings.Fixed.Index (To_String (Source), Pattern, Backward);
   --  </CODE>

   function Last_Index
     (Source  : in Unbounded_String;
      Pattern : in String;
      Limit   : in Positive)
     return Natural;
   --  Equivalent to
   --  <CODE>Last_Index (Slice (Source, 1, Limit), Pattern);</CODE>

   function First_Index
     (Source  : in Unbounded_String;
      Pattern : in Unbounded_String)
     return Natural;
   --  Equivalent to
   --  <CODE>First_Index (Source, To_String (Pattern));</CODE>

   function First_Index
     (Source  : in Unbounded_String;
      Pattern : in Unbounded_String;
      From    : in Positive)
     return Natural;
   --  Equivalent to
   --  <CODE>First_Index (Source, To_String (Pattern), From);</CODE>

   function Last_Index
     (Source  : in Unbounded_String;
      Pattern : in Unbounded_String)
     return Natural;
   --  Equivalent to
   --  <CODE>Last_Index (Source, To_String (Pattern));</CODE>

   function Last_Index
     (Source  : in Unbounded_String;
      Pattern : in Unbounded_String;
      Limit   : in Positive)
     return Natural;
   --  Equivalent to
   --  <CODE>Last_Index (Source, To_String (Pattern), From);</CODE>

   function Index
     (Source  : in Unbounded_String;
      Pattern : in String;
      From    : in Positive;
      Going   : in Ada.Strings.Direction              := Ada.Strings.Forward;
      Mapping : in Ada.Strings.Maps.Character_Mapping :=
                     Ada.Strings.Maps.Identity)
     return Natural;
   --  If <CODE>Going = Forward</CODE>, equivalent to
   --
   --  <CODE>Index (Slice (Source, From, Length (Source),
   --  Pattern, Going, Mapping);</CODE>
   --
   --  otherwise equivalent to
   --
   --  <CODE>Index (Slice (Source, 1, From), Pattern, Going, Mapping);</CODE>

   function Index
     (Source  : in Unbounded_String;
      Pattern : in String;
      From    : in Positive;
      Going   : in Ada.Strings.Direction := Ada.Strings.Forward;
      Mapping : in Ada.Strings.Maps.Character_Mapping_Function)
     return Natural;
   --  If <CODE>Going = Forward</CODE>, equivalent to
   --
   --  <CODE>Index (Slice (Source, From, Length (Source),
   --  Pattern, Going, Mapping);</CODE>
   --
   --  otherwise equivalent to
   --
   --  <CODE>Index (Slice (Source, 1, From), Pattern, Going, Mapping);</CODE>

   function Index
     (Source : in Unbounded_String;
      From   : in Positive;
      Set    : in Ada.Strings.Maps.Character_Set;
      Test   : in Ada.Strings.Membership := Ada.Strings.Inside;
      Going  : in Ada.Strings.Direction  := Ada.Strings.Forward)
     return Natural;
   --  If <CODE>Going = Forward</CODE>, equivalent to
   --
   --  <CODE>Index (Slice (Source, From, Length (Source),
   --  Set, Test, Going);</CODE>
   --
   --  otherwise equivalent to
   --
   --  <CODE>Index (Slice (Source, 1, From), Set, Test, Going);</CODE>

   function Index
     (Source  : in Unbounded_String;
      Pattern : in Unbounded_String;
      Going   : in Ada.Strings.Direction              := Ada.Strings.Forward;
      Mapping : in Ada.Strings.Maps.Character_Mapping :=
                     Ada.Strings.Maps.Identity)
     return Natural;
   --  Equivalent to
   --  <CODE>Index (Source, To_String (Pattern), Going, Mapping);</CODE>

   function Index
     (Source  : in Unbounded_String;
      Pattern : in Unbounded_String;
      Going   : in Ada.Strings.Direction              := Ada.Strings.Forward;
      Mapping : in Ada.Strings.Maps.Character_Mapping_Function)
     return Natural;
   --  Equivalent to
   --  <CODE>Index (Source, To_String (Pattern), Going, Mapping);</CODE>

   function Index
     (Source  : in Unbounded_String;
      Pattern : in Unbounded_String;
      From    : in Positive;
      Going   : in Ada.Strings.Direction              := Ada.Strings.Forward;
      Mapping : in Ada.Strings.Maps.Character_Mapping :=
                     Ada.Strings.Maps.Identity)
     return Natural;
   --  Equivalent to
   --  <CODE>Index (Source, To_String (Pattern), From, Going, Mapping);</CODE>

   function Index
     (Source  : in Unbounded_String;
      Pattern : in Unbounded_String;
      From    : in Positive;
      Going   : in Ada.Strings.Direction              := Ada.Strings.Forward;
      Mapping : in Ada.Strings.Maps.Character_Mapping_Function)
     return Natural;
   --  Equivalent to
   --  <CODE>Index (Source, To_String (Pattern), From, Going, Mapping);</CODE>

   function Index_Non_Blank
     (Source : in Unbounded_String;
      From   : in Positive;
      Going  : in Ada.Strings.Direction := Ada.Strings.Forward)
     return Natural;
   --  If <CODE>Going = Forward</CODE>, equivalent to
   --
   --  <CODE>Index_Non_Blank
   --  (Slice (Source, From, Length (Source)), Going);</CODE>
   --
   --  otherwise equivalent to
   --
   --  <CODE>Index_Non_Blank (Slice (Source, 1, From), Going);</CODE>

   function Count
     (Source  : in Unbounded_String;
      Pattern : in Unbounded_String;
      Mapping : in Ada.Strings.Maps.Character_Mapping :=
                     Ada.Strings.Maps.Identity)
     return Natural;
   --  Equivalent to
   --  <CODE>Count (Source, To_String (Pattern), Mapping);</CODE>

   function Count
     (Source  : in Unbounded_String;
      Pattern : in Unbounded_String;
      Mapping : in Ada.Strings.Maps.Character_Mapping_Function)
     return Natural;
   --  Equivalent to
   --  <CODE>Count (Source, To_String (Pattern), Mapping);</CODE>

   function Occurrences
     (Source  : in Unbounded_String;
      Pattern : in String)
     return Natural;
   --  Equivalent to <CODE>Count (Source, Pattern)</CODE>, but hard-wired not
   --  to use any mapping.

   function  Occurrences
     (Source  : in Unbounded_String;
      Pattern : in Unbounded_String)
     return Natural;
   --  Equivalent to <CODE>Occurrences (Source, To_String (Pattern));</CODE>

   procedure Find_Token
     (Source : in     Unbounded_String;
      From   : in     Positive;
      Set    : in     Ada.Strings.Maps.Character_Set;
      Test   : in     Ada.Strings.Membership;
      First  :    out Positive;
      Last   :    out Natural);
   --  Equivalent to
   --  <CODE>Find_Token (Slice (Source, From, Length (Source)),
   --  Set, Test, First, Last)</CODE>.

   procedure Find_Token
     (Source : in     Unbounded_String;
      Low    : in     Positive;
      High   : in     Natural;
      Set    : in     Ada.Strings.Maps.Character_Set;
      Test   : in     Ada.Strings.Membership;
      First  :    out Positive;
      Last   :    out Natural);
   --  Equivalent to
   --  <CODE>Find_Token (Slice (Source, Low, High),
   --  Set, Test, First, Last)</CODE>.

   function Is_Prefix
     (Source : in Unbounded_String;
      Prefix : in String)
     return Boolean;
   --  Equivalent to
   --  <CODE><A HREF="util-strings.html">Util.Strings</A>.Is_Prefix
   --  (To_String (Source), Prefix);</CODE>

   function Is_Prefix
     (Source : in Unbounded_String;
      Prefix : in Unbounded_String)
     return Boolean;
   --  Equivalent to <CODE>Is_Prefix (Source, To_String (Prefix));</CODE>.

   function Is_Suffix
     (Source : in Unbounded_String;
      Suffix : in String)
     return Boolean;
   --  Equivalent to
   --  <CODE><A HREF="util-strings.html">Util.Strings</A>.Is_Suffix
   --  (To_String (Source), Suffix);</CODE>

   function Is_Suffix
     (Source : in Unbounded_String;
      Suffix : in Unbounded_String)
     return Boolean;
   --  Equivalent to <CODE>Is_Suffix (Source, To_String (Suffix));</CODE>.

   procedure Replace
     (Source : in out Unbounded_String;
      What   : in     String;
      By     : in     String);
   --  Equivalent to
   --  <CODE><A HREF="util-strings.html">Util.Strings</A>.Replace
   --  (To_String (Source), What, By);</CODE>

   procedure Replace
     (Source : in out Unbounded_String;
      What   : in     Unbounded_String;
      By     : in     Unbounded_String);
   --  Equivalent to
   --  <CODE>Replace (Source, To_String (What), To_String (By))</CODE>.

   ----------------------------------------------------------------------------
   --  Slice operations. These are useful because they avoid the need of
   --  an intermediary conversion to @String@, which would require an
   --  intermediary extra copy of the string data.

   function  Append
     (Source : in Unbounded_String;
      From   : in Unbounded_String;
      Low    : in Positive;
      High   : in Natural)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>Source & Slice (From, Low, High);</CODE>

   procedure Append
     (Source : in out Unbounded_String;
      From   : in     Unbounded_String;
      Low    : in     Positive;
      High   : in     Natural);
   --  Equivalent to
   --  <CODE>Append (Source, Slice (From, Low, High));</CODE>

   function  Replace_Slice
     (Source : in Unbounded_String;
      Low    : in Positive;
      High   : in Natural;
      By     : in Unbounded_String)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>Replace_Slice (Source, Low, High, To_String (By));</CODE>

   procedure Replace_Slice
     (Source : in out Unbounded_String;
      Low    : in     Positive;
      High   : in     Natural;
      By     : in     Unbounded_String);
   --  Equivalent to
   --  <CODE>Replace_Slice (Source, Low, High, To_String (By));</CODE>

   function  Replace_Slice
     (Source : in Unbounded_String;
      Low    : in Positive;
      High   : in Natural;
      By     : in Unbounded_String;
      From   : in Positive;
      To     : in Natural)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>Replace_Slice (Source, Low, High, Slice (By, From, To));</CODE>

   procedure Replace_Slice
     (Source : in out Unbounded_String;
      Low    : in     Positive;
      High   : in     Natural;
      By     : in     Unbounded_String;
      From   : in     Positive;
      To     : in     Natural);
   --  Equivalent to
   --  <CODE>Replace_Slice (Source, Low, High, Slice (By, From, To));</CODE>

   function Insert
     (Source   : in Unbounded_String;
      Before   : in Positive;
      New_Item : in Unbounded_String)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>Insert (Source, Before, To_String (New_Item));</CODE>.

   procedure Insert
     (Source   : in out Unbounded_String;
      Before   : in     Positive;
      New_Item : in     Unbounded_String);
   --  Equivalent to
   --  <CODE>Insert (Source, Before, To_String (New_Item));</CODE>.

   function Insert
     (Source   : in Unbounded_String;
      Before   : in Positive;
      New_Item : in Unbounded_String;
      Low      : in Positive;
      High     : in Natural)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>Insert (Source, Before, Slice (New_Item, Low, High));</CODE>.

   procedure Insert
     (Source   : in out Unbounded_String;
      Before   : in     Positive;
      New_Item : in     Unbounded_String;
      Low      : in     Positive;
      High     : in     Natural);
   --  Equivalent to
   --  <CODE>Insert (Source, Before, Slice (New_Item, Low, High));</CODE>.

   function Overwrite
     (Source   : in Unbounded_String;
      Position : in Positive;
      New_Item : in Unbounded_String)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>Overwrite (Source, Position, To_String (New_Item));</CODE>.

   procedure Overwrite
     (Source   : in out Unbounded_String;
      Position : in     Positive;
      New_Item : in     Unbounded_String);
   --  Equivalent to
   --  <CODE>Overwrite (Source, Position, To_String (New_Item));</CODE>.

   function Overwrite
     (Source   : in Unbounded_String;
      Position : in Positive;
      New_Item : in Unbounded_String;
      Low      : in Positive;
      High     : in Natural)
     return Unbounded_String;
   --  Equivalent to
   --  <CODE>Overwrite (Source, Position, Slice (New_Item, Low, High));</CODE>.

   procedure Overwrite
     (Source   : in out Unbounded_String;
      Position : in     Positive;
      New_Item : in     Unbounded_String;
      Low      : in     Positive;
      High     : in     Natural);
   --  Equivalent to
   --  <CODE>Overwrite (Source, Position, Slice (New_Item, Low, High));</CODE>.

   ----------------------------------------------------------------------------

   type String_Access is access all String;

   procedure Free
     (Ptr : in out String_Access);

private

   pragma Inline (Length, Element, Replace_Element,
                  "=", "<", "<=", ">", ">=",
                  Is_Prefix, Is_Suffix, Last_Index, First_Index);

   Null_String : aliased String := "";
   --  Sentinel object to avoid null-tests thoughout the implementation.

   type Unbounded_String is new Ada.Finalization.Controlled with
      record
         Data : String_Access := Null_String'Access;
      end record;

   procedure Initialize (S : in out Unbounded_String);
   procedure Adjust     (S : in out Unbounded_String);
   procedure Finalize   (S : in out Unbounded_String);

   procedure Write  (Stream : access Ada.Streams.Root_Stream_Type'Class;
                     S      : in     Unbounded_String);

   procedure Read   (Stream : access Ada.Streams.Root_Stream_Type'Class;
                     S      :    out Unbounded_String);

   for Unbounded_String'Read   use Read;
   for Unbounded_String'Write  use Write;

   Null_Unbounded_String : constant Unbounded_String :=
     (Ada.Finalization.Controlled with Data => Null_String'Access);

end Util.Text;
