------------------------------------------------------------------------------
--                                                                          --
--                         GNAT RUN-TIME COMPONENTS                         --
--                                                                          --
--                    A D A . S T R I N G S . F I X E D                     --
--                                                                          --
--                                 B o d y                                  --
--                                                                          --
--          Copyright (C) 1992-2023, Free Software Foundation, Inc.         --
--                                                                          --
-- GNAT is free software;  you can  redistribute it  and/or modify it under --
-- terms of the  GNU General Public License as published  by the Free Soft- --
-- ware  Foundation;  either version 3,  or (at your option) any later ver- --
-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
-- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
--                                                                          --
-- As a special exception under Section 7 of GPL version 3, you are granted --
-- additional permissions described in the GCC Runtime Library Exception,   --
-- version 3.1, as published by the Free Software Foundation.               --
--                                                                          --
-- You should have received a copy of the GNU General Public License and    --
-- a copy of the GCC Runtime Library Exception along with this program;     --
-- see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see    --
-- <http://www.gnu.org/licenses/>.                                          --
--                                                                          --
-- GNAT was originally developed  by the GNAT team at  New York University. --
-- Extensive contributions were provided by Ada Core Technologies Inc.      --
--                                                                          --
------------------------------------------------------------------------------

--  Note: This code is derived from the ADAR.CSH public domain Ada 83 versions
--  of the Appendix C string handling packages. One change is to avoid the use
--  of Is_In, so that we are not dependent on inlining. Note that the search
--  function implementations are to be found in the auxiliary package
--  Ada.Strings.Search. Also the Move procedure is directly incorporated (ADAR
--  used a subunit for this procedure). The number of errors having to do with
--  bounds of function return results were also fixed, and use of & removed for
--  efficiency reasons.

--  Ghost code, loop invariants and assertions in this unit are meant for
--  analysis only, not for run-time checking, as it would be too costly
--  otherwise. This is enforced by setting the assertion policy to Ignore.

pragma Assertion_Policy (Ghost          => Ignore,
                         Loop_Invariant => Ignore,
                         Assert         => Ignore);

with Ada.Strings.Maps; use Ada.Strings.Maps;

package body Ada.Strings.Fixed with SPARK_Mode is

   ------------------------
   -- Search Subprograms --
   ------------------------

   function Index
     (Source  : String;
      Pattern : String;
      Going   : Direction := Forward;
      Mapping : Maps.Character_Mapping := Maps.Identity) return Natural
   renames Ada.Strings.Search.Index;

   function Index
     (Source  : String;
      Pattern : String;
      Going   : Direction := Forward;
      Mapping : Maps.Character_Mapping_Function) return Natural
   renames Ada.Strings.Search.Index;

   function Index
     (Source : String;
      Set    : Maps.Character_Set;
      Test   : Membership := Inside;
      Going  : Direction  := Forward) return Natural
   renames Ada.Strings.Search.Index;

   function Index
     (Source  : String;
      Pattern : String;
      From    : Positive;
      Going   : Direction := Forward;
      Mapping : Maps.Character_Mapping := Maps.Identity) return Natural
   renames Ada.Strings.Search.Index;

   function Index
     (Source  : String;
      Pattern : String;
      From    : Positive;
      Going   : Direction := Forward;
      Mapping : Maps.Character_Mapping_Function) return Natural
   renames Ada.Strings.Search.Index;

   function Index
     (Source  : String;
      Set     : Maps.Character_Set;
      From    : Positive;
      Test    : Membership := Inside;
      Going   : Direction := Forward) return Natural
   renames Ada.Strings.Search.Index;

   function Index_Non_Blank
     (Source : String;
      Going  : Direction := Forward) return Natural
   renames Ada.Strings.Search.Index_Non_Blank;

   function Index_Non_Blank
     (Source : String;
      From   : Positive;
      Going  : Direction := Forward) return Natural
   renames Ada.Strings.Search.Index_Non_Blank;

   function Count
     (Source  : String;
      Pattern : String;
      Mapping : Maps.Character_Mapping := Maps.Identity) return Natural
   renames Ada.Strings.Search.Count;

   function Count
     (Source  : String;
      Pattern : String;
      Mapping : Maps.Character_Mapping_Function) return Natural
   renames Ada.Strings.Search.Count;

   function Count
     (Source : String;
      Set    : Maps.Character_Set) return Natural
   renames Ada.Strings.Search.Count;

   procedure Find_Token
     (Source : String;
      Set    : Maps.Character_Set;
      From   : Positive;
      Test   : Membership;
      First  : out Positive;
      Last   : out Natural)
   renames Ada.Strings.Search.Find_Token;

   procedure Find_Token
     (Source : String;
      Set    : Maps.Character_Set;
      Test   : Membership;
      First  : out Positive;
      Last   : out Natural)
   renames Ada.Strings.Search.Find_Token;

   ---------
   -- "*" --
   ---------

   function "*"
     (Left  : Natural;
      Right : Character) return String
   is
   begin
      return Result : String (1 .. Left) with Relaxed_Initialization do
         for J in Result'Range loop
            Result (J) := Right;
            pragma Loop_Invariant
              (for all K in 1 .. J =>
                 Result (K)'Initialized and then Result (K) = Right);
         end loop;
      end return;
   end "*";

   function "*"
     (Left  : Natural;
      Right : String) return String
   is
      Ptr : Integer := 0;

      --  Parts of the proof involving manipulations with the modulo operator
      --  are complicated for the prover and can't be done automatically in
      --  the global subprogram. That's why we isolate them in these two ghost
      --  lemmas.

      procedure Lemma_Mod (K : Integer) with
        Ghost,
        Pre =>
          Right'Length /= 0
          and then Ptr mod Right'Length = 0
          and then Ptr in 0 .. Natural'Last - Right'Length
          and then K in Ptr .. Ptr + Right'Length - 1,
        Post => K mod Right'Length = K - Ptr;
      --  Lemma_Mod is applied to an index considered in Lemma_Split to prove
      --  that it has the right value modulo Right'Length.

      procedure Lemma_Split (Result : String) with
        Ghost,
        Relaxed_Initialization => Result,
        Pre                    =>
          Right'Length /= 0
            and then Result'First = 1
            and then Result'Last >= 0
            and then Ptr mod Right'Length = 0
            and then Ptr in 0 .. Result'Last - Right'Length
            and then Result (Result'First .. Ptr + Right'Length)'Initialized
            and then Result (Ptr + 1 .. Ptr + Right'Length) = Right,
        Post                   =>
          (for all K in Ptr + 1 .. Ptr + Right'Length =>
            Result (K) = Right (Right'First + (K - 1) mod Right'Length));
      --  Lemma_Split is used after Result (Ptr + 1 .. Ptr + Right'Length) is
      --  updated to Right and concludes that the characters match for each
      --  index when taken modulo Right'Length, as the considered slice starts
      --  at index 1 modulo Right'Length.

      ---------------
      -- Lemma_Mod --
      ---------------

      procedure Lemma_Mod (K : Integer) is null;

      -----------------
      -- Lemma_Split --
      -----------------

      procedure Lemma_Split (Result : String)
      is
      begin
         for K in Ptr + 1 .. Ptr + Right'Length loop
            Lemma_Mod (K - 1);
            pragma Loop_Invariant
              (for all J in Ptr + 1 .. K =>
                 Result (J) = Right (Right'First + (J - 1) mod Right'Length));
         end loop;
      end Lemma_Split;

   --  Start of processing for "*"

   begin
      if Right'Length = 0 then
         return "";
      end if;

      return Result : String (1 .. Left * Right'Length)
        with Relaxed_Initialization
      do
         for J in 1 .. Left loop
            Result (Ptr + 1 .. Ptr + Right'Length) := Right;
            Lemma_Split (Result);
            Ptr := Ptr + Right'Length;
            pragma Loop_Invariant (Ptr = J * Right'Length);
            pragma Loop_Invariant (Result (1 .. Ptr)'Initialized);
            pragma Loop_Invariant
              (for all K in 1 .. Ptr =>
                 Result (K) = Right (Right'First + (K - 1) mod Right'Length));
         end loop;
      end return;
   end "*";

   ------------
   -- Delete --
   ------------

   function Delete
     (Source  : String;
      From    : Positive;
      Through : Natural) return String
   is
   begin
      if From > Through then
         declare
            subtype Result_Type is String (1 .. Source'Length);

         begin
            return Result_Type (Source);
         end;

      elsif From not in Source'Range
        or else Through > Source'Last
      then
         pragma Annotate
           (CodePeer, False_Positive,
            "test always false", "self fullfilling prophecy");

         --  In most cases this raises an exception, but the case of deleting
         --  a null string at the end of the current one is a special-case, and
         --  reflects the equivalence with Replace_String (RM A.4.3 (86/3)).

         if From = Source'Last + 1 and then From = Through then
            return Source;
         else
            raise Index_Error;
         end if;

      else
         declare
            Front : constant Integer := From - Source'First;

         begin
            return Result : String (1 .. Source'Length - (Through - From + 1))
              with Relaxed_Initialization
            do
               Result (1 .. Front) :=
                 Source (Source'First .. From - 1);

               if Through < Source'Last then
                  Result (Front + 1 .. Result'Last) :=
                    Source (Through + 1 .. Source'Last);
               end if;
            end return;
         end;
      end if;
   end Delete;

   procedure Delete
     (Source  : in out String;
      From    : Positive;
      Through : Natural;
      Justify : Alignment := Left;
      Pad     : Character := Space)
   is
   begin
      Move (Source  => Delete (Source, From, Through),
            Target  => Source,
            Justify => Justify,
            Pad     => Pad);
   end Delete;

   ----------
   -- Head --
   ----------

   function Head
     (Source : String;
      Count  : Natural;
      Pad    : Character := Space) return String
   is
      subtype Result_Type is String (1 .. Count);

   begin
      if Count <= Source'Length then
         return
           Result_Type (Source (Source'First .. Source'First + (Count - 1)));

      else
         return Result : Result_Type with Relaxed_Initialization do
            Result (1 .. Source'Length) := Source;

            for J in Source'Length + 1 .. Count loop
               Result (J) := Pad;
               pragma Loop_Invariant
                 (for all K in Source'Length + 1 .. J =>
                    Result (K)'Initialized and then Result (K) = Pad);
            end loop;
         end return;
      end if;
   end Head;

   procedure Head
     (Source  : in out String;
      Count   : Natural;
      Justify : Alignment := Left;
      Pad     : Character := Space)
   is
   begin
      Move (Source  => Head (Source, Count, Pad),
            Target  => Source,
            Drop    => Error,
            Justify => Justify,
            Pad     => Pad);
   end Head;

   ------------
   -- Insert --
   ------------

   function Insert
     (Source   : String;
      Before   : Positive;
      New_Item : String) return String
   is
      Front : constant Integer := Before - Source'First;

   begin
      if Before - 1 not in Source'First - 1 .. Source'Last then
         raise Index_Error;
      end if;

      return Result : String (1 .. Source'Length + New_Item'Length)
        with Relaxed_Initialization
      do
         Result (1 .. Front) :=
           Source (Source'First .. Before - 1);
         Result (Front + 1 .. Front + New_Item'Length) :=
           New_Item;

         pragma Assert
           (Result (1 .. Before - Source'First)
            = Source (Source'First .. Before - 1));
         pragma Assert
           (Result
              (Before - Source'First + 1
               .. Before - Source'First + New_Item'Length)
            = New_Item);

         if Before <= Source'Last then
            Result (Front + New_Item'Length + 1 .. Result'Last) :=
              Source (Before .. Source'Last);
         end if;

         pragma Assert
           (Result (1 .. Before - Source'First)
            = Source (Source'First .. Before - 1));
      end return;
   end Insert;

   procedure Insert
     (Source   : in out String;
      Before   : Positive;
      New_Item : String;
      Drop     : Truncation := Error)
   is
   begin
      Move (Source => Insert (Source, Before, New_Item),
            Target => Source,
            Drop   => Drop);
   end Insert;

   ----------
   -- Move --
   ----------

   procedure Move
     (Source  : String;
      Target  : out String;
      Drop    : Truncation := Error;
      Justify : Alignment  := Left;
      Pad     : Character  := Space)
   with SPARK_Mode => Off
   is
      Sfirst  : constant Integer := Source'First;
      Slast   : constant Integer := Source'Last;
      Slength : constant Integer := Source'Length;

      Tfirst  : constant Integer := Target'First;
      Tlast   : constant Integer := Target'Last;
      Tlength : constant Integer := Target'Length;

      function Is_Padding (Item : String) return Boolean;
      --  Check if Item is all Pad characters, return True if so, False if not

      function Is_Padding (Item : String) return Boolean is
      begin
         for J in Item'Range loop
            if Item (J) /= Pad then
               return False;
            end if;
         end loop;

         return True;
      end Is_Padding;

   --  Start of processing for Move

   begin
      if Slength = Tlength then
         Target := Source;

      elsif Slength > Tlength then
         case Drop is
            when Left =>
               Target := Source (Slast - Tlength + 1 .. Slast);

            when Right =>
               Target := Source (Sfirst .. Sfirst + Tlength - 1);

            when Error =>
               case Justify is
                  when Left =>
                     if Is_Padding (Source (Sfirst + Tlength .. Slast)) then
                        Target :=
                          Source (Sfirst .. Sfirst + Target'Length - 1);
                     else
                        raise Length_Error;
                     end if;

                  when Right =>
                     if Is_Padding (Source (Sfirst .. Slast - Tlength)) then
                        Target := Source (Slast - Tlength + 1 .. Slast);
                     else
                        raise Length_Error;
                     end if;

                  when Center =>
                     raise Length_Error;
               end case;
         end case;

      --  Source'Length < Target'Length

      else
         case Justify is
            when Left =>
               Target (Tfirst .. Tfirst + Slength - 1) := Source;

               for I in Tfirst + Slength .. Tlast loop
                  Target (I) := Pad;
               end loop;

            when Right =>
               for I in Tfirst .. Tlast - Slength loop
                  Target (I) := Pad;
               end loop;

               Target (Tlast - Slength + 1 .. Tlast) := Source;

            when Center =>
               declare
                  Front_Pad   : constant Integer := (Tlength - Slength) / 2;
                  Tfirst_Fpad : constant Integer := Tfirst + Front_Pad;

               begin
                  for I in Tfirst .. Tfirst_Fpad - 1 loop
                     Target (I) := Pad;
                  end loop;

                  Target (Tfirst_Fpad .. Tfirst_Fpad + Slength - 1) := Source;

                  for I in Tfirst_Fpad + Slength .. Tlast loop
                     Target (I) := Pad;
                  end loop;
               end;
         end case;
      end if;
   end Move;

   ---------------
   -- Overwrite --
   ---------------

   function Overwrite
     (Source   : String;
      Position : Positive;
      New_Item : String) return String is
   begin
      if Position - 1 not in Source'First - 1 .. Source'Last then
         raise Index_Error;
      end if;

      declare
         Result_Length : constant Natural :=
           Integer'Max (Source'Length,
                        Position - Source'First + New_Item'Length);
         Front         : constant Integer := Position - Source'First;

      begin
         return Result : String (1 .. Result_Length)
           with Relaxed_Initialization
         do
            Result (1 .. Front) := Source (Source'First .. Position - 1);
            pragma Assert
              (Result (1 .. Position - Source'First)
               = Source (Source'First .. Position - 1));
            Result (Front + 1 .. Front + New_Item'Length) := New_Item;
            pragma Assert
              (Result
                 (Position - Source'First + 1
                  .. Position - Source'First + New_Item'Length)
               = New_Item);

            if Position <= Source'Last - New_Item'Length then
               Result (Front + New_Item'Length + 1 .. Result'Last) :=
                 Source (Position + New_Item'Length .. Source'Last);

               pragma Assert
                 (Result
                    (Position - Source'First + New_Item'Length + 1
                     .. Result'Last)
                  = Source (Position + New_Item'Length .. Source'Last));
            end if;

            pragma Assert
              (if Position <= Source'Last - New_Item'Length
               then
                  Result
                    (Position - Source'First + New_Item'Length + 1
                     .. Result'Last)
                  = Source (Position + New_Item'Length .. Source'Last));
         end return;
      end;
   end Overwrite;

   procedure Overwrite
     (Source   : in out String;
      Position : Positive;
      New_Item : String;
      Drop     : Truncation := Right)
   is
   begin
      Move (Source => Overwrite (Source, Position, New_Item),
            Target => Source,
            Drop   => Drop);
   end Overwrite;

   -------------------
   -- Replace_Slice --
   -------------------

   function Replace_Slice
     (Source : String;
      Low    : Positive;
      High   : Natural;
      By     : String) return String is
   begin
      if Low - 1 > Source'Last or else High < Source'First - 1 then
         raise Index_Error;
      end if;

      if High >= Low then
         declare
            Front_Len : constant Integer :=
              Integer'Max (0, Low - Source'First);
            --  Length of prefix of Source copied to result

            Back_Len : constant Integer := Integer'Max (0, Source'Last - High);
            --  Length of suffix of Source copied to result

            Result_Length : constant Integer :=
              Front_Len + By'Length + Back_Len;
            --  Length of result

         begin
            return Result : String (1 .. Result_Length)
              with Relaxed_Initialization do
               Result (1 .. Front_Len) := Source (Source'First .. Low - 1);
               pragma Assert
                 (Result (1 .. Integer'Max (0, Low - Source'First))
                  = Source (Source'First .. Low - 1));
               Result (Front_Len + 1 .. Front_Len + By'Length) := By;
               pragma Assert
                 (Result
                    (Integer'Max (0, Low - Source'First) + 1
                     .. Integer'Max (0, Low - Source'First) + By'Length)
                  = By);

               if High < Source'Last then
                  Result (Front_Len + By'Length + 1 .. Result'Last) :=
                    Source (High + 1 .. Source'Last);
               end if;

               pragma Assert
                 (Result (1 .. Integer'Max (0, Low - Source'First))
                  = Source (Source'First .. Low - 1));
               pragma Assert
                 (Result
                    (Integer'Max (0, Low - Source'First) + 1
                     .. Integer'Max (0, Low - Source'First) + By'Length)
                  = By);
               pragma Assert
                 (if High < Source'Last
                  then
                     Result
                    (Integer'Max (0, Low - Source'First) + By'Length + 1
                     .. Result'Last)
                  = Source (High + 1 .. Source'Last));
            end return;
         end;
      else
         return Insert (Source, Before => Low, New_Item => By);
      end if;
   end Replace_Slice;

   procedure Replace_Slice
     (Source   : in out String;
      Low      : Positive;
      High     : Natural;
      By       : String;
      Drop     : Truncation := Error;
      Justify  : Alignment  := Left;
      Pad      : Character  := Space)
   is
   begin
      Move (Replace_Slice (Source, Low, High, By), Source, Drop, Justify, Pad);
   end Replace_Slice;

   ----------
   -- Tail --
   ----------

   function Tail
     (Source : String;
      Count  : Natural;
      Pad    : Character := Space) return String
   is
      subtype Result_Type is String (1 .. Count);

   begin
      if Count = 0 then
         return "";

      elsif Count < Source'Length then
         return Result_Type (Source (Source'Last - Count + 1 .. Source'Last));

      --  Pad on left

      else
         return Result : Result_Type with Relaxed_Initialization do
            for J in 1 .. Count - Source'Length loop
               Result (J) := Pad;
               pragma Loop_Invariant
                 (for all K in 1 .. J =>
                    Result (K)'Initialized and then Result (K) = Pad);
            end loop;

            if Source'Length /= 0 then
               Result (Count - Source'Length + 1 .. Count) := Source;
            end if;
         end return;
      end if;
   end Tail;

   procedure Tail
     (Source  : in out String;
      Count   : Natural;
      Justify : Alignment := Left;
      Pad     : Character := Space)
   is
   begin
      Move (Source  => Tail (Source, Count, Pad),
            Target  => Source,
            Drop    => Error,
            Justify => Justify,
            Pad     => Pad);
   end Tail;

   ---------------
   -- Translate --
   ---------------

   function Translate
     (Source  : String;
      Mapping : Maps.Character_Mapping) return String
   is
   begin
      return Result : String (1 .. Source'Length)
        with Relaxed_Initialization
      do
         for J in Source'Range loop
            Result (J - (Source'First - 1)) := Value (Mapping, Source (J));
            pragma Loop_Invariant
              (for all K in Source'First .. J =>
                 Result (K - (Source'First - 1))'Initialized);
            pragma Loop_Invariant
              (for all K in Source'First .. J =>
                 Result (K - (Source'First - 1)) =
                   Value (Mapping, Source (K)));
         end loop;
      end return;
   end Translate;

   procedure Translate
     (Source  : in out String;
      Mapping : Maps.Character_Mapping)
   is
   begin
      for J in Source'Range loop
         Source (J) := Value (Mapping, Source (J));
         pragma Loop_Invariant
           (for all K in Source'First .. J =>
              Source (K) = Value (Mapping, Source'Loop_Entry (K)));
      end loop;
   end Translate;

   function Translate
     (Source  : String;
      Mapping : Maps.Character_Mapping_Function) return String
   is
      pragma Unsuppress (Access_Check);
   begin
      return Result : String (1 .. Source'Length)
        with Relaxed_Initialization
      do
         for J in Source'Range loop
            Result (J - (Source'First - 1)) := Mapping.all (Source (J));
            pragma Annotate (GNATprove, False_Positive,
                             "call via access-to-subprogram",
                             "function Mapping must always terminate");
            pragma Loop_Invariant
              (for all K in Source'First .. J =>
                 Result (K - (Source'First - 1))'Initialized);
            pragma Loop_Invariant
              (for all K in Source'First .. J =>
                 Result (K - (Source'First - 1)) = Mapping (Source (K)));
            pragma Annotate (GNATprove, False_Positive,
                             "call via access-to-subprogram",
                             "function Mapping must always terminate");
         end loop;
      end return;
   end Translate;

   procedure Translate
     (Source  : in out String;
      Mapping : Maps.Character_Mapping_Function)
   is
      pragma Unsuppress (Access_Check);
   begin
      for J in Source'Range loop
         Source (J) := Mapping.all (Source (J));
         pragma Annotate (GNATprove, False_Positive,
                          "call via access-to-subprogram",
                          "function Mapping must always terminate");
         pragma Loop_Invariant
           (for all K in Source'First .. J =>
              Source (K) = Mapping (Source'Loop_Entry (K)));
         pragma Annotate (GNATprove, False_Positive,
                          "call via access-to-subprogram",
                          "function Mapping must always terminate");
      end loop;
   end Translate;

   ----------
   -- Trim --
   ----------

   function Trim
     (Source : String;
      Side   : Trim_End) return String
   is
      Empty_String : constant String (1 .. 0) := "";
      --  Without declaring the empty string as a separate string starting
      --  at 1, SPARK provers have trouble proving the postcondition.
   begin
      case Side is
         when Strings.Left =>
            declare
               Low : constant Natural := Index_Non_Blank (Source, Forward);
            begin
               --  All blanks case

               if Low = 0 then
                  return Empty_String;
               end if;

               declare
                  subtype Result_Type is String (1 .. Source'Last - Low + 1);
               begin
                  return Result_Type (Source (Low .. Source'Last));
               end;
            end;

         when Strings.Right =>
            declare
               High : constant Natural := Index_Non_Blank (Source, Backward);
            begin
               --  All blanks case

               if High = 0 then
                  return Empty_String;
               end if;

               declare
                  subtype Result_Type is String (1 .. High - Source'First + 1);
               begin
                  return Result_Type (Source (Source'First .. High));
               end;
            end;

         when Strings.Both =>
            declare
               Low : constant Natural := Index_Non_Blank (Source, Forward);
            begin
               --  All blanks case

               if Low = 0 then
                  return Empty_String;
               end if;

               declare
                  High : constant Natural :=
                    Index_Non_Blank (Source, Backward);
                  subtype Result_Type is String (1 .. High - Low + 1);
               begin
                  return Result_Type (Source (Low .. High));
               end;
            end;
      end case;
   end Trim;

   procedure Trim
     (Source  : in out String;
      Side    : Trim_End;
      Justify : Alignment := Left;
      Pad     : Character := Space)
   is
   begin
      Move (Trim (Source, Side),
            Source,
            Justify => Justify,
            Pad => Pad);
   end Trim;

   function Trim
     (Source : String;
      Left   : Maps.Character_Set;
      Right  : Maps.Character_Set) return String
   is
      High, Low : Integer;

   begin
      Low := Index (Source, Set => Left, Test => Outside, Going => Forward);

      --  Case where source comprises only characters in Left

      if Low = 0 then
         return "";
      end if;

      High := Index (Source, Set => Right, Test => Outside, Going => Backward);

      --  Case where source comprises only characters in Right

      if High = 0 then
         return "";
      end if;

      declare
         Result_Length : constant Integer := High - Low + 1;
         subtype Result_Type is String (1 .. Result_Length);

      begin
         return Result_Type (Source (Low .. High));
      end;
   end Trim;

   procedure Trim
     (Source  : in out String;
      Left    : Maps.Character_Set;
      Right   : Maps.Character_Set;
      Justify : Alignment := Strings.Left;
      Pad     : Character := Space)
   is
   begin
      Move (Source  => Trim (Source, Left, Right),
            Target  => Source,
            Justify => Justify,
            Pad     => Pad);
   end Trim;

end Ada.Strings.Fixed;