OSDN Git Service

gcc/ChangeLog:
[pf3gnuchains/gcc-fork.git] / gcc / ada / g-comlin.adb
index 8a4f19b..eb98254 100644 (file)
 --                                                                          --
 --                                 B o d y                                  --
 --                                                                          --
---          Copyright (C) 1999-2003 Free Software Foundation, Inc.          --
+--          Copyright (C) 1999-2009, 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 2,  or (at your option) any later ver- --
+-- 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.  See the GNU General Public License --
--- for  more details.  You should have  received  a copy of the GNU General --
--- Public License  distributed with GNAT;  see file COPYING.  If not, write --
--- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
--- MA 02111-1307, USA.                                                      --
+-- or FITNESS FOR A PARTICULAR PURPOSE.                                     --
 --                                                                          --
--- As a special exception,  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  GNU  General  Public  License.  This exception does not --
--- however invalidate  any other reasons why  the executable file  might be --
--- covered by the  GNU Public License.                                      --
+-- 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.      --
 --                                                                          --
 ------------------------------------------------------------------------------
 
-with Ada.Command_Line;
+with Ada.Unchecked_Deallocation;
+with Ada.Strings.Unbounded;
+
 with GNAT.OS_Lib; use GNAT.OS_Lib;
 
 package body GNAT.Command_Line is
 
    package CL renames Ada.Command_Line;
 
-   type Section_Number is new Natural range 0 .. 65534;
-   for Section_Number'Size use 16;
-
-   type Parameter_Type is record
-      Arg_Num : Positive;
-      First   : Positive;
-      Last    : Positive;
-   end record;
-   The_Parameter : Parameter_Type;
-   The_Switch    : Parameter_Type;
-   --  This type and this variable are provided to store the current switch
-   --  and parameter
-
-   type Is_Switch_Type is array (1 .. CL.Argument_Count) of Boolean;
-   pragma Pack (Is_Switch_Type);
-
-   Is_Switch : Is_Switch_Type := (others => False);
-   --  Indicates wich arguments on the command line are considered not be
-   --  switches or parameters to switches (this leaves e.g. the filenames...)
-
-   type Section_Type is array (1 .. CL.Argument_Count + 1) of Section_Number;
-   pragma Pack (Section_Type);
-   Section : Section_Type := (others => 1);
-   --  Contains the number of the section associated with the current
-   --  switch.  If this number is 0, then it is a section delimiter, which
-   --  is never returns by GetOpt.
-   --  The last element of this array is set to 0 to avoid the need to test for
-   --  if we have reached the end of the command line in loops.
-
-   Current_Argument : Natural := 1;
-   --  Number of the current argument parsed on the command line
-
-   Current_Index : Natural := 1;
-   --  Index in the current argument of the character to be processed
-
-   Current_Section : Section_Number := 1;
-
-   Expansion_It : aliased Expansion_Iterator;
-   --  When Get_Argument is expanding a file name, this is the iterator used
-
-   In_Expansion : Boolean := False;
-   --  True if we are expanding a file
-
-   Switch_Character : Character := '-';
-   --  The character at the beginning of the command line arguments,
-   --  indicating the beginning of a switch
-
-   Stop_At_First : Boolean := False;
-   --  If it is True then Getopt stops at the first non-switch argument
+   type Switch_Parameter_Type is
+     (Parameter_None,
+      Parameter_With_Optional_Space,  --  ':' in getopt
+      Parameter_With_Space_Or_Equal,  --  '=' in getopt
+      Parameter_No_Space,             --  '!' in getopt
+      Parameter_Optional);            --  '?' in getopt
 
    procedure Set_Parameter
      (Variable : out Parameter_Type;
       Arg_Num  : Positive;
       First    : Positive;
-      Last     : Positive);
+      Last     : Positive;
+      Extra    : Character := ASCII.NUL);
    pragma Inline (Set_Parameter);
    --  Set the parameter that will be returned by Parameter below
+   --  Parameters need to be defined ???
 
-   function Goto_Next_Argument_In_Section return Boolean;
-   --  Go to the next argument on the command line. If we are at the end
-   --  of the current section, we want to make sure there is no other
-   --  identical section on the command line (there might be multiple
-   --  instances of -largs). Returns True iff there is another argument.
+   function Goto_Next_Argument_In_Section (Parser : Opt_Parser) return Boolean;
+   --  Go to the next argument on the command line. If we are at the end of
+   --  the current section, we want to make sure there is no other identical
+   --  section on the command line (there might be multiple instances of
+   --  -largs). Returns True iff there is another argument.
 
    function Get_File_Names_Case_Sensitive return Integer;
    pragma Import (C, Get_File_Names_Case_Sensitive,
                   "__gnat_get_file_names_case_sensitive");
+
    File_Names_Case_Sensitive : constant Boolean :=
                                  Get_File_Names_Case_Sensitive /= 0;
 
    procedure Canonical_Case_File_Name (S : in out String);
-   --  Given a file name, converts it to canonical case form. For systems
-   --  where file names are case sensitive, this procedure has no effect.
-   --  If file names are not case sensitive (i.e. for example if you have
-   --  the file "xyz.adb", you can refer to it as XYZ.adb or XyZ.AdB), then
-   --  this call converts the given string to canonical all lower case form,
-   --  so that two file names compare equal if they refer to the same file.
+   --  Given a file name, converts it to canonical case form. For systems where
+   --  file names are case sensitive, this procedure has no effect. If file
+   --  names are not case sensitive (i.e. for example if you have the file
+   --  "xyz.adb", you can refer to it as XYZ.adb or XyZ.AdB), then this call
+   --  converts the given string to canonical all lower case form, so that two
+   --  file names compare equal if they refer to the same file.
+
+   procedure Internal_Initialize_Option_Scan
+     (Parser                   : Opt_Parser;
+      Switch_Char              : Character;
+      Stop_At_First_Non_Switch : Boolean;
+      Section_Delimiters       : String);
+   --  Initialize Parser, which must have been allocated already
+
+   function Argument (Parser : Opt_Parser; Index : Integer) return String;
+   --  Return the index-th command line argument
+
+   procedure Find_Longest_Matching_Switch
+     (Switches          : String;
+      Arg               : String;
+      Index_In_Switches : out Integer;
+      Switch_Length     : out Integer;
+      Param             : out Switch_Parameter_Type);
+   --  Return the Longest switch from Switches that at least partially
+   --  partially Arg. Index_In_Switches is set to 0 if none matches.
+   --  What are other parameters??? in particular Param is not always set???
+
+   procedure Unchecked_Free is new Ada.Unchecked_Deallocation
+     (Argument_List, Argument_List_Access);
+
+   procedure Unchecked_Free is new Ada.Unchecked_Deallocation
+     (Command_Line_Configuration_Record, Command_Line_Configuration);
+
+   procedure Remove (Line : in out Argument_List_Access; Index : Integer);
+   --  Remove a specific element from Line
+
+   procedure Add
+     (Line   : in out Argument_List_Access;
+      Str    : String_Access;
+      Before : Boolean := False);
+   --  Add a new element to Line. If Before is True, the item is inserted at
+   --  the beginning, else it is appended.
+
+   function Can_Have_Parameter (S : String) return Boolean;
+   --  True if S can have a parameter
+
+   function Require_Parameter (S : String) return Boolean;
+   --  True if S requires a parameter
+
+   function Actual_Switch (S : String) return String;
+   --  Remove any possible trailing '!', ':', '?' and '='
+
+   generic
+      with procedure Callback (Simple_Switch : String; Parameter : String);
+   procedure For_Each_Simple_Switch
+     (Cmd       : Command_Line;
+      Switch    : String;
+      Parameter : String  := "";
+      Unalias   : Boolean := True);
+   --  Breaks Switch into as simple switches as possible (expanding aliases and
+   --  ungrouping common prefixes when possible), and call Callback for each of
+   --  these.
+
+   procedure Sort_Sections
+     (Line     : GNAT.OS_Lib.Argument_List_Access;
+      Sections : GNAT.OS_Lib.Argument_List_Access;
+      Params   : GNAT.OS_Lib.Argument_List_Access);
+   --  Reorder the command line switches so that the switches belonging to a
+   --  section are grouped together.
+
+   procedure Group_Switches
+     (Cmd      : Command_Line;
+      Result   : Argument_List_Access;
+      Sections : Argument_List_Access;
+      Params   : Argument_List_Access);
+   --  Group switches with common prefixes whenever possible. Once they have
+   --  been grouped, we also check items for possible aliasing.
+
+   procedure Alias_Switches
+     (Cmd    : Command_Line;
+      Result : Argument_List_Access;
+      Params : Argument_List_Access);
+   --  When possible, replace one or more switches by an alias, i.e. a shorter
+   --  version.
+
+   function Looking_At
+     (Type_Str  : String;
+      Index     : Natural;
+      Substring : String) return Boolean;
+   --  Return True if the characters starting at Index in Type_Str are
+   --  equivalent to Substring.
+
+   --------------
+   -- Argument --
+   --------------
+
+   function Argument (Parser : Opt_Parser; Index : Integer) return String is
+   begin
+      if Parser.Arguments /= null then
+         return Parser.Arguments (Index + Parser.Arguments'First - 1).all;
+      else
+         return CL.Argument (Index);
+      end if;
+   end Argument;
 
    ------------------------------
    -- Canonical_Case_File_Name --
@@ -125,8 +183,8 @@ package body GNAT.Command_Line is
       if not File_Names_Case_Sensitive then
          for J in S'Range loop
             if S (J) in 'A' .. 'Z' then
-               S (J) := Character'Val (
-                          Character'Pos (S (J)) +
+               S (J) := Character'Val
+                         (Character'Pos (S (J)) +
                           Character'Pos ('a')   -
                           Character'Pos ('A'));
             end if;
@@ -142,16 +200,16 @@ package body GNAT.Command_Line is
       use GNAT.Directory_Operations;
       type Pointer is access all Expansion_Iterator;
 
+      It   : constant Pointer := Iterator'Unrestricted_Access;
       S    : String (1 .. 1024);
       Last : Natural;
-      It   : Pointer := Iterator'Unrestricted_Access;
 
       Current : Depth := It.Current_Depth;
       NL      : Positive;
 
    begin
-      --  It is assumed that a directory is opened at the current level;
-      --  otherwise, GNAT.Directory_Operations.Directory_Error will be raised
+      --  It is assumed that a directory is opened at the current level.
+      --  Otherwise GNAT.Directory_Operations.Directory_Error will be raised
       --  at the first call to Read.
 
       loop
@@ -162,12 +220,12 @@ package body GNAT.Command_Line is
          if Last = 0 then
             Close (It.Levels (Current).Dir);
 
-            --  If we are at level 1, we are finished; return an empty string.
+            --  If we are at level 1, we are finished; return an empty string
 
             if Current = 1 then
                return String'(1 .. 0 => ' ');
             else
-               --  Otherwise, continue with the directory at the previous level
+               --  Otherwise continue with the directory at the previous level
 
                Current := Current - 1;
                It.Current_Depth := Current;
@@ -204,118 +262,127 @@ package body GNAT.Command_Line is
                     (It.Levels (Current).Dir, It.Dir_Name (1 .. NL));
                end if;
             end if;
+         end if;
 
-         --  If not a directory, check the relative path against the pattern
+         --  Check the relative path against the pattern
 
-         else
-            declare
-               Name : String :=
-                 It.Dir_Name (It.Start .. It.Levels (Current).Name_Last) &
-                 S (1 .. Last);
-            begin
-               Canonical_Case_File_Name (Name);
+         --  Note that we try to match also against directory names, since
+         --  clients of this function may expect to retrieve directories.
 
-               --  If it matches, return the relative path
+         declare
+            Name : String :=
+                     It.Dir_Name (It.Start .. It.Levels (Current).Name_Last)
+                       & S (1 .. Last);
 
-               if GNAT.Regexp.Match (Name, Iterator.Regexp) then
-                  return Name;
-               end if;
-            end;
-         end if;
+         begin
+            Canonical_Case_File_Name (Name);
 
-      end loop;
+            --  If it matches return the relative path
 
-      return String'(1 .. 0 => ' ');
+            if GNAT.Regexp.Match (Name, Iterator.Regexp) then
+               return Name;
+            end if;
+         end;
+      end loop;
    end Expansion;
 
    -----------------
    -- Full_Switch --
    -----------------
 
-   function Full_Switch return String is
+   function Full_Switch
+     (Parser : Opt_Parser := Command_Line_Parser) return String
+   is
    begin
-      return CL.Argument (The_Switch.Arg_Num)
-        (The_Switch.First .. The_Switch.Last);
+      if Parser.The_Switch.Extra = ASCII.NUL then
+         return Argument (Parser, Parser.The_Switch.Arg_Num)
+           (Parser.The_Switch.First .. Parser.The_Switch.Last);
+      else
+         return Parser.The_Switch.Extra
+           & Argument (Parser, Parser.The_Switch.Arg_Num)
+           (Parser.The_Switch.First .. Parser.The_Switch.Last);
+      end if;
    end Full_Switch;
 
    ------------------
    -- Get_Argument --
    ------------------
 
-   function Get_Argument (Do_Expansion : Boolean := False) return String is
-      Total : constant Natural := CL.Argument_Count;
-
+   function Get_Argument
+     (Do_Expansion : Boolean    := False;
+      Parser       : Opt_Parser := Command_Line_Parser) return String
+   is
    begin
-      if In_Expansion then
+      if Parser.In_Expansion then
          declare
-            S : constant String := Expansion (Expansion_It);
-
+            S : constant String := Expansion (Parser.Expansion_It);
          begin
             if S'Length /= 0 then
                return S;
             else
-               In_Expansion := False;
+               Parser.In_Expansion := False;
             end if;
          end;
       end if;
 
-      if Current_Argument > Total then
+      if Parser.Current_Argument > Parser.Arg_Count then
 
          --  If this is the first time this function is called
 
-         if Current_Index = 1 then
-            Current_Argument := 1;
-            while Current_Argument <= CL.Argument_Count
-              and then Section (Current_Argument) /= Current_Section
+         if Parser.Current_Index = 1 then
+            Parser.Current_Argument := 1;
+            while Parser.Current_Argument <= Parser.Arg_Count
+              and then Parser.Section (Parser.Current_Argument) /=
+                Parser.Current_Section
             loop
-               Current_Argument := Current_Argument + 1;
+               Parser.Current_Argument := Parser.Current_Argument + 1;
             end loop;
          else
             return String'(1 .. 0 => ' ');
          end if;
 
-      elsif Section (Current_Argument) = 0 then
-         while Current_Argument <= CL.Argument_Count
-           and then Section (Current_Argument) /= Current_Section
+      elsif Parser.Section (Parser.Current_Argument) = 0 then
+         while Parser.Current_Argument <= Parser.Arg_Count
+           and then Parser.Section (Parser.Current_Argument) /=
+             Parser.Current_Section
          loop
-            Current_Argument := Current_Argument + 1;
+            Parser.Current_Argument := Parser.Current_Argument + 1;
          end loop;
       end if;
 
-      Current_Index := 2;
+      Parser.Current_Index := Integer'Last;
 
-      while Current_Argument <= Total
-        and then Is_Switch (Current_Argument)
+      while Parser.Current_Argument <= Parser.Arg_Count
+        and then Parser.Is_Switch (Parser.Current_Argument)
       loop
-         Current_Argument := Current_Argument + 1;
+         Parser.Current_Argument := Parser.Current_Argument + 1;
       end loop;
 
-      if Current_Argument > Total then
+      if Parser.Current_Argument > Parser.Arg_Count then
          return String'(1 .. 0 => ' ');
-      end if;
-
-      if Section (Current_Argument) = 0 then
+      elsif Parser.Section (Parser.Current_Argument) = 0 then
          return Get_Argument (Do_Expansion);
       end if;
 
-      Current_Argument := Current_Argument + 1;
+      Parser.Current_Argument := Parser.Current_Argument + 1;
 
       --  Could it be a file name with wild cards to expand?
 
       if Do_Expansion then
          declare
-            Arg       : String renames CL.Argument (Current_Argument - 1);
-            Index     : Positive := Arg'First;
+            Arg   : constant String :=
+                      Argument (Parser, Parser.Current_Argument - 1);
+            Index : Positive;
 
          begin
+            Index := Arg'First;
             while Index <= Arg'Last loop
-
                if Arg (Index) = '*'
                  or else Arg (Index) = '?'
                  or else Arg (Index) = '['
                then
-                  In_Expansion := True;
-                  Start_Expansion (Expansion_It, Arg);
+                  Parser.In_Expansion := True;
+                  Start_Expansion (Parser.Expansion_It, Arg);
                   return Get_Argument (Do_Expansion);
                end if;
 
@@ -324,278 +391,351 @@ package body GNAT.Command_Line is
          end;
       end if;
 
-      return CL.Argument (Current_Argument - 1);
+      return Argument (Parser, Parser.Current_Argument - 1);
    end Get_Argument;
 
+   ----------------------------------
+   -- Find_Longest_Matching_Switch --
+   ----------------------------------
+
+   procedure Find_Longest_Matching_Switch
+     (Switches          : String;
+      Arg               : String;
+      Index_In_Switches : out Integer;
+      Switch_Length     : out Integer;
+      Param             : out Switch_Parameter_Type)
+   is
+      Index  : Natural;
+      Length : Natural := 1;
+      P      : Switch_Parameter_Type;
+
+   begin
+      Index_In_Switches := 0;
+      Switch_Length     := 0;
+
+      --  Remove all leading spaces first to make sure that Index points
+      --  at the start of the first switch.
+
+      Index := Switches'First;
+      while Index <= Switches'Last and then Switches (Index) = ' ' loop
+         Index := Index + 1;
+      end loop;
+
+      while Index <= Switches'Last loop
+
+         --  Search the length of the parameter at this position in Switches
+
+         Length := Index;
+         while Length <= Switches'Last
+           and then Switches (Length) /= ' '
+         loop
+            Length := Length + 1;
+         end loop;
+
+         if Length = Index + 1 then
+            P := Parameter_None;
+         else
+            case Switches (Length - 1) is
+               when ':'    =>
+                  P      := Parameter_With_Optional_Space;
+                  Length := Length - 1;
+               when '='    =>
+                  P      := Parameter_With_Space_Or_Equal;
+                  Length := Length - 1;
+               when '!'    =>
+                  P      := Parameter_No_Space;
+                  Length := Length - 1;
+               when '?'    =>
+                  P      := Parameter_Optional;
+                  Length := Length - 1;
+               when others =>
+                  P      := Parameter_None;
+            end case;
+         end if;
+
+         --  If it is the one we searched, it may be a candidate
+
+         if Arg'First + Length - 1 - Index <= Arg'Last
+           and then Switches (Index .. Length - 1) =
+                      Arg (Arg'First .. Arg'First + Length - 1 - Index)
+           and then Length - Index > Switch_Length
+         then
+            Param             := P;
+            Index_In_Switches := Index;
+            Switch_Length     := Length - Index;
+         end if;
+
+         --  Look for the next switch in Switches
+
+         while Index <= Switches'Last
+           and then Switches (Index) /= ' '
+         loop
+            Index := Index + 1;
+         end loop;
+
+         Index := Index + 1;
+      end loop;
+   end Find_Longest_Matching_Switch;
+
    ------------
    -- Getopt --
    ------------
 
    function Getopt
      (Switches    : String;
-      Concatenate : Boolean := True) return Character
+      Concatenate : Boolean := True;
+      Parser      : Opt_Parser := Command_Line_Parser) return Character
    is
       Dummy : Boolean;
       pragma Unreferenced (Dummy);
 
    begin
+      <<Restart>>
+
       --  If we have finished parsing the current command line item (there
       --  might be multiple switches in a single item), then go to the next
       --  element
 
-      if Current_Argument > CL.Argument_Count
-        or else (Current_Index > CL.Argument (Current_Argument)'Last
-                   and then not Goto_Next_Argument_In_Section)
+      if Parser.Current_Argument > Parser.Arg_Count
+        or else (Parser.Current_Index >
+                   Argument (Parser, Parser.Current_Argument)'Last
+                 and then not Goto_Next_Argument_In_Section (Parser))
       then
          return ASCII.NUL;
       end if;
 
-      --  If we are on a new item, test if this might be a switch
-
-      if Current_Index = 1 then
-         if CL.Argument (Current_Argument)(1) /= Switch_Character then
-            if Switches (Switches'First) = '*' then
-               Set_Parameter (The_Switch,
-                              Arg_Num => Current_Argument,
-                              First   => 1,
-                              Last    => CL.Argument (Current_Argument)'Last);
-               Is_Switch (Current_Argument) := True;
-               Dummy := Goto_Next_Argument_In_Section;
-               return '*';
-            end if;
-
-            if Stop_At_First then
-               Current_Argument := Positive'Last;
-               return ASCII.NUL;
-
-            elsif not Goto_Next_Argument_In_Section then
-               return ASCII.NUL;
-
-            else
-               return Getopt (Switches);
-            end if;
-         end if;
+      --  By default, the switch will not have a parameter
 
-         Current_Index := 2;
-         Is_Switch (Current_Argument) := True;
-      end if;
+      Parser.The_Parameter :=
+        (Integer'Last, Integer'Last, Integer'Last - 1, ASCII.NUL);
+      Parser.The_Separator := ASCII.NUL;
 
       declare
-         Arg            : String renames CL.Argument (Current_Argument);
+         Arg            : constant String :=
+                            Argument (Parser, Parser.Current_Argument);
          Index_Switches : Natural := 0;
          Max_Length     : Natural := 0;
-         Index          : Natural := Switches'First;
-         Length         : Natural := 1;
          End_Index      : Natural;
-
+         Param          : Switch_Parameter_Type;
       begin
-         while Index <= Switches'Last loop
-
-            --  Search the length of the parameter at this position in Switches
+         --  If we are on a new item, test if this might be a switch
+
+         if Parser.Current_Index = Arg'First then
+            if Arg (Arg'First) /= Parser.Switch_Character then
+
+               --  If it isn't a switch, return it immediately. We also know it
+               --  isn't the parameter to a previous switch, since that has
+               --  already been handled
+
+               if Switches (Switches'First) = '*' then
+                  Set_Parameter
+                    (Parser.The_Switch,
+                     Arg_Num => Parser.Current_Argument,
+                     First   => Arg'First,
+                     Last    => Arg'Last);
+                  Parser.Is_Switch (Parser.Current_Argument) := True;
+                  Dummy := Goto_Next_Argument_In_Section (Parser);
+                  return '*';
+               end if;
 
-            Length := Index;
-            while Length <= Switches'Last
-              and then Switches (Length) /= ' '
-            loop
-               Length := Length + 1;
-            end loop;
+               if Parser.Stop_At_First then
+                  Parser.Current_Argument := Positive'Last;
+                  return ASCII.NUL;
 
-            if (Switches (Length - 1) = ':'  or else
-                Switches (Length - 1) = '='  or else
-                Switches (Length - 1) = '?'  or else
-                Switches (Length - 1) = '!')
-              and then Length > Index + 1
-            then
-               Length := Length - 1;
-            end if;
+               elsif not Goto_Next_Argument_In_Section (Parser) then
+                  return ASCII.NUL;
 
-            --  If it is the one we searched, it may be a candidate
+               else
+                  --  Recurse to get the next switch on the command line
 
-            if Current_Index + Length - 1 - Index <= Arg'Last
-              and then
-                Switches (Index .. Length - 1) =
-                  Arg (Current_Index .. Current_Index + Length - 1 - Index)
-              and then Length - Index > Max_Length
-            then
-               Index_Switches := Index;
-               Max_Length     := Length - Index;
+                  goto Restart;
+               end if;
             end if;
 
-            --  Look for the next switch in Switches
+            --  We are on the first character of a new command line argument,
+            --  which starts with Switch_Character. Further analysis is needed.
 
-            while Index <= Switches'Last
-              and then Switches (Index) /= ' ' loop
-               Index := Index + 1;
-            end loop;
-
-            Index := Index + 1;
-         end loop;
+            Parser.Current_Index := Parser.Current_Index + 1;
+            Parser.Is_Switch (Parser.Current_Argument) := True;
+         end if;
 
-         End_Index := Current_Index + Max_Length - 1;
+         Find_Longest_Matching_Switch
+           (Switches          => Switches,
+            Arg               => Arg (Parser.Current_Index .. Arg'Last),
+            Index_In_Switches => Index_Switches,
+            Switch_Length     => Max_Length,
+            Param             => Param);
 
-         --  If switch is not accepted, skip it, unless we had '*' in Switches
+         --  If switch is not accepted, it is either invalid or is returned
+         --  in the context of '*'.
 
          if Index_Switches = 0 then
-            if Switches (Switches'First) = '*' then
-               Set_Parameter (The_Switch,
-                              Arg_Num => Current_Argument,
-                              First   => 1,
-                              Last    => CL.Argument (Current_Argument)'Last);
-               Is_Switch (Current_Argument) := True;
-               Dummy := Goto_Next_Argument_In_Section;
-               return '*';
-            end if;
 
             --  Depending on the value of Concatenate, the full switch is
-            --  a single character (True) or the rest of the argument (False).
+            --  a single character or the rest of the argument.
 
-            if Concatenate then
-               End_Index := Current_Index;
-            else
-               End_Index := Arg'Last;
+            End_Index :=
+              (if Concatenate then Parser.Current_Index else Arg'Last);
+
+            if Switches (Switches'First) = '*' then
+
+               --  Always prepend the switch character, so that users know that
+               --  this comes from a switch on the command line. This is
+               --  especially important when Concatenate is False, since
+               --  otherwise the current argument first character is lost.
+
+               Set_Parameter
+                 (Parser.The_Switch,
+                  Arg_Num => Parser.Current_Argument,
+                  First   => Parser.Current_Index,
+                  Last    => Arg'Last,
+                  Extra   => Parser.Switch_Character);
+               Parser.Is_Switch (Parser.Current_Argument) := True;
+               Dummy := Goto_Next_Argument_In_Section (Parser);
+               return '*';
             end if;
 
-            Set_Parameter (The_Switch,
-                           Arg_Num => Current_Argument,
-                           First   => Current_Index,
-                           Last    => End_Index);
-            Current_Index := End_Index + 1;
+            Set_Parameter
+              (Parser.The_Switch,
+               Arg_Num => Parser.Current_Argument,
+               First   => Parser.Current_Index,
+               Last    => End_Index);
+            Parser.Current_Index := End_Index + 1;
             raise Invalid_Switch;
          end if;
 
-         Set_Parameter (The_Switch,
-                        Arg_Num => Current_Argument,
-                        First   => Current_Index,
-                        Last    => End_Index);
-
-         --  Case of switch needs an argument
-
-         if Index_Switches + Max_Length <= Switches'Last then
+         End_Index := Parser.Current_Index + Max_Length - 1;
+         Set_Parameter
+           (Parser.The_Switch,
+            Arg_Num => Parser.Current_Argument,
+            First   => Parser.Current_Index,
+            Last    => End_Index);
+
+         case Param is
+            when Parameter_With_Optional_Space =>
+               if End_Index < Arg'Last then
+                  Set_Parameter
+                    (Parser.The_Parameter,
+                     Arg_Num => Parser.Current_Argument,
+                     First   => End_Index + 1,
+                     Last    => Arg'Last);
+                  Dummy := Goto_Next_Argument_In_Section (Parser);
+
+               elsif Parser.Current_Argument < Parser.Arg_Count
+                 and then Parser.Section (Parser.Current_Argument + 1) /= 0
+               then
+                  Parser.Current_Argument := Parser.Current_Argument + 1;
+                  Parser.The_Separator := ' ';
+                  Set_Parameter
+                    (Parser.The_Parameter,
+                     Arg_Num => Parser.Current_Argument,
+                     First => Argument (Parser, Parser.Current_Argument)'First,
+                     Last  => Argument (Parser, Parser.Current_Argument)'Last);
+                  Parser.Is_Switch (Parser.Current_Argument) := True;
+                  Dummy := Goto_Next_Argument_In_Section (Parser);
+
+               else
+                  Parser.Current_Index := End_Index + 1;
+                  raise Invalid_Parameter;
+               end if;
 
-            case Switches (Index_Switches + Max_Length) is
+            when Parameter_With_Space_Or_Equal =>
 
-               when ':' =>
+               --  If the switch is of the form <switch>=xxx
 
-                  if End_Index < Arg'Last then
-                     Set_Parameter (The_Parameter,
-                                    Arg_Num => Current_Argument,
-                                    First   => End_Index + 1,
-                                    Last    => Arg'Last);
-                     Dummy := Goto_Next_Argument_In_Section;
+               if End_Index < Arg'Last then
 
-                  elsif Section (Current_Argument + 1) /= 0 then
+                  if Arg (End_Index + 1) = '='
+                    and then End_Index + 1 < Arg'Last
+                  then
+                     Parser.The_Separator := '=';
                      Set_Parameter
-                       (The_Parameter,
-                        Arg_Num => Current_Argument + 1,
-                        First   => 1,
-                        Last    => CL.Argument (Current_Argument + 1)'Last);
-                     Current_Argument := Current_Argument + 1;
-                     Is_Switch (Current_Argument) := True;
-                     Dummy := Goto_Next_Argument_In_Section;
-
+                       (Parser.The_Parameter,
+                        Arg_Num => Parser.Current_Argument,
+                        First   => End_Index + 2,
+                        Last    => Arg'Last);
+                     Dummy := Goto_Next_Argument_In_Section (Parser);
                   else
-                     Current_Index := End_Index + 1;
+                     Parser.Current_Index := End_Index + 1;
                      raise Invalid_Parameter;
                   end if;
 
-               when '=' =>
-
-                  --  If the switch is of the form <switch>=xxx
+               --  If the switch is of the form <switch> xxx
 
-                  if End_Index < Arg'Last then
-
-                     if Arg (End_Index + 1) = '='
-                       and then End_Index + 1 < Arg'Last
-                     then
-                        Set_Parameter (The_Parameter,
-                                       Arg_Num => Current_Argument,
-                                       First   => End_Index + 2,
-                                       Last    => Arg'Last);
-                        Dummy := Goto_Next_Argument_In_Section;
-
-                     else
-                        Current_Index := End_Index + 1;
-                        raise Invalid_Parameter;
-                     end if;
-
-                  --  If the switch is of the form <switch> xxx
+               elsif Parser.Current_Argument < Parser.Arg_Count
+                 and then Parser.Section (Parser.Current_Argument + 1) /= 0
+               then
+                  Parser.Current_Argument := Parser.Current_Argument + 1;
+                  Parser.The_Separator := ' ';
+                  Set_Parameter
+                    (Parser.The_Parameter,
+                     Arg_Num => Parser.Current_Argument,
+                     First => Argument (Parser, Parser.Current_Argument)'First,
+                     Last  => Argument (Parser, Parser.Current_Argument)'Last);
+                  Parser.Is_Switch (Parser.Current_Argument) := True;
+                  Dummy := Goto_Next_Argument_In_Section (Parser);
+
+               else
+                  Parser.Current_Index := End_Index + 1;
+                  raise Invalid_Parameter;
+               end if;
 
-                  elsif Section (Current_Argument + 1) /= 0 then
-                     Set_Parameter
-                       (The_Parameter,
-                        Arg_Num => Current_Argument + 1,
-                        First   => 1,
-                        Last    => CL.Argument (Current_Argument + 1)'Last);
-                     Current_Argument := Current_Argument + 1;
-                     Is_Switch (Current_Argument) := True;
-                     Dummy := Goto_Next_Argument_In_Section;
+            when Parameter_No_Space =>
 
-                  else
-                     Current_Index := End_Index + 1;
-                     raise Invalid_Parameter;
-                  end if;
+               if End_Index < Arg'Last then
+                  Set_Parameter
+                    (Parser.The_Parameter,
+                     Arg_Num => Parser.Current_Argument,
+                     First   => End_Index + 1,
+                     Last    => Arg'Last);
+                  Dummy := Goto_Next_Argument_In_Section (Parser);
 
-               when '!' =>
+               else
+                  Parser.Current_Index := End_Index + 1;
+                  raise Invalid_Parameter;
+               end if;
 
-                  if End_Index < Arg'Last then
-                     Set_Parameter (The_Parameter,
-                                    Arg_Num => Current_Argument,
-                                    First   => End_Index + 1,
-                                    Last    => Arg'Last);
-                     Dummy := Goto_Next_Argument_In_Section;
+            when Parameter_Optional =>
 
-                  else
-                     Current_Index := End_Index + 1;
-                     raise Invalid_Parameter;
-                  end if;
+               if End_Index < Arg'Last then
+                  Set_Parameter
+                    (Parser.The_Parameter,
+                     Arg_Num => Parser.Current_Argument,
+                     First   => End_Index + 1,
+                     Last    => Arg'Last);
+               end if;
 
-               when '?' =>
+               Dummy := Goto_Next_Argument_In_Section (Parser);
 
-                  if End_Index < Arg'Last then
-                     Set_Parameter (The_Parameter,
-                                    Arg_Num => Current_Argument,
-                                    First   => End_Index + 1,
-                                    Last    => Arg'Last);
+            when Parameter_None =>
 
-                  else
-                     Set_Parameter (The_Parameter,
-                                    Arg_Num => Current_Argument,
-                                    First   => 2,
-                                    Last    => 1);
-                  end if;
-                  Dummy := Goto_Next_Argument_In_Section;
+               if Concatenate or else End_Index = Arg'Last then
+                  Parser.Current_Index := End_Index + 1;
 
-               when others =>
-                  if Concatenate or else End_Index = Arg'Last then
-                     Current_Index := End_Index + 1;
+               else
+                  --  If Concatenate is False and the full argument is not
+                  --  recognized as a switch, this is an invalid switch.
 
-                  else
-                     --  If Concatenate is False and the full argument is not
-                     --  recognized as a switch, this is an invalid switch.
-
-                     Set_Parameter (The_Switch,
-                                    Arg_Num => Current_Argument,
-                                    First   => Current_Index,
-                                    Last    => Arg'Last);
-                     Current_Index := Arg'Last + 1;
-                     raise Invalid_Switch;
+                  if Switches (Switches'First) = '*' then
+                     Set_Parameter
+                       (Parser.The_Switch,
+                        Arg_Num => Parser.Current_Argument,
+                        First   => Arg'First,
+                        Last    => Arg'Last);
+                     Parser.Is_Switch (Parser.Current_Argument) := True;
+                     Dummy := Goto_Next_Argument_In_Section (Parser);
+                     return '*';
                   end if;
-            end case;
-
-         elsif Concatenate or else End_Index = Arg'Last then
-            Current_Index := End_Index + 1;
 
-         else
-            --  If Concatenate is False and the full argument is not
-            --  recognized as a switch, this is an invalid switch.
-
-            Set_Parameter (The_Switch,
-                           Arg_Num => Current_Argument,
-                           First   => Current_Index,
-                           Last    => Arg'Last);
-            Current_Index := Arg'Last + 1;
-            raise Invalid_Switch;
-         end if;
+                  Set_Parameter
+                    (Parser.The_Switch,
+                     Arg_Num => Parser.Current_Argument,
+                     First   => Parser.Current_Index,
+                     Last    => Arg'Last);
+                  Parser.Current_Index := Arg'Last + 1;
+                  raise Invalid_Switch;
+               end if;
+         end case;
 
          return Switches (Index_Switches);
       end;
@@ -605,21 +745,31 @@ package body GNAT.Command_Line is
    -- Goto_Next_Argument_In_Section --
    -----------------------------------
 
-   function Goto_Next_Argument_In_Section return Boolean is
+   function Goto_Next_Argument_In_Section
+     (Parser : Opt_Parser) return Boolean
+   is
    begin
-      Current_Index := 1;
-      Current_Argument := Current_Argument + 1;
+      Parser.Current_Argument := Parser.Current_Argument + 1;
 
-      if Section (Current_Argument) = 0 then
+      if Parser.Current_Argument > Parser.Arg_Count
+        or else Parser.Section (Parser.Current_Argument) = 0
+      then
          loop
-            if Current_Argument > CL.Argument_Count then
+            Parser.Current_Argument := Parser.Current_Argument + 1;
+
+            if Parser.Current_Argument > Parser.Arg_Count then
+               Parser.Current_Index := 1;
                return False;
             end if;
 
-            Current_Argument := Current_Argument + 1;
-            exit when Section (Current_Argument) = Current_Section;
+            exit when Parser.Section (Parser.Current_Argument) =
+                                                  Parser.Current_Section;
          end loop;
       end if;
+
+      Parser.Current_Index :=
+        Argument (Parser, Parser.Current_Argument)'First;
+
       return True;
    end Goto_Next_Argument_In_Section;
 
@@ -627,29 +777,33 @@ package body GNAT.Command_Line is
    -- Goto_Section --
    ------------------
 
-   procedure Goto_Section (Name : String := "") is
-      Index : Integer := 1;
+   procedure Goto_Section
+     (Name   : String := "";
+      Parser : Opt_Parser := Command_Line_Parser)
+   is
+      Index : Integer;
 
    begin
-      In_Expansion := False;
+      Parser.In_Expansion := False;
 
       if Name = "" then
-         Current_Argument := 1;
-         Current_Index    := 1;
-         Current_Section  := 1;
+         Parser.Current_Argument := 1;
+         Parser.Current_Index    := 1;
+         Parser.Current_Section  := 1;
          return;
       end if;
 
-      while Index <= CL.Argument_Count loop
-
-         if Section (Index) = 0
-           and then CL.Argument (Index) = Switch_Character & Name
+      Index := 1;
+      while Index <= Parser.Arg_Count loop
+         if Parser.Section (Index) = 0
+           and then Argument (Parser, Index) = Parser.Switch_Character & Name
          then
-            Current_Argument := Index + 1;
-            Current_Index    := 1;
+            Parser.Current_Argument := Index + 1;
+            Parser.Current_Index    := 1;
 
-            if Current_Argument <= CL.Argument_Count then
-               Current_Section := Section (Current_Argument);
+            if Parser.Current_Argument <= Parser.Arg_Count then
+               Parser.Current_Section :=
+                 Parser.Section (Parser.Current_Argument);
             end if;
             return;
          end if;
@@ -657,8 +811,8 @@ package body GNAT.Command_Line is
          Index := Index + 1;
       end loop;
 
-      Current_Argument := Positive'Last;
-      Current_Index := 2;   --  so that Get_Argument returns nothing
+      Parser.Current_Argument := Positive'Last;
+      Parser.Current_Index := 2;   --  so that Get_Argument returns nothing
    end Goto_Section;
 
    ----------------------------
@@ -667,27 +821,82 @@ package body GNAT.Command_Line is
 
    procedure Initialize_Option_Scan
      (Switch_Char              : Character := '-';
+      Stop_At_First_Non_Switch : Boolean   := False;
+      Section_Delimiters       : String    := "")
+   is
+   begin
+      Internal_Initialize_Option_Scan
+        (Parser                   => Command_Line_Parser,
+         Switch_Char              => Switch_Char,
+         Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
+         Section_Delimiters       => Section_Delimiters);
+   end Initialize_Option_Scan;
+
+   ----------------------------
+   -- Initialize_Option_Scan --
+   ----------------------------
+
+   procedure Initialize_Option_Scan
+     (Parser                   : out Opt_Parser;
+      Command_Line             : GNAT.OS_Lib.Argument_List_Access;
+      Switch_Char              : Character := '-';
       Stop_At_First_Non_Switch : Boolean := False;
       Section_Delimiters       : String := "")
    is
-      Section_Num     : Section_Number := 1;
-      Section_Index   : Integer        := Section_Delimiters'First;
+   begin
+      Free (Parser);
+
+      if Command_Line = null then
+         Parser := new Opt_Parser_Data (CL.Argument_Count);
+         Internal_Initialize_Option_Scan
+           (Parser                   => Parser,
+            Switch_Char              => Switch_Char,
+            Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
+            Section_Delimiters       => Section_Delimiters);
+      else
+         Parser := new Opt_Parser_Data (Command_Line'Length);
+         Parser.Arguments := Command_Line;
+         Internal_Initialize_Option_Scan
+           (Parser                   => Parser,
+            Switch_Char              => Switch_Char,
+            Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
+            Section_Delimiters       => Section_Delimiters);
+      end if;
+   end Initialize_Option_Scan;
+
+   -------------------------------------
+   -- Internal_Initialize_Option_Scan --
+   -------------------------------------
+
+   procedure Internal_Initialize_Option_Scan
+     (Parser                   : Opt_Parser;
+      Switch_Char              : Character;
+      Stop_At_First_Non_Switch : Boolean;
+      Section_Delimiters       : String)
+   is
+      Section_Num     : Section_Number;
+      Section_Index   : Integer;
       Last            : Integer;
       Delimiter_Found : Boolean;
 
+      Discard : Boolean;
+      pragma Warnings (Off, Discard);
+
    begin
-      Current_Argument := 0;
-      Current_Index := 0;
-      In_Expansion := False;
-      Switch_Character := Switch_Char;
-      Stop_At_First := Stop_At_First_Non_Switch;
+      Parser.Current_Argument := 0;
+      Parser.Current_Index    := 0;
+      Parser.In_Expansion     := False;
+      Parser.Switch_Character := Switch_Char;
+      Parser.Stop_At_First    := Stop_At_First_Non_Switch;
+      Parser.Section          := (others => 1);
 
       --  If we are using sections, we have to preprocess the command line
       --  to delimit them. A section can be repeated, so we just give each
       --  item on the command line a section number
 
+      Section_Num   := 1;
+      Section_Index := Section_Delimiters'First;
       while Section_Index <= Section_Delimiters'Last loop
-
          Last := Section_Index;
          while Last <= Section_Delimiters'Last
            and then Section_Delimiters (Last) /= ' '
@@ -698,21 +907,21 @@ package body GNAT.Command_Line is
          Delimiter_Found := False;
          Section_Num := Section_Num + 1;
 
-         for Index in 1 .. CL.Argument_Count loop
-            if CL.Argument (Index)(1) = Switch_Character
+         for Index in 1 .. Parser.Arg_Count loop
+            if Argument (Parser, Index)(1) = Parser.Switch_Character
               and then
-                CL.Argument (Index) = Switch_Character &
+                Argument (Parser, Index) = Parser.Switch_Character &
                                         Section_Delimiters
                                           (Section_Index .. Last - 1)
             then
-               Section (Index) := 0;
+               Parser.Section (Index) := 0;
                Delimiter_Found := True;
 
-            elsif Section (Index) = 0 then
+            elsif Parser.Section (Index) = 0 then
                Delimiter_Found := False;
 
             elsif Delimiter_Found then
-               Section (Index) := Section_Num;
+               Parser.Section (Index) := Section_Num;
             end if;
          end loop;
 
@@ -724,23 +933,36 @@ package body GNAT.Command_Line is
          end loop;
       end loop;
 
-      Delimiter_Found := Goto_Next_Argument_In_Section;
-   end Initialize_Option_Scan;
+      Discard := Goto_Next_Argument_In_Section (Parser);
+   end Internal_Initialize_Option_Scan;
 
    ---------------
    -- Parameter --
    ---------------
 
-   function Parameter return String is
+   function Parameter
+     (Parser : Opt_Parser := Command_Line_Parser) return String
+   is
    begin
-      if The_Parameter.First > The_Parameter.Last then
+      if Parser.The_Parameter.First > Parser.The_Parameter.Last then
          return String'(1 .. 0 => ' ');
       else
-         return CL.Argument (The_Parameter.Arg_Num)
-           (The_Parameter.First .. The_Parameter.Last);
+         return Argument (Parser, Parser.The_Parameter.Arg_Num)
+           (Parser.The_Parameter.First .. Parser.The_Parameter.Last);
       end if;
    end Parameter;
 
+   ---------------
+   -- Separator --
+   ---------------
+
+   function Separator
+     (Parser : Opt_Parser := Command_Line_Parser) return Character
+   is
+   begin
+      return Parser.The_Separator;
+   end Separator;
+
    -------------------
    -- Set_Parameter --
    -------------------
@@ -749,12 +971,14 @@ package body GNAT.Command_Line is
      (Variable : out Parameter_Type;
       Arg_Num  : Positive;
       First    : Positive;
-      Last     : Positive)
+      Last     : Positive;
+      Extra    : Character := ASCII.NUL)
    is
    begin
       Variable.Arg_Num := Arg_Num;
       Variable.First   := First;
       Variable.Last    := Last;
+      Variable.Extra   := Extra;
    end Set_Parameter;
 
    ---------------------
@@ -769,9 +993,9 @@ package body GNAT.Command_Line is
    is
       Directory_Separator : Character;
       pragma Import (C, Directory_Separator, "__gnat_dir_separator");
-      First : Positive := Pattern'First;
 
-      Pat : String := Pattern;
+      First : Positive := Pattern'First;
+      Pat   : String := Pattern;
 
    begin
       Canonical_Case_File_Name (Pat);
@@ -827,9 +1051,1414 @@ package body GNAT.Command_Line is
             exit when Iterator.Maximum_Depth = Max_Depth;
          end if;
       end loop;
-
    end Start_Expansion;
 
-begin
-   Section (CL.Argument_Count + 1) := 0;
+   ----------
+   -- Free --
+   ----------
+
+   procedure Free (Parser : in out Opt_Parser) is
+      procedure Unchecked_Free is new Ada.Unchecked_Deallocation
+        (Opt_Parser_Data, Opt_Parser);
+   begin
+      if Parser /= null
+        and then Parser /= Command_Line_Parser
+      then
+         Free (Parser.Arguments);
+         Unchecked_Free (Parser);
+      end if;
+   end Free;
+
+   ------------------
+   -- Define_Alias --
+   ------------------
+
+   procedure Define_Alias
+     (Config   : in out Command_Line_Configuration;
+      Switch   : String;
+      Expanded : String)
+   is
+   begin
+      if Config = null then
+         Config := new Command_Line_Configuration_Record;
+      end if;
+
+      Add (Config.Aliases,    new String'(Switch));
+      Add (Config.Expansions, new String'(Expanded));
+   end Define_Alias;
+
+   -------------------
+   -- Define_Prefix --
+   -------------------
+
+   procedure Define_Prefix
+     (Config : in out Command_Line_Configuration;
+      Prefix : String)
+   is
+   begin
+      if Config = null then
+         Config := new Command_Line_Configuration_Record;
+      end if;
+
+      Add (Config.Prefixes, new String'(Prefix));
+   end Define_Prefix;
+
+   -------------------
+   -- Define_Switch --
+   -------------------
+
+   procedure Define_Switch
+     (Config : in out Command_Line_Configuration;
+      Switch : String)
+   is
+   begin
+      if Config = null then
+         Config := new Command_Line_Configuration_Record;
+      end if;
+
+      Add (Config.Switches, new String'(Switch));
+   end Define_Switch;
+
+   --------------------
+   -- Define_Section --
+   --------------------
+
+   procedure Define_Section
+     (Config : in out Command_Line_Configuration;
+      Section : String)
+   is
+   begin
+      if Config = null then
+         Config := new Command_Line_Configuration_Record;
+      end if;
+
+      Add (Config.Sections, new String'(Section));
+   end Define_Section;
+
+   ------------------
+   -- Get_Switches --
+   ------------------
+
+   function Get_Switches
+     (Config      : Command_Line_Configuration;
+      Switch_Char : Character)
+      return String
+   is
+      Ret : Ada.Strings.Unbounded.Unbounded_String;
+      use type Ada.Strings.Unbounded.Unbounded_String;
+
+   begin
+      if Config = null or else Config.Switches = null then
+         return "";
+      end if;
+
+      for J in Config.Switches'Range loop
+         if Config.Switches (J) (Config.Switches (J)'First) = Switch_Char then
+            Ret :=
+              Ret & " " &
+                Config.Switches (J)
+                  (Config.Switches (J)'First + 1 .. Config.Switches (J)'Last);
+         else
+            Ret := Ret & " " & Config.Switches (J).all;
+         end if;
+      end loop;
+
+      return Ada.Strings.Unbounded.To_String (Ret);
+   end Get_Switches;
+
+   -----------------------
+   -- Set_Configuration --
+   -----------------------
+
+   procedure Set_Configuration
+     (Cmd    : in out Command_Line;
+      Config : Command_Line_Configuration)
+   is
+   begin
+      Cmd.Config := Config;
+   end Set_Configuration;
+
+   -----------------------
+   -- Get_Configuration --
+   -----------------------
+
+   function Get_Configuration
+     (Cmd : Command_Line) return Command_Line_Configuration is
+   begin
+      return Cmd.Config;
+   end Get_Configuration;
+
+   ----------------------
+   -- Set_Command_Line --
+   ----------------------
+
+   procedure Set_Command_Line
+     (Cmd                : in out Command_Line;
+      Switches           : String;
+      Getopt_Description : String := "";
+      Switch_Char        : Character := '-')
+   is
+      Tmp     : Argument_List_Access;
+      Parser  : Opt_Parser;
+      S       : Character;
+      Section : String_Access := null;
+
+      function Real_Full_Switch
+        (S      : Character;
+         Parser : Opt_Parser) return String;
+      --  Ensure that the returned switch value contains the
+      --  Switch_Char prefix if needed.
+
+      ----------------------
+      -- Real_Full_Switch --
+      ----------------------
+
+      function Real_Full_Switch
+        (S      : Character;
+         Parser : Opt_Parser) return String
+      is
+      begin
+         if S = '*' then
+            return Full_Switch (Parser);
+         else
+            return Switch_Char & Full_Switch (Parser);
+         end if;
+      end Real_Full_Switch;
+
+   --  Start of processing for Set_Command_Line
+
+   begin
+      Free (Cmd.Expanded);
+      Free (Cmd.Params);
+
+      if Switches /= "" then
+         Tmp := Argument_String_To_List (Switches);
+         Initialize_Option_Scan (Parser, Tmp, Switch_Char);
+
+         loop
+            begin
+               S := Getopt (Switches    => "* " & Getopt_Description,
+                            Concatenate => False,
+                            Parser      => Parser);
+               exit when S = ASCII.NUL;
+
+               declare
+                  Sw         : constant String :=
+                                 Real_Full_Switch (S, Parser);
+                  Is_Section : Boolean := False;
+
+               begin
+                  if Cmd.Config /= null
+                    and then Cmd.Config.Sections /= null
+                  then
+                     Section_Search :
+                     for S in Cmd.Config.Sections'Range loop
+                        if Sw = Cmd.Config.Sections (S).all then
+                           Section := Cmd.Config.Sections (S);
+                           Is_Section := True;
+
+                           exit Section_Search;
+                        end if;
+                     end loop Section_Search;
+                  end if;
+
+                  if not Is_Section then
+                     if Section = null then
+
+                        --  Work around some weird cases: some switches may
+                        --  expect parameters, but have the same value as
+                        --  longer switches: -gnaty3 (-gnaty, parameter=3) and
+                        --  -gnatya (-gnatya, no parameter).
+
+                        --  So we are calling add_switch here with parameter
+                        --  attached. This will be anyway correctly handled by
+                        --  Add_Switch if -gnaty3 is actually provided.
+
+                        if Separator (Parser) = ASCII.NUL then
+                           Add_Switch
+                             (Cmd, Sw & Parameter (Parser), "", ASCII.NUL);
+                        else
+                           Add_Switch
+                             (Cmd, Sw, Parameter (Parser), Separator (Parser));
+                        end if;
+                     else
+                        if Separator (Parser) = ASCII.NUL then
+                           Add_Switch
+                             (Cmd, Sw & Parameter (Parser), "",
+                              Separator (Parser),
+                              Section.all);
+                        else
+                           Add_Switch
+                             (Cmd, Sw,
+                              Parameter (Parser),
+                              Separator (Parser),
+                              Section.all);
+                        end if;
+                     end if;
+                  end if;
+               end;
+
+            exception
+               when Invalid_Parameter =>
+
+                  --  Add it with no parameter, if that's the way the user
+                  --  wants it.
+
+                  --  Specify the separator in all cases, as the switch might
+                  --  need to be unaliased, and the alias might contain
+                  --  switches with parameters.
+
+                  if Section = null then
+                     Add_Switch
+                       (Cmd, Switch_Char & Full_Switch (Parser),
+                        Separator => Separator (Parser));
+                  else
+                     Add_Switch
+                       (Cmd, Switch_Char & Full_Switch (Parser),
+                        Separator => Separator (Parser),
+                        Section   => Section.all);
+                  end if;
+            end;
+         end loop;
+
+         Free (Parser);
+      end if;
+   end Set_Command_Line;
+
+   ----------------
+   -- Looking_At --
+   ----------------
+
+   function Looking_At
+     (Type_Str  : String;
+      Index     : Natural;
+      Substring : String) return Boolean is
+   begin
+      return Index + Substring'Length - 1 <= Type_Str'Last
+        and then Type_Str (Index .. Index + Substring'Length - 1) = Substring;
+   end Looking_At;
+
+   ------------------------
+   -- Can_Have_Parameter --
+   ------------------------
+
+   function Can_Have_Parameter (S : String) return Boolean is
+   begin
+      if S'Length <= 1 then
+         return False;
+      end if;
+
+      case S (S'Last) is
+         when '!' | ':' | '?' | '=' =>
+            return True;
+         when others =>
+            return False;
+      end case;
+   end Can_Have_Parameter;
+
+   -----------------------
+   -- Require_Parameter --
+   -----------------------
+
+   function Require_Parameter (S : String) return Boolean is
+   begin
+      if S'Length <= 1 then
+         return False;
+      end if;
+
+      case S (S'Last) is
+         when '!' | ':' | '=' =>
+            return True;
+         when others =>
+            return False;
+      end case;
+   end Require_Parameter;
+
+   -------------------
+   -- Actual_Switch --
+   -------------------
+
+   function Actual_Switch (S : String) return String is
+   begin
+      if S'Length <= 1 then
+         return S;
+      end if;
+
+      case S (S'Last) is
+         when '!' | ':' | '?' | '=' =>
+            return S (S'First .. S'Last - 1);
+         when others =>
+            return S;
+      end case;
+   end Actual_Switch;
+
+   ----------------------------
+   -- For_Each_Simple_Switch --
+   ----------------------------
+
+   procedure For_Each_Simple_Switch
+     (Cmd       : Command_Line;
+      Switch    : String;
+      Parameter : String := "";
+      Unalias   : Boolean := True)
+   is
+      function Group_Analysis
+        (Prefix : String;
+         Group  : String) return Boolean;
+      --  Perform the analysis of a group of switches
+
+      --------------------
+      -- Group_Analysis --
+      --------------------
+
+      function Group_Analysis
+        (Prefix : String;
+         Group  : String) return Boolean
+      is
+         Idx   : Natural;
+         Found : Boolean;
+
+      begin
+         Idx := Group'First;
+         while Idx <= Group'Last loop
+            Found := False;
+
+            for S in Cmd.Config.Switches'Range loop
+               declare
+                  Sw              : constant String :=
+                                      Actual_Switch
+                                        (Cmd.Config.Switches (S).all);
+                  Full            : constant String :=
+                                      Prefix & Group (Idx .. Group'Last);
+                  Last            : Natural;
+                  Param           : Natural;
+
+               begin
+                  if Sw'Length >= Prefix'Length
+
+                     --  Verify that sw starts with Prefix
+
+                     and then Looking_At (Sw, Sw'First, Prefix)
+
+                     --  Verify that the group starts with sw
+
+                     and then Looking_At (Full, Full'First, Sw)
+                  then
+                     Last := Idx + Sw'Length - Prefix'Length - 1;
+                     Param := Last + 1;
+
+                     if Can_Have_Parameter (Cmd.Config.Switches (S).all) then
+
+                        --  Include potential parameter to the recursive call.
+                        --  Only numbers are allowed.
+
+                        while Last < Group'Last
+                          and then Group (Last + 1) in '0' .. '9'
+                        loop
+                           Last := Last + 1;
+                        end loop;
+                     end if;
+
+                     if not Require_Parameter (Cmd.Config.Switches (S).all)
+                       or else Last >= Param
+                     then
+                        if Idx = Group'First
+                          and then Last = Group'Last
+                          and then Last < Param
+                        then
+                           --  The group only concerns a single switch. Do not
+                           --  perform recursive call.
+
+                           --  Note that we still perform a recursive call if
+                           --  a parameter is detected in the switch, as this
+                           --  is a way to correctly identify such a parameter
+                           --  in aliases.
+
+                           return False;
+                        end if;
+
+                        Found := True;
+
+                        --  Recursive call, using the detected parameter if any
+
+                        if Last >= Param then
+                           For_Each_Simple_Switch
+                             (Cmd,
+                              Prefix & Group (Idx .. Param - 1),
+                              Group (Param .. Last));
+                        else
+                           For_Each_Simple_Switch
+                             (Cmd, Prefix & Group (Idx .. Last), "");
+                        end if;
+
+                        Idx := Last + 1;
+                        exit;
+                     end if;
+                  end if;
+               end;
+            end loop;
+
+            if not Found then
+               For_Each_Simple_Switch (Cmd, Prefix & Group (Idx), "");
+               Idx := Idx + 1;
+            end if;
+         end loop;
+
+         return True;
+      end Group_Analysis;
+
+   begin
+      --  First determine if the switch corresponds to one belonging to the
+      --  configuration. If so, run callback and exit.
+
+      if Cmd.Config /= null and then Cmd.Config.Switches /= null then
+         for S in Cmd.Config.Switches'Range loop
+            declare
+               Config_Switch : String renames Cmd.Config.Switches (S).all;
+            begin
+               if Actual_Switch (Config_Switch) = Switch
+                    and then
+                  ((Can_Have_Parameter (Config_Switch)
+                      and then Parameter /= "")
+                   or else
+                   (not Require_Parameter (Config_Switch)
+                       and then Parameter = ""))
+               then
+                  Callback (Switch, Parameter);
+                  return;
+               end if;
+            end;
+         end loop;
+      end if;
+
+      --  If adding a switch that can in fact be expanded through aliases,
+      --  add separately each of its expansions.
+
+      --  This takes care of expansions like "-T" -> "-gnatwrs", where the
+      --  alias and its expansion do not have the same prefix. Given the order
+      --  in which we do things here, the expansion of the alias will itself
+      --  be checked for a common prefix and split into simple switches.
+
+      if Unalias
+        and then Cmd.Config /= null
+        and then Cmd.Config.Aliases /= null
+      then
+         for A in Cmd.Config.Aliases'Range loop
+            if Cmd.Config.Aliases (A).all = Switch and then Parameter = "" then
+               For_Each_Simple_Switch
+                 (Cmd, Cmd.Config.Expansions (A).all, "");
+               return;
+            end if;
+         end loop;
+      end if;
+
+      --  If adding a switch grouping several switches, add each of the simple
+      --  switches instead.
+
+      if Cmd.Config /= null and then Cmd.Config.Prefixes /= null then
+         for P in Cmd.Config.Prefixes'Range loop
+            if Switch'Length > Cmd.Config.Prefixes (P)'Length + 1
+              and then Looking_At
+                (Switch, Switch'First, Cmd.Config.Prefixes (P).all)
+            then
+               --  Alias expansion will be done recursively
+
+               if Cmd.Config.Switches = null then
+                  for S in Switch'First + Cmd.Config.Prefixes (P)'Length
+                            .. Switch'Last
+                  loop
+                     For_Each_Simple_Switch
+                       (Cmd, Cmd.Config.Prefixes (P).all & Switch (S), "");
+                  end loop;
+
+                  return;
+
+               elsif Group_Analysis
+                 (Cmd.Config.Prefixes (P).all,
+                  Switch
+                    (Switch'First + Cmd.Config.Prefixes (P)'Length
+                      .. Switch'Last))
+               then
+                  --  Recursive calls already done on each switch of the group:
+                  --  Return without executing Callback.
+
+                  return;
+               end if;
+            end if;
+         end loop;
+      end if;
+
+      --  Test if added switch is a known switch with parameter attached
+
+      if Parameter = ""
+        and then Cmd.Config /= null
+        and then Cmd.Config.Switches /= null
+      then
+         for S in Cmd.Config.Switches'Range loop
+            declare
+               Sw    : constant String :=
+                         Actual_Switch (Cmd.Config.Switches (S).all);
+               Last  : Natural;
+               Param : Natural;
+
+            begin
+               --  Verify that switch starts with Sw
+               --  What if the "verification" fails???
+
+               if Switch'Length >= Sw'Length
+                 and then Looking_At (Switch, Switch'First, Sw)
+               then
+                  Param := Switch'First + Sw'Length - 1;
+                  Last := Param;
+
+                  if Can_Have_Parameter (Cmd.Config.Switches (S).all) then
+                     while Last < Switch'Last
+                       and then Switch (Last + 1) in '0' .. '9'
+                     loop
+                        Last := Last + 1;
+                     end loop;
+                  end if;
+
+                  --  If full Switch is a known switch with attached parameter
+                  --  then we use this parameter in the callback.
+
+                  if Last = Switch'Last then
+                     Callback
+                       (Switch (Switch'First .. Param),
+                        Switch (Param + 1 .. Last));
+                     return;
+
+                  end if;
+               end if;
+            end;
+         end loop;
+      end if;
+
+      Callback (Switch, Parameter);
+   end For_Each_Simple_Switch;
+
+   ----------------
+   -- Add_Switch --
+   ----------------
+
+   procedure Add_Switch
+     (Cmd        : in out Command_Line;
+      Switch     : String;
+      Parameter  : String    := "";
+      Separator  : Character := ' ';
+      Section    : String    := "";
+      Add_Before : Boolean   := False)
+   is
+      Success : Boolean;
+      pragma Unreferenced (Success);
+   begin
+      Add_Switch
+        (Cmd, Switch, Parameter, Separator, Section, Add_Before, Success);
+   end Add_Switch;
+
+   ----------------
+   -- Add_Switch --
+   ----------------
+
+   procedure Add_Switch
+     (Cmd        : in out Command_Line;
+      Switch     : String;
+      Parameter  : String := "";
+      Separator  : Character := ' ';
+      Section    : String := "";
+      Add_Before : Boolean := False;
+      Success    : out Boolean)
+   is
+      procedure Add_Simple_Switch (Simple : String; Param : String);
+      --  Add a new switch that has had all its aliases expanded, and switches
+      --  ungrouped. We know there are no more aliases in Switches.
+
+      -----------------------
+      -- Add_Simple_Switch --
+      -----------------------
+
+      procedure Add_Simple_Switch (Simple : String; Param : String) is
+      begin
+         if Cmd.Expanded = null then
+            Cmd.Expanded := new Argument_List'(1 .. 1 => new String'(Simple));
+
+            if Param /= "" then
+               Cmd.Params := new Argument_List'
+                 (1 .. 1 => new String'(Separator & Param));
+
+            else
+               Cmd.Params := new Argument_List'(1 .. 1 => null);
+            end if;
+
+            if Section = "" then
+               Cmd.Sections := new Argument_List'(1 .. 1 => null);
+
+            else
+               Cmd.Sections := new Argument_List'
+                 (1 .. 1 => new String'(Section));
+            end if;
+
+         else
+            --  Do we already have this switch?
+
+            for C in Cmd.Expanded'Range loop
+               if Cmd.Expanded (C).all = Simple
+                 and then
+                   ((Cmd.Params (C) = null and then Param = "")
+                     or else
+                       (Cmd.Params (C) /= null
+                         and then Cmd.Params (C).all = Separator & Param))
+                 and then
+                   ((Cmd.Sections (C) = null and then Section = "")
+                     or else
+                       (Cmd.Sections (C) /= null
+                         and then Cmd.Sections (C).all = Section))
+               then
+                  return;
+               end if;
+            end loop;
+
+            --  Inserting at least one switch
+
+            Success := True;
+            Add (Cmd.Expanded, new String'(Simple), Add_Before);
+
+            if Param /= "" then
+               Add
+                 (Cmd.Params,
+                  new String'(Separator & Param),
+                  Add_Before);
+
+            else
+               Add
+                 (Cmd.Params,
+                  null,
+                  Add_Before);
+            end if;
+
+            if Section = "" then
+               Add
+                 (Cmd.Sections,
+                  null,
+                  Add_Before);
+            else
+               Add
+                 (Cmd.Sections,
+                  new String'(Section),
+                  Add_Before);
+            end if;
+         end if;
+      end Add_Simple_Switch;
+
+      procedure Add_Simple_Switches is
+         new For_Each_Simple_Switch (Add_Simple_Switch);
+
+   --  Start of processing for Add_Switch
+
+   begin
+      Success := False;
+      Add_Simple_Switches (Cmd, Switch, Parameter);
+      Free (Cmd.Coalesce);
+   end Add_Switch;
+
+   ------------
+   -- Remove --
+   ------------
+
+   procedure Remove (Line : in out Argument_List_Access; Index : Integer) is
+      Tmp : Argument_List_Access := Line;
+
+   begin
+      Line := new Argument_List (Tmp'First .. Tmp'Last - 1);
+
+      if Index /= Tmp'First then
+         Line (Tmp'First .. Index - 1) := Tmp (Tmp'First .. Index - 1);
+      end if;
+
+      Free (Tmp (Index));
+
+      if Index /= Tmp'Last then
+         Line (Index .. Tmp'Last - 1) := Tmp (Index + 1 .. Tmp'Last);
+      end if;
+
+      Unchecked_Free (Tmp);
+   end Remove;
+
+   ---------
+   -- Add --
+   ---------
+
+   procedure Add
+     (Line   : in out Argument_List_Access;
+      Str    : String_Access;
+      Before : Boolean := False)
+   is
+      Tmp : Argument_List_Access := Line;
+
+   begin
+      if Tmp /= null then
+         Line := new Argument_List (Tmp'First .. Tmp'Last + 1);
+
+         if Before then
+            Line (Tmp'First)                     := Str;
+            Line (Tmp'First + 1 .. Tmp'Last + 1) := Tmp.all;
+         else
+            Line (Tmp'Range)    := Tmp.all;
+            Line (Tmp'Last + 1) := Str;
+         end if;
+
+         Unchecked_Free (Tmp);
+
+      else
+         Line := new Argument_List'(1 .. 1 => Str);
+      end if;
+   end Add;
+
+   -------------------
+   -- Remove_Switch --
+   -------------------
+
+   procedure Remove_Switch
+     (Cmd           : in out Command_Line;
+      Switch        : String;
+      Remove_All    : Boolean := False;
+      Has_Parameter : Boolean := False;
+      Section       : String := "")
+   is
+      Success : Boolean;
+      pragma Unreferenced (Success);
+   begin
+      Remove_Switch (Cmd, Switch, Remove_All, Has_Parameter, Section, Success);
+   end Remove_Switch;
+
+   -------------------
+   -- Remove_Switch --
+   -------------------
+
+   procedure Remove_Switch
+     (Cmd           : in out Command_Line;
+      Switch        : String;
+      Remove_All    : Boolean := False;
+      Has_Parameter : Boolean := False;
+      Section       : String  := "";
+      Success       : out Boolean)
+   is
+      procedure Remove_Simple_Switch (Simple : String; Param : String);
+      --  Removes a simple switch, with no aliasing or grouping
+
+      --------------------------
+      -- Remove_Simple_Switch --
+      --------------------------
+
+      procedure Remove_Simple_Switch (Simple : String; Param : String) is
+         C : Integer;
+         pragma Unreferenced (Param);
+
+      begin
+         if Cmd.Expanded /= null then
+            C := Cmd.Expanded'First;
+            while C <= Cmd.Expanded'Last loop
+               if Cmd.Expanded (C).all = Simple
+                 and then
+                   (Remove_All
+                     or else (Cmd.Sections (C) = null
+                               and then Section = "")
+                     or else (Cmd.Sections (C) /= null
+                               and then Section = Cmd.Sections (C).all))
+                 and then (not Has_Parameter or else Cmd.Params (C) /= null)
+               then
+                  Remove (Cmd.Expanded, C);
+                  Remove (Cmd.Params, C);
+                  Remove (Cmd.Sections, C);
+                  Success := True;
+
+                  if not Remove_All then
+                     return;
+                  end if;
+
+               else
+                  C := C + 1;
+               end if;
+            end loop;
+         end if;
+      end Remove_Simple_Switch;
+
+      procedure Remove_Simple_Switches is
+        new For_Each_Simple_Switch (Remove_Simple_Switch);
+
+   --  Start of processing for Remove_Switch
+
+   begin
+      Success := False;
+      Remove_Simple_Switches (Cmd, Switch, "", Unalias => not Has_Parameter);
+      Free (Cmd.Coalesce);
+   end Remove_Switch;
+
+   -------------------
+   -- Remove_Switch --
+   -------------------
+
+   procedure Remove_Switch
+     (Cmd       : in out Command_Line;
+      Switch    : String;
+      Parameter : String;
+      Section   : String  := "")
+   is
+      procedure Remove_Simple_Switch (Simple : String; Param : String);
+      --  Removes a simple switch, with no aliasing or grouping
+
+      --------------------------
+      -- Remove_Simple_Switch --
+      --------------------------
+
+      procedure Remove_Simple_Switch (Simple : String; Param : String) is
+         C : Integer;
+
+      begin
+         if Cmd.Expanded /= null then
+            C := Cmd.Expanded'First;
+            while C <= Cmd.Expanded'Last loop
+               if Cmd.Expanded (C).all = Simple
+                 and then
+                   ((Cmd.Sections (C) = null
+                      and then Section = "")
+                    or else
+                      (Cmd.Sections (C) /= null
+                        and then Section = Cmd.Sections (C).all))
+                 and then
+                   ((Cmd.Params (C) = null and then Param = "")
+                      or else
+                        (Cmd.Params (C) /= null
+                           and then
+
+                           --  Ignore the separator stored in Parameter
+
+                             Cmd.Params (C) (Cmd.Params (C)'First + 1
+                                             .. Cmd.Params (C)'Last) =
+                           Param))
+               then
+                  Remove (Cmd.Expanded, C);
+                  Remove (Cmd.Params, C);
+                  Remove (Cmd.Sections, C);
+
+                  --  The switch is necessarily unique by construction of
+                  --  Add_Switch.
+
+                  return;
+
+               else
+                  C := C + 1;
+               end if;
+            end loop;
+         end if;
+      end Remove_Simple_Switch;
+
+      procedure Remove_Simple_Switches is
+         new For_Each_Simple_Switch (Remove_Simple_Switch);
+
+   --  Start of processing for Remove_Switch
+
+   begin
+      Remove_Simple_Switches (Cmd, Switch, Parameter);
+      Free (Cmd.Coalesce);
+   end Remove_Switch;
+
+   --------------------
+   -- Group_Switches --
+   --------------------
+
+   procedure Group_Switches
+     (Cmd      : Command_Line;
+      Result   : Argument_List_Access;
+      Sections : Argument_List_Access;
+      Params   : Argument_List_Access)
+   is
+      function Compatible_Parameter (Param : String_Access) return Boolean;
+      --  True when the parameter can be part of a group
+
+      --------------------------
+      -- Compatible_Parameter --
+      --------------------------
+
+      function Compatible_Parameter (Param : String_Access) return Boolean is
+      begin
+         --  No parameter OK
+
+         if Param = null then
+            return True;
+
+         --  We need parameters without separators
+
+         elsif Param (Param'First) /= ASCII.NUL then
+            return False;
+
+         --  Parameters must be all digits
+
+         else
+            for J in Param'First + 1 .. Param'Last loop
+               if Param (J) not in '0' .. '9' then
+                  return False;
+               end if;
+            end loop;
+
+            return True;
+         end if;
+      end Compatible_Parameter;
+
+      --  Local declarations
+
+      Group : Ada.Strings.Unbounded.Unbounded_String;
+      First : Natural;
+      use type Ada.Strings.Unbounded.Unbounded_String;
+
+   --  Start of processing for Group_Switches
+
+   begin
+      if Cmd.Config = null
+        or else Cmd.Config.Prefixes = null
+      then
+         return;
+      end if;
+
+      for P in Cmd.Config.Prefixes'Range loop
+         Group   := Ada.Strings.Unbounded.Null_Unbounded_String;
+         First   := 0;
+
+         for C in Result'Range loop
+            if Result (C) /= null
+              and then Compatible_Parameter (Params (C))
+              and then Looking_At
+                (Result (C).all, Result (C)'First, Cmd.Config.Prefixes (P).all)
+            then
+               --  If we are still in the same section, group the switches
+
+               if First = 0
+                 or else
+                   (Sections (C) = null
+                     and then Sections (First) = null)
+                 or else
+                   (Sections (C) /= null
+                     and then Sections (First) /= null
+                     and then Sections (C).all = Sections (First).all)
+               then
+                  Group :=
+                    Group &
+                      Result (C)
+                        (Result (C)'First + Cmd.Config.Prefixes (P)'Length ..
+                         Result (C)'Last);
+
+                  if Params (C) /= null then
+                     Group :=
+                       Group &
+                         Params (C) (Params (C)'First + 1 .. Params (C)'Last);
+                     Free (Params (C));
+                  end if;
+
+                  if First = 0 then
+                     First := C;
+                  end if;
+
+                  Free (Result (C));
+
+               else
+                  --  We changed section: we put the grouped switches to the
+                  --  first place, on continue with the new section.
+
+                  Result (First) :=
+                    new String'
+                      (Cmd.Config.Prefixes (P).all &
+                       Ada.Strings.Unbounded.To_String (Group));
+                  Group :=
+                    Ada.Strings.Unbounded.To_Unbounded_String
+                      (Result (C)
+                       (Result (C)'First + Cmd.Config.Prefixes (P)'Length ..
+                            Result (C)'Last));
+                  First := C;
+               end if;
+            end if;
+         end loop;
+
+         if First > 0 then
+            Result (First) :=
+              new String'
+                (Cmd.Config.Prefixes (P).all &
+                 Ada.Strings.Unbounded.To_String (Group));
+         end if;
+      end loop;
+   end Group_Switches;
+
+   --------------------
+   -- Alias_Switches --
+   --------------------
+
+   procedure Alias_Switches
+     (Cmd    : Command_Line;
+      Result : Argument_List_Access;
+      Params : Argument_List_Access)
+   is
+      Found : Boolean;
+      First : Natural;
+
+      procedure Check_Cb (Switch : String; Param : String);
+      --  Comment required ???
+
+      procedure Remove_Cb (Switch : String; Param : String);
+      --  Comment required ???
+
+      --------------
+      -- Check_Cb --
+      --------------
+
+      procedure Check_Cb (Switch : String; Param : String) is
+      begin
+         if Found then
+            for E in Result'Range loop
+               if Result (E) /= null
+                 and then
+                   (Params (E) = null
+                    or else Params (E) (Params (E)'First + 1
+                                            .. Params (E)'Last) = Param)
+                 and then Result (E).all = Switch
+               then
+                  return;
+               end if;
+            end loop;
+
+            Found := False;
+         end if;
+      end Check_Cb;
+
+      ---------------
+      -- Remove_Cb --
+      ---------------
+
+      procedure Remove_Cb (Switch : String; Param : String) is
+      begin
+         for E in Result'Range loop
+            if Result (E) /= null
+                 and then
+                   (Params (E) = null
+                    or else Params (E) (Params (E)'First + 1
+                                            .. Params (E)'Last) = Param)
+              and then Result (E).all = Switch
+            then
+               if First > E then
+                  First := E;
+               end if;
+               Free (Result (E));
+               Free (Params (E));
+               return;
+            end if;
+         end loop;
+      end Remove_Cb;
+
+      procedure Check_All is new For_Each_Simple_Switch (Check_Cb);
+      procedure Remove_All is new For_Each_Simple_Switch (Remove_Cb);
+
+   --  Start of processing for Alias_Switches
+
+   begin
+      if Cmd.Config = null
+        or else Cmd.Config.Aliases = null
+      then
+         return;
+      end if;
+
+      for A in Cmd.Config.Aliases'Range loop
+
+         --  Compute the various simple switches that make up the alias. We
+         --  split the expansion into as many simple switches as possible, and
+         --  then check whether the expanded command line has all of them.
+
+         Found := True;
+         Check_All (Cmd, Cmd.Config.Expansions (A).all);
+
+         if Found then
+            First := Integer'Last;
+            Remove_All (Cmd, Cmd.Config.Expansions (A).all);
+            Result (First) := new String'(Cmd.Config.Aliases (A).all);
+         end if;
+      end loop;
+   end Alias_Switches;
+
+   -------------------
+   -- Sort_Sections --
+   -------------------
+
+   procedure Sort_Sections
+     (Line     : GNAT.OS_Lib.Argument_List_Access;
+      Sections : GNAT.OS_Lib.Argument_List_Access;
+      Params   : GNAT.OS_Lib.Argument_List_Access)
+   is
+      Sections_List : Argument_List_Access :=
+                        new Argument_List'(1 .. 1 => null);
+      Found         : Boolean;
+      Old_Line      : constant Argument_List := Line.all;
+      Old_Sections  : constant Argument_List := Sections.all;
+      Old_Params    : constant Argument_List := Params.all;
+      Index         : Natural;
+
+   begin
+      if Line = null then
+         return;
+      end if;
+
+      --  First construct a list of all sections
+
+      for E in Line'Range loop
+         if Sections (E) /= null then
+            Found := False;
+            for S in Sections_List'Range loop
+               if (Sections_List (S) = null and then Sections (E) = null)
+                 or else
+                   (Sections_List (S) /= null
+                     and then Sections (E) /= null
+                     and then Sections_List (S).all = Sections (E).all)
+               then
+                  Found := True;
+                  exit;
+               end if;
+            end loop;
+
+            if not Found then
+               Add (Sections_List, Sections (E));
+            end if;
+         end if;
+      end loop;
+
+      Index := Line'First;
+
+      for S in Sections_List'Range loop
+         for E in Old_Line'Range loop
+            if (Sections_List (S) = null and then Old_Sections (E) = null)
+              or else
+                (Sections_List (S) /= null
+                  and then Old_Sections (E) /= null
+                  and then Sections_List (S).all = Old_Sections (E).all)
+            then
+               Line (Index) := Old_Line (E);
+               Sections (Index) := Old_Sections (E);
+               Params (Index) := Old_Params (E);
+               Index := Index + 1;
+            end if;
+         end loop;
+      end loop;
+   end Sort_Sections;
+
+   -----------
+   -- Start --
+   -----------
+
+   procedure Start
+     (Cmd      : in out Command_Line;
+      Iter     : in out Command_Line_Iterator;
+      Expanded : Boolean)
+   is
+   begin
+      if Cmd.Expanded = null then
+         Iter.List := null;
+         return;
+      end if;
+
+      --  Reorder the expanded line so that sections are grouped
+
+      Sort_Sections (Cmd.Expanded, Cmd.Sections, Cmd.Params);
+
+      --  Coalesce the switches as much as possible
+
+      if not Expanded
+        and then Cmd.Coalesce = null
+      then
+         Cmd.Coalesce := new Argument_List (Cmd.Expanded'Range);
+         for E in Cmd.Expanded'Range loop
+            Cmd.Coalesce (E) := new String'(Cmd.Expanded (E).all);
+         end loop;
+
+         Cmd.Coalesce_Sections := new Argument_List (Cmd.Sections'Range);
+         for E in Cmd.Sections'Range loop
+            Cmd.Coalesce_Sections (E) :=
+              (if Cmd.Sections (E) = null then null
+               else new String'(Cmd.Sections (E).all));
+         end loop;
+
+         Cmd.Coalesce_Params := new Argument_List (Cmd.Params'Range);
+         for E in Cmd.Params'Range loop
+            Cmd.Coalesce_Params (E) :=
+              (if Cmd.Params (E) = null then null
+               else new String'(Cmd.Params (E).all));
+         end loop;
+
+         --  Not a clone, since we will not modify the parameters anyway
+
+         Alias_Switches (Cmd, Cmd.Coalesce, Cmd.Coalesce_Params);
+         Group_Switches
+           (Cmd, Cmd.Coalesce, Cmd.Coalesce_Sections, Cmd.Coalesce_Params);
+      end if;
+
+      if Expanded then
+         Iter.List     := Cmd.Expanded;
+         Iter.Params   := Cmd.Params;
+         Iter.Sections := Cmd.Sections;
+      else
+         Iter.List     := Cmd.Coalesce;
+         Iter.Params   := Cmd.Coalesce_Params;
+         Iter.Sections := Cmd.Coalesce_Sections;
+      end if;
+
+      if Iter.List = null then
+         Iter.Current := Integer'Last;
+      else
+         Iter.Current := Iter.List'First;
+
+         while Iter.Current <= Iter.List'Last
+           and then Iter.List (Iter.Current) = null
+         loop
+            Iter.Current := Iter.Current + 1;
+         end loop;
+      end if;
+   end Start;
+
+   --------------------
+   -- Current_Switch --
+   --------------------
+
+   function Current_Switch (Iter : Command_Line_Iterator) return String is
+   begin
+      return Iter.List (Iter.Current).all;
+   end Current_Switch;
+
+   --------------------
+   -- Is_New_Section --
+   --------------------
+
+   function Is_New_Section    (Iter : Command_Line_Iterator) return Boolean is
+      Section : constant String := Current_Section (Iter);
+   begin
+      if Iter.Sections = null then
+         return False;
+      elsif Iter.Current = Iter.Sections'First
+        or else Iter.Sections (Iter.Current - 1) = null
+      then
+         return Section /= "";
+      end if;
+
+      return Section /= Iter.Sections (Iter.Current - 1).all;
+   end Is_New_Section;
+
+   ---------------------
+   -- Current_Section --
+   ---------------------
+
+   function Current_Section (Iter : Command_Line_Iterator) return String is
+   begin
+      if Iter.Sections = null
+        or else Iter.Current > Iter.Sections'Last
+        or else Iter.Sections (Iter.Current) = null
+      then
+         return "";
+      end if;
+
+      return Iter.Sections (Iter.Current).all;
+   end Current_Section;
+
+   -----------------------
+   -- Current_Separator --
+   -----------------------
+
+   function Current_Separator (Iter : Command_Line_Iterator) return String is
+   begin
+      if Iter.Params = null
+        or else Iter.Current > Iter.Params'Last
+        or else Iter.Params (Iter.Current) = null
+      then
+         return "";
+
+      else
+         declare
+            Sep : constant Character :=
+              Iter.Params (Iter.Current) (Iter.Params (Iter.Current)'First);
+         begin
+            if Sep = ASCII.NUL then
+               return "";
+            else
+               return "" & Sep;
+            end if;
+         end;
+      end if;
+   end Current_Separator;
+
+   -----------------------
+   -- Current_Parameter --
+   -----------------------
+
+   function Current_Parameter (Iter : Command_Line_Iterator) return String is
+   begin
+      if Iter.Params = null
+        or else Iter.Current > Iter.Params'Last
+        or else Iter.Params (Iter.Current) = null
+      then
+         return "";
+
+      else
+         declare
+            P : constant String := Iter.Params (Iter.Current).all;
+
+         begin
+            --  Skip separator
+
+            return P (P'First + 1 .. P'Last);
+         end;
+      end if;
+   end Current_Parameter;
+
+   --------------
+   -- Has_More --
+   --------------
+
+   function Has_More (Iter : Command_Line_Iterator) return Boolean is
+   begin
+      return Iter.List /= null and then Iter.Current <= Iter.List'Last;
+   end Has_More;
+
+   ----------
+   -- Next --
+   ----------
+
+   procedure Next (Iter : in out Command_Line_Iterator) is
+   begin
+      Iter.Current := Iter.Current + 1;
+      while Iter.Current <= Iter.List'Last
+        and then Iter.List (Iter.Current) = null
+      loop
+         Iter.Current := Iter.Current + 1;
+      end loop;
+   end Next;
+
+   ----------
+   -- Free --
+   ----------
+
+   procedure Free (Config : in out Command_Line_Configuration) is
+   begin
+      if Config /= null then
+         Free (Config.Aliases);
+         Free (Config.Expansions);
+         Free (Config.Prefixes);
+         Free (Config.Sections);
+         Free (Config.Switches);
+         Unchecked_Free (Config);
+      end if;
+   end Free;
+
+   ----------
+   -- Free --
+   ----------
+
+   procedure Free (Cmd : in out Command_Line) is
+   begin
+      Free (Cmd.Expanded);
+      Free (Cmd.Coalesce);
+      Free (Cmd.Params);
+   end Free;
+
 end GNAT.Command_Line;