OSDN Git Service

2008-08-06 Jerome Lambourg <lambourg@adacore.com>
[pf3gnuchains/gcc-fork.git] / gcc / ada / g-comlin.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT COMPILER COMPONENTS                         --
4 --                                                                          --
5 --                    G N A T . C O M M A N D _ L I N E                     --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --          Copyright (C) 1999-2008, Free Software Foundation, Inc.         --
10 --                                                                          --
11 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
12 -- terms of the  GNU General Public License as published  by the Free Soft- --
13 -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
14 -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
17 -- for  more details.  You should have  received  a copy of the GNU General --
18 -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
19 -- to  the  Free Software Foundation,  51  Franklin  Street,  Fifth  Floor, --
20 -- Boston, MA 02110-1301, USA.                                              --
21 --                                                                          --
22 -- As a special exception,  if other files  instantiate  generics from this --
23 -- unit, or you link  this unit with other files  to produce an executable, --
24 -- this  unit  does not  by itself cause  the resulting  executable  to  be --
25 -- covered  by the  GNU  General  Public  License.  This exception does not --
26 -- however invalidate  any other reasons why  the executable file  might be --
27 -- covered by the  GNU Public License.                                      --
28 --                                                                          --
29 -- GNAT was originally developed  by the GNAT team at  New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
31 --                                                                          --
32 ------------------------------------------------------------------------------
33
34 with Ada.Unchecked_Deallocation;
35 with Ada.Strings.Unbounded;
36
37 with GNAT.OS_Lib; use GNAT.OS_Lib;
38
39 package body GNAT.Command_Line is
40
41    package CL renames Ada.Command_Line;
42
43    type Switch_Parameter_Type is
44      (Parameter_None,
45       Parameter_With_Optional_Space,  --  ':' in getopt
46       Parameter_With_Space_Or_Equal,  --  '=' in getopt
47       Parameter_No_Space,             --  '!' in getopt
48       Parameter_Optional);            --  '?' in getopt
49
50    procedure Set_Parameter
51      (Variable : out Parameter_Type;
52       Arg_Num  : Positive;
53       First    : Positive;
54       Last     : Positive;
55       Extra    : Character := ASCII.NUL);
56    pragma Inline (Set_Parameter);
57    --  Set the parameter that will be returned by Parameter below
58    --  Parameters need to be defined ???
59
60    function Goto_Next_Argument_In_Section (Parser : Opt_Parser) return Boolean;
61    --  Go to the next argument on the command line. If we are at the end of
62    --  the current section, we want to make sure there is no other identical
63    --  section on the command line (there might be multiple instances of
64    --  -largs). Returns True iff there is another argument.
65
66    function Get_File_Names_Case_Sensitive return Integer;
67    pragma Import (C, Get_File_Names_Case_Sensitive,
68                   "__gnat_get_file_names_case_sensitive");
69
70    File_Names_Case_Sensitive : constant Boolean :=
71                                  Get_File_Names_Case_Sensitive /= 0;
72
73    procedure Canonical_Case_File_Name (S : in out String);
74    --  Given a file name, converts it to canonical case form. For systems where
75    --  file names are case sensitive, this procedure has no effect. If file
76    --  names are not case sensitive (i.e. for example if you have the file
77    --  "xyz.adb", you can refer to it as XYZ.adb or XyZ.AdB), then this call
78    --  converts the given string to canonical all lower case form, so that two
79    --  file names compare equal if they refer to the same file.
80
81    procedure Internal_Initialize_Option_Scan
82      (Parser                   : Opt_Parser;
83       Switch_Char              : Character;
84       Stop_At_First_Non_Switch : Boolean;
85       Section_Delimiters       : String);
86    --  Initialize Parser, which must have been allocated already
87
88    function Argument (Parser : Opt_Parser; Index : Integer) return String;
89    --  Return the index-th command line argument
90
91    procedure Find_Longest_Matching_Switch
92      (Switches          : String;
93       Arg               : String;
94       Index_In_Switches : out Integer;
95       Switch_Length     : out Integer;
96       Param             : out Switch_Parameter_Type);
97    --  return the Longest switch from Switches that matches at least
98    --  partially Arg. Index_In_Switches is set to 0 if none matches
99
100    procedure Unchecked_Free is new Ada.Unchecked_Deallocation
101      (Argument_List, Argument_List_Access);
102
103    procedure Unchecked_Free is new Ada.Unchecked_Deallocation
104      (Command_Line_Configuration_Record, Command_Line_Configuration);
105
106    procedure Remove (Line : in out Argument_List_Access; Index : Integer);
107    --  Remove a specific element from Line
108
109    procedure Append
110      (Line : in out Argument_List_Access;
111       Str  : String_Access);
112    --  Append a new element to Line
113
114    function Can_Have_Parameter (S : String) return Boolean;
115    --  Tell if S can have a parameter.
116
117    function Require_Parameter (S : String) return Boolean;
118    --  Tell if S requires a paramter.
119
120    function Actual_Switch (S : String) return String;
121    --  Remove any possible trailing '!', ':', '?' and '='
122
123    generic
124       with procedure Callback (Simple_Switch : String; Parameter : String);
125    procedure For_Each_Simple_Switch
126      (Cmd       : Command_Line;
127       Switch    : String;
128       Parameter : String := "";
129       Unalias   : Boolean := True);
130    --  Breaks Switch into as simple switches as possible (expanding aliases and
131    --  ungrouping common prefixes when possible), and call Callback for each of
132    --  these.
133
134    procedure Sort_Sections
135      (Line     : GNAT.OS_Lib.Argument_List_Access;
136       Sections : GNAT.OS_Lib.Argument_List_Access;
137       Params   : GNAT.OS_Lib.Argument_List_Access);
138    --  Reorder the command line switches so that the switches belonging to a
139    --  section are grouped together.
140
141    procedure Group_Switches
142      (Cmd      : Command_Line;
143       Result   : Argument_List_Access;
144       Sections : Argument_List_Access;
145       Params   : Argument_List_Access);
146    --  Group switches with common prefixes whenever possible.
147    --  Once they have been grouped, we also check items for possible aliasing
148
149    procedure Alias_Switches
150      (Cmd    : Command_Line;
151       Result : Argument_List_Access;
152       Params : Argument_List_Access);
153    --  When possible, replace or more switches by an alias, i.e. a shorter
154    --  version.
155
156    function Looking_At
157      (Type_Str  : String;
158       Index     : Natural;
159       Substring : String) return Boolean;
160    --  Return True if the characters starting at Index in Type_Str are
161    --  equivalent to Substring.
162
163    --------------
164    -- Argument --
165    --------------
166
167    function Argument (Parser : Opt_Parser; Index : Integer) return String is
168    begin
169       if Parser.Arguments /= null then
170          return Parser.Arguments (Index + Parser.Arguments'First - 1).all;
171       else
172          return CL.Argument (Index);
173       end if;
174    end Argument;
175
176    ------------------------------
177    -- Canonical_Case_File_Name --
178    ------------------------------
179
180    procedure Canonical_Case_File_Name (S : in out String) is
181    begin
182       if not File_Names_Case_Sensitive then
183          for J in S'Range loop
184             if S (J) in 'A' .. 'Z' then
185                S (J) := Character'Val
186                          (Character'Pos (S (J)) +
187                           Character'Pos ('a')   -
188                           Character'Pos ('A'));
189             end if;
190          end loop;
191       end if;
192    end Canonical_Case_File_Name;
193
194    ---------------
195    -- Expansion --
196    ---------------
197
198    function Expansion (Iterator : Expansion_Iterator) return String is
199       use GNAT.Directory_Operations;
200       type Pointer is access all Expansion_Iterator;
201
202       It   : constant Pointer := Iterator'Unrestricted_Access;
203       S    : String (1 .. 1024);
204       Last : Natural;
205
206       Current : Depth := It.Current_Depth;
207       NL      : Positive;
208
209    begin
210       --  It is assumed that a directory is opened at the current level.
211       --  Otherwise GNAT.Directory_Operations.Directory_Error will be raised
212       --  at the first call to Read.
213
214       loop
215          Read (It.Levels (Current).Dir, S, Last);
216
217          --  If we have exhausted the directory, close it and go back one level
218
219          if Last = 0 then
220             Close (It.Levels (Current).Dir);
221
222             --  If we are at level 1, we are finished; return an empty string
223
224             if Current = 1 then
225                return String'(1 .. 0 => ' ');
226             else
227                --  Otherwise continue with the directory at the previous level
228
229                Current := Current - 1;
230                It.Current_Depth := Current;
231             end if;
232
233          --  If this is a directory, that is neither "." or "..", attempt to
234          --  go to the next level.
235
236          elsif Is_Directory
237            (It.Dir_Name (1 .. It.Levels (Current).Name_Last) & S (1 .. Last))
238            and then S (1 .. Last) /= "."
239            and then S (1 .. Last) /= ".."
240          then
241             --  We can go to the next level only if we have not reached the
242             --  maximum depth,
243
244             if Current < It.Maximum_Depth then
245                NL := It.Levels (Current).Name_Last;
246
247                --  And if relative path of this new directory is not too long
248
249                if NL + Last + 1 < Max_Path_Length then
250                   Current := Current + 1;
251                   It.Current_Depth := Current;
252                   It.Dir_Name (NL + 1 .. NL + Last) := S (1 .. Last);
253                   NL := NL + Last + 1;
254                   It.Dir_Name (NL) := Directory_Separator;
255                   It.Levels (Current).Name_Last := NL;
256                   Canonical_Case_File_Name (It.Dir_Name (1 .. NL));
257
258                   --  Open the new directory, and read from it
259
260                   GNAT.Directory_Operations.Open
261                     (It.Levels (Current).Dir, It.Dir_Name (1 .. NL));
262                end if;
263             end if;
264
265          --  If not a directory, check the relative path against the pattern
266
267          else
268             declare
269                Name : String :=
270                         It.Dir_Name (It.Start .. It.Levels (Current).Name_Last)
271                           & S (1 .. Last);
272             begin
273                Canonical_Case_File_Name (Name);
274
275                --  If it matches return the relative path
276
277                if GNAT.Regexp.Match (Name, Iterator.Regexp) then
278                   return Name;
279                end if;
280             end;
281          end if;
282       end loop;
283    end Expansion;
284
285    -----------------
286    -- Full_Switch --
287    -----------------
288
289    function Full_Switch
290      (Parser : Opt_Parser := Command_Line_Parser) return String
291    is
292    begin
293       if Parser.The_Switch.Extra = ASCII.NUL then
294          return Argument (Parser, Parser.The_Switch.Arg_Num)
295            (Parser.The_Switch.First .. Parser.The_Switch.Last);
296       else
297          return Parser.The_Switch.Extra
298            & Argument (Parser, Parser.The_Switch.Arg_Num)
299            (Parser.The_Switch.First .. Parser.The_Switch.Last);
300       end if;
301    end Full_Switch;
302
303    ------------------
304    -- Get_Argument --
305    ------------------
306
307    function Get_Argument
308      (Do_Expansion : Boolean    := False;
309       Parser       : Opt_Parser := Command_Line_Parser) return String
310    is
311    begin
312       if Parser.In_Expansion then
313          declare
314             S : constant String := Expansion (Parser.Expansion_It);
315          begin
316             if S'Length /= 0 then
317                return S;
318             else
319                Parser.In_Expansion := False;
320             end if;
321          end;
322       end if;
323
324       if Parser.Current_Argument > Parser.Arg_Count then
325
326          --  If this is the first time this function is called
327
328          if Parser.Current_Index = 1 then
329             Parser.Current_Argument := 1;
330             while Parser.Current_Argument <= Parser.Arg_Count
331               and then Parser.Section (Parser.Current_Argument) /=
332                 Parser.Current_Section
333             loop
334                Parser.Current_Argument := Parser.Current_Argument + 1;
335             end loop;
336          else
337             return String'(1 .. 0 => ' ');
338          end if;
339
340       elsif Parser.Section (Parser.Current_Argument) = 0 then
341          while Parser.Current_Argument <= Parser.Arg_Count
342            and then Parser.Section (Parser.Current_Argument) /=
343              Parser.Current_Section
344          loop
345             Parser.Current_Argument := Parser.Current_Argument + 1;
346          end loop;
347       end if;
348
349       Parser.Current_Index := Integer'Last;
350
351       while Parser.Current_Argument <= Parser.Arg_Count
352         and then Parser.Is_Switch (Parser.Current_Argument)
353       loop
354          Parser.Current_Argument := Parser.Current_Argument + 1;
355       end loop;
356
357       if Parser.Current_Argument > Parser.Arg_Count then
358          return String'(1 .. 0 => ' ');
359       elsif Parser.Section (Parser.Current_Argument) = 0 then
360          return Get_Argument (Do_Expansion);
361       end if;
362
363       Parser.Current_Argument := Parser.Current_Argument + 1;
364
365       --  Could it be a file name with wild cards to expand?
366
367       if Do_Expansion then
368          declare
369             Arg   : constant String :=
370                       Argument (Parser, Parser.Current_Argument - 1);
371             Index : Positive;
372
373          begin
374             Index := Arg'First;
375             while Index <= Arg'Last loop
376                if Arg (Index) = '*'
377                  or else Arg (Index) = '?'
378                  or else Arg (Index) = '['
379                then
380                   Parser.In_Expansion := True;
381                   Start_Expansion (Parser.Expansion_It, Arg);
382                   return Get_Argument (Do_Expansion);
383                end if;
384
385                Index := Index + 1;
386             end loop;
387          end;
388       end if;
389
390       return Argument (Parser, Parser.Current_Argument - 1);
391    end Get_Argument;
392
393    ----------------------------------
394    -- Find_Longest_Matching_Switch --
395    ----------------------------------
396
397    procedure Find_Longest_Matching_Switch
398      (Switches          : String;
399       Arg               : String;
400       Index_In_Switches : out Integer;
401       Switch_Length     : out Integer;
402       Param             : out Switch_Parameter_Type)
403    is
404       Index  : Natural;
405       Length : Natural := 1;
406       P      : Switch_Parameter_Type;
407
408    begin
409       Index_In_Switches := 0;
410       Switch_Length     := 0;
411
412       --  Remove all leading spaces first to make sure that Index points
413       --  at the start of the first switch.
414
415       Index := Switches'First;
416       while Index <= Switches'Last and then Switches (Index) = ' ' loop
417          Index := Index + 1;
418       end loop;
419
420       while Index <= Switches'Last loop
421
422          --  Search the length of the parameter at this position in Switches
423
424          Length := Index;
425          while Length <= Switches'Last
426            and then Switches (Length) /= ' '
427          loop
428             Length := Length + 1;
429          end loop;
430
431          if Length = Index + 1 then
432             P := Parameter_None;
433          else
434             case Switches (Length - 1) is
435                when ':'    =>
436                   P      := Parameter_With_Optional_Space;
437                   Length := Length - 1;
438                when '='    =>
439                   P      := Parameter_With_Space_Or_Equal;
440                   Length := Length - 1;
441                when '!'    =>
442                   P      := Parameter_No_Space;
443                   Length := Length - 1;
444                when '?'    =>
445                   P      := Parameter_Optional;
446                   Length := Length - 1;
447                when others =>
448                   P      := Parameter_None;
449             end case;
450          end if;
451
452          --  If it is the one we searched, it may be a candidate
453
454          if Arg'First + Length - 1 - Index <= Arg'Last
455            and then Switches (Index .. Length - 1) =
456                       Arg (Arg'First .. Arg'First + Length - 1 - Index)
457            and then Length - Index > Switch_Length
458          then
459             Param             := P;
460             Index_In_Switches := Index;
461             Switch_Length     := Length - Index;
462          end if;
463
464          --  Look for the next switch in Switches
465
466          while Index <= Switches'Last
467            and then Switches (Index) /= ' '
468          loop
469             Index := Index + 1;
470          end loop;
471
472          Index := Index + 1;
473       end loop;
474    end Find_Longest_Matching_Switch;
475
476    ------------
477    -- Getopt --
478    ------------
479
480    function Getopt
481      (Switches    : String;
482       Concatenate : Boolean := True;
483       Parser      : Opt_Parser := Command_Line_Parser) return Character
484    is
485       Dummy : Boolean;
486       pragma Unreferenced (Dummy);
487
488    begin
489       <<Restart>>
490
491       --  If we have finished parsing the current command line item (there
492       --  might be multiple switches in a single item), then go to the next
493       --  element
494
495       if Parser.Current_Argument > Parser.Arg_Count
496         or else (Parser.Current_Index >
497                    Argument (Parser, Parser.Current_Argument)'Last
498                  and then not Goto_Next_Argument_In_Section (Parser))
499       then
500          return ASCII.NUL;
501       end if;
502
503       --  By default, the switch will not have a parameter
504
505       Parser.The_Parameter :=
506         (Integer'Last, Integer'Last, Integer'Last - 1, ASCII.NUL);
507       Parser.The_Separator := ASCII.NUL;
508
509       declare
510          Arg            : constant String :=
511                             Argument (Parser, Parser.Current_Argument);
512          Index_Switches : Natural := 0;
513          Max_Length     : Natural := 0;
514          End_Index      : Natural;
515          Param          : Switch_Parameter_Type;
516       begin
517          --  If we are on a new item, test if this might be a switch
518
519          if Parser.Current_Index = Arg'First then
520             if Arg (Arg'First) /= Parser.Switch_Character then
521
522                --  If it isn't a switch, return it immediately. We also know it
523                --  isn't the parameter to a previous switch, since that has
524                --  already been handled
525
526                if Switches (Switches'First) = '*' then
527                   Set_Parameter
528                     (Parser.The_Switch,
529                      Arg_Num => Parser.Current_Argument,
530                      First   => Arg'First,
531                      Last    => Arg'Last);
532                   Parser.Is_Switch (Parser.Current_Argument) := True;
533                   Dummy := Goto_Next_Argument_In_Section (Parser);
534                   return '*';
535                end if;
536
537                if Parser.Stop_At_First then
538                   Parser.Current_Argument := Positive'Last;
539                   return ASCII.NUL;
540
541                elsif not Goto_Next_Argument_In_Section (Parser) then
542                   return ASCII.NUL;
543
544                else
545                   --  Recurse to get the next switch on the command line
546
547                   goto Restart;
548                end if;
549             end if;
550
551             --  We are on the first character of a new command line argument,
552             --  which starts with Switch_Character. Further analysis is needed.
553
554             Parser.Current_Index := Parser.Current_Index + 1;
555             Parser.Is_Switch (Parser.Current_Argument) := True;
556          end if;
557
558          Find_Longest_Matching_Switch
559            (Switches          => Switches,
560             Arg               => Arg (Parser.Current_Index .. Arg'Last),
561             Index_In_Switches => Index_Switches,
562             Switch_Length     => Max_Length,
563             Param             => Param);
564
565          --  If switch is not accepted, it is either invalid or is returned
566          --  in the context of '*'.
567
568          if Index_Switches = 0 then
569
570             --  Depending on the value of Concatenate, the full switch is
571             --  a single character or the rest of the argument.
572
573             if Concatenate then
574                End_Index := Parser.Current_Index;
575             else
576                End_Index := Arg'Last;
577             end if;
578
579             if Switches (Switches'First) = '*' then
580
581                --  Always prepend the switch character, so that users know that
582                --  this comes from a switch on the command line. This is
583                --  especially important when Concatenate is False, since
584                --  otherwise the current argument first character is lost.
585
586                Set_Parameter
587                  (Parser.The_Switch,
588                   Arg_Num => Parser.Current_Argument,
589                   First   => Parser.Current_Index,
590                   Last    => Arg'Last,
591                   Extra   => Parser.Switch_Character);
592                Parser.Is_Switch (Parser.Current_Argument) := True;
593                Dummy := Goto_Next_Argument_In_Section (Parser);
594                return '*';
595             end if;
596
597             Set_Parameter
598               (Parser.The_Switch,
599                Arg_Num => Parser.Current_Argument,
600                First   => Parser.Current_Index,
601                Last    => End_Index);
602             Parser.Current_Index := End_Index + 1;
603             raise Invalid_Switch;
604          end if;
605
606          End_Index := Parser.Current_Index + Max_Length - 1;
607          Set_Parameter
608            (Parser.The_Switch,
609             Arg_Num => Parser.Current_Argument,
610             First   => Parser.Current_Index,
611             Last    => End_Index);
612
613          case Param is
614             when Parameter_With_Optional_Space =>
615                if End_Index < Arg'Last then
616                   Set_Parameter
617                     (Parser.The_Parameter,
618                      Arg_Num => Parser.Current_Argument,
619                      First   => End_Index + 1,
620                      Last    => Arg'Last);
621                   Dummy := Goto_Next_Argument_In_Section (Parser);
622
623                elsif Parser.Current_Argument < Parser.Arg_Count
624                  and then Parser.Section (Parser.Current_Argument + 1) /= 0
625                then
626                   Parser.Current_Argument := Parser.Current_Argument + 1;
627                   Parser.The_Separator := ' ';
628                   Set_Parameter
629                     (Parser.The_Parameter,
630                      Arg_Num => Parser.Current_Argument,
631                      First => Argument (Parser, Parser.Current_Argument)'First,
632                      Last  => Argument (Parser, Parser.Current_Argument)'Last);
633                   Parser.Is_Switch (Parser.Current_Argument) := True;
634                   Dummy := Goto_Next_Argument_In_Section (Parser);
635
636                else
637                   Parser.Current_Index := End_Index + 1;
638                   raise Invalid_Parameter;
639                end if;
640
641             when Parameter_With_Space_Or_Equal =>
642
643                --  If the switch is of the form <switch>=xxx
644
645                if End_Index < Arg'Last then
646
647                   if Arg (End_Index + 1) = '='
648                     and then End_Index + 1 < Arg'Last
649                   then
650                      Parser.The_Separator := '=';
651                      Set_Parameter
652                        (Parser.The_Parameter,
653                         Arg_Num => Parser.Current_Argument,
654                         First   => End_Index + 2,
655                         Last    => Arg'Last);
656                      Dummy := Goto_Next_Argument_In_Section (Parser);
657                   else
658                      Parser.Current_Index := End_Index + 1;
659                      raise Invalid_Parameter;
660                   end if;
661
662                --  If the switch is of the form <switch> xxx
663
664                elsif Parser.Current_Argument < Parser.Arg_Count
665                  and then Parser.Section (Parser.Current_Argument + 1) /= 0
666                then
667                   Parser.Current_Argument := Parser.Current_Argument + 1;
668                   Parser.The_Separator := ' ';
669                   Set_Parameter
670                     (Parser.The_Parameter,
671                      Arg_Num => Parser.Current_Argument,
672                      First => Argument (Parser, Parser.Current_Argument)'First,
673                      Last  => Argument (Parser, Parser.Current_Argument)'Last);
674                   Parser.Is_Switch (Parser.Current_Argument) := True;
675                   Dummy := Goto_Next_Argument_In_Section (Parser);
676
677                else
678                   Parser.Current_Index := End_Index + 1;
679                   raise Invalid_Parameter;
680                end if;
681
682             when Parameter_No_Space =>
683
684                if End_Index < Arg'Last then
685                   Set_Parameter
686                     (Parser.The_Parameter,
687                      Arg_Num => Parser.Current_Argument,
688                      First   => End_Index + 1,
689                      Last    => Arg'Last);
690                   Dummy := Goto_Next_Argument_In_Section (Parser);
691
692                else
693                   Parser.Current_Index := End_Index + 1;
694                   raise Invalid_Parameter;
695                end if;
696
697             when Parameter_Optional =>
698
699                if End_Index < Arg'Last then
700                   Set_Parameter
701                     (Parser.The_Parameter,
702                      Arg_Num => Parser.Current_Argument,
703                      First   => End_Index + 1,
704                      Last    => Arg'Last);
705                end if;
706
707                Dummy := Goto_Next_Argument_In_Section (Parser);
708
709             when Parameter_None =>
710
711                if Concatenate or else End_Index = Arg'Last then
712                   Parser.Current_Index := End_Index + 1;
713
714                else
715                   --  If Concatenate is False and the full argument is not
716                   --  recognized as a switch, this is an invalid switch.
717
718                   if Switches (Switches'First) = '*' then
719                      Set_Parameter
720                        (Parser.The_Switch,
721                         Arg_Num => Parser.Current_Argument,
722                         First   => Arg'First,
723                         Last    => Arg'Last);
724                      Parser.Is_Switch (Parser.Current_Argument) := True;
725                      Dummy := Goto_Next_Argument_In_Section (Parser);
726                      return '*';
727                   end if;
728
729                   Set_Parameter
730                     (Parser.The_Switch,
731                      Arg_Num => Parser.Current_Argument,
732                      First   => Parser.Current_Index,
733                      Last    => Arg'Last);
734                   Parser.Current_Index := Arg'Last + 1;
735                   raise Invalid_Switch;
736                end if;
737          end case;
738
739          return Switches (Index_Switches);
740       end;
741    end Getopt;
742
743    -----------------------------------
744    -- Goto_Next_Argument_In_Section --
745    -----------------------------------
746
747    function Goto_Next_Argument_In_Section
748      (Parser : Opt_Parser) return Boolean
749    is
750    begin
751       Parser.Current_Argument := Parser.Current_Argument + 1;
752
753       if Parser.Current_Argument > Parser.Arg_Count
754         or else Parser.Section (Parser.Current_Argument) = 0
755       then
756          loop
757             Parser.Current_Argument := Parser.Current_Argument + 1;
758
759             if Parser.Current_Argument > Parser.Arg_Count then
760                Parser.Current_Index := 1;
761                return False;
762             end if;
763
764             exit when Parser.Section (Parser.Current_Argument) =
765                                                   Parser.Current_Section;
766          end loop;
767       end if;
768
769       Parser.Current_Index :=
770         Argument (Parser, Parser.Current_Argument)'First;
771
772       return True;
773    end Goto_Next_Argument_In_Section;
774
775    ------------------
776    -- Goto_Section --
777    ------------------
778
779    procedure Goto_Section
780      (Name   : String := "";
781       Parser : Opt_Parser := Command_Line_Parser)
782    is
783       Index : Integer;
784
785    begin
786       Parser.In_Expansion := False;
787
788       if Name = "" then
789          Parser.Current_Argument := 1;
790          Parser.Current_Index    := 1;
791          Parser.Current_Section  := 1;
792          return;
793       end if;
794
795       Index := 1;
796       while Index <= Parser.Arg_Count loop
797          if Parser.Section (Index) = 0
798            and then Argument (Parser, Index) = Parser.Switch_Character & Name
799          then
800             Parser.Current_Argument := Index + 1;
801             Parser.Current_Index    := 1;
802
803             if Parser.Current_Argument <= Parser.Arg_Count then
804                Parser.Current_Section :=
805                  Parser.Section (Parser.Current_Argument);
806             end if;
807             return;
808          end if;
809
810          Index := Index + 1;
811       end loop;
812
813       Parser.Current_Argument := Positive'Last;
814       Parser.Current_Index := 2;   --  so that Get_Argument returns nothing
815    end Goto_Section;
816
817    ----------------------------
818    -- Initialize_Option_Scan --
819    ----------------------------
820
821    procedure Initialize_Option_Scan
822      (Switch_Char              : Character := '-';
823       Stop_At_First_Non_Switch : Boolean   := False;
824       Section_Delimiters       : String    := "")
825    is
826    begin
827       Internal_Initialize_Option_Scan
828         (Parser                   => Command_Line_Parser,
829          Switch_Char              => Switch_Char,
830          Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
831          Section_Delimiters       => Section_Delimiters);
832    end Initialize_Option_Scan;
833
834    ----------------------------
835    -- Initialize_Option_Scan --
836    ----------------------------
837
838    procedure Initialize_Option_Scan
839      (Parser                   : out Opt_Parser;
840       Command_Line             : GNAT.OS_Lib.Argument_List_Access;
841       Switch_Char              : Character := '-';
842       Stop_At_First_Non_Switch : Boolean := False;
843       Section_Delimiters       : String := "")
844    is
845    begin
846       Free (Parser);
847
848       if Command_Line = null then
849          Parser := new Opt_Parser_Data (CL.Argument_Count);
850          Initialize_Option_Scan
851            (Switch_Char              => Switch_Char,
852             Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
853             Section_Delimiters       => Section_Delimiters);
854       else
855          Parser := new Opt_Parser_Data (Command_Line'Length);
856          Parser.Arguments := Command_Line;
857          Internal_Initialize_Option_Scan
858            (Parser                   => Parser,
859             Switch_Char              => Switch_Char,
860             Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
861             Section_Delimiters       => Section_Delimiters);
862       end if;
863    end Initialize_Option_Scan;
864
865    -------------------------------------
866    -- Internal_Initialize_Option_Scan --
867    -------------------------------------
868
869    procedure Internal_Initialize_Option_Scan
870      (Parser                   : Opt_Parser;
871       Switch_Char              : Character;
872       Stop_At_First_Non_Switch : Boolean;
873       Section_Delimiters       : String)
874    is
875       Section_Num     : Section_Number;
876       Section_Index   : Integer;
877       Last            : Integer;
878       Delimiter_Found : Boolean;
879
880       Discard : Boolean;
881       pragma Warnings (Off, Discard);
882
883    begin
884       Parser.Current_Argument := 0;
885       Parser.Current_Index    := 0;
886       Parser.In_Expansion     := False;
887       Parser.Switch_Character := Switch_Char;
888       Parser.Stop_At_First    := Stop_At_First_Non_Switch;
889
890       --  If we are using sections, we have to preprocess the command line
891       --  to delimit them. A section can be repeated, so we just give each
892       --  item on the command line a section number
893
894       Section_Num   := 1;
895       Section_Index := Section_Delimiters'First;
896       while Section_Index <= Section_Delimiters'Last loop
897          Last := Section_Index;
898          while Last <= Section_Delimiters'Last
899            and then Section_Delimiters (Last) /= ' '
900          loop
901             Last := Last + 1;
902          end loop;
903
904          Delimiter_Found := False;
905          Section_Num := Section_Num + 1;
906
907          for Index in 1 .. Parser.Arg_Count loop
908             if Argument (Parser, Index)(1) = Parser.Switch_Character
909               and then
910                 Argument (Parser, Index) = Parser.Switch_Character &
911                                         Section_Delimiters
912                                           (Section_Index .. Last - 1)
913             then
914                Parser.Section (Index) := 0;
915                Delimiter_Found := True;
916
917             elsif Parser.Section (Index) = 0 then
918                Delimiter_Found := False;
919
920             elsif Delimiter_Found then
921                Parser.Section (Index) := Section_Num;
922             end if;
923          end loop;
924
925          Section_Index := Last + 1;
926          while Section_Index <= Section_Delimiters'Last
927            and then Section_Delimiters (Section_Index) = ' '
928          loop
929             Section_Index := Section_Index + 1;
930          end loop;
931       end loop;
932
933       Discard := Goto_Next_Argument_In_Section (Parser);
934    end Internal_Initialize_Option_Scan;
935
936    ---------------
937    -- Parameter --
938    ---------------
939
940    function Parameter
941      (Parser : Opt_Parser := Command_Line_Parser) return String
942    is
943    begin
944       if Parser.The_Parameter.First > Parser.The_Parameter.Last then
945          return String'(1 .. 0 => ' ');
946       else
947          return Argument (Parser, Parser.The_Parameter.Arg_Num)
948            (Parser.The_Parameter.First .. Parser.The_Parameter.Last);
949       end if;
950    end Parameter;
951
952    ---------------
953    -- Separator --
954    ---------------
955
956    function Separator
957      (Parser : Opt_Parser := Command_Line_Parser) return Character
958    is
959    begin
960       return Parser.The_Separator;
961    end Separator;
962
963    -------------------
964    -- Set_Parameter --
965    -------------------
966
967    procedure Set_Parameter
968      (Variable : out Parameter_Type;
969       Arg_Num  : Positive;
970       First    : Positive;
971       Last     : Positive;
972       Extra    : Character := ASCII.NUL)
973    is
974    begin
975       Variable.Arg_Num := Arg_Num;
976       Variable.First   := First;
977       Variable.Last    := Last;
978       Variable.Extra   := Extra;
979    end Set_Parameter;
980
981    ---------------------
982    -- Start_Expansion --
983    ---------------------
984
985    procedure Start_Expansion
986      (Iterator     : out Expansion_Iterator;
987       Pattern      : String;
988       Directory    : String := "";
989       Basic_Regexp : Boolean := True)
990    is
991       Directory_Separator : Character;
992       pragma Import (C, Directory_Separator, "__gnat_dir_separator");
993
994       First : Positive := Pattern'First;
995       Pat   : String := Pattern;
996
997    begin
998       Canonical_Case_File_Name (Pat);
999       Iterator.Current_Depth := 1;
1000
1001       --  If Directory is unspecified, use the current directory ("./" or ".\")
1002
1003       if Directory = "" then
1004          Iterator.Dir_Name (1 .. 2) := "." & Directory_Separator;
1005          Iterator.Start := 3;
1006
1007       else
1008          Iterator.Dir_Name (1 .. Directory'Length) := Directory;
1009          Iterator.Start := Directory'Length + 1;
1010          Canonical_Case_File_Name (Iterator.Dir_Name (1 .. Directory'Length));
1011
1012          --  Make sure that the last character is a directory separator
1013
1014          if Directory (Directory'Last) /= Directory_Separator then
1015             Iterator.Dir_Name (Iterator.Start) := Directory_Separator;
1016             Iterator.Start := Iterator.Start + 1;
1017          end if;
1018       end if;
1019
1020       Iterator.Levels (1).Name_Last := Iterator.Start - 1;
1021
1022       --  Open the initial Directory, at depth 1
1023
1024       GNAT.Directory_Operations.Open
1025         (Iterator.Levels (1).Dir, Iterator.Dir_Name (1 .. Iterator.Start - 1));
1026
1027       --  If in the current directory and the pattern starts with "./" or ".\",
1028       --  drop the "./" or ".\" from the pattern.
1029
1030       if Directory = "" and then Pat'Length > 2
1031         and then Pat (Pat'First) = '.'
1032         and then Pat (Pat'First + 1) = Directory_Separator
1033       then
1034          First := Pat'First + 2;
1035       end if;
1036
1037       Iterator.Regexp :=
1038         GNAT.Regexp.Compile (Pat (First .. Pat'Last), Basic_Regexp, True);
1039
1040       Iterator.Maximum_Depth := 1;
1041
1042       --  Maximum_Depth is equal to 1 plus the number of directory separators
1043       --  in the pattern.
1044
1045       for Index in First .. Pat'Last loop
1046          if Pat (Index) = Directory_Separator then
1047             Iterator.Maximum_Depth := Iterator.Maximum_Depth + 1;
1048             exit when Iterator.Maximum_Depth = Max_Depth;
1049          end if;
1050       end loop;
1051    end Start_Expansion;
1052
1053    ----------
1054    -- Free --
1055    ----------
1056
1057    procedure Free (Parser : in out Opt_Parser) is
1058       procedure Unchecked_Free is new Ada.Unchecked_Deallocation
1059         (Opt_Parser_Data, Opt_Parser);
1060    begin
1061       if Parser /= null
1062         and then Parser /= Command_Line_Parser
1063       then
1064          Free (Parser.Arguments);
1065          Unchecked_Free (Parser);
1066       end if;
1067    end Free;
1068
1069    ------------------
1070    -- Define_Alias --
1071    ------------------
1072
1073    procedure Define_Alias
1074      (Config   : in out Command_Line_Configuration;
1075       Switch   : String;
1076       Expanded : String)
1077    is
1078    begin
1079       if Config = null then
1080          Config := new Command_Line_Configuration_Record;
1081       end if;
1082
1083       Append (Config.Aliases,    new String'(Switch));
1084       Append (Config.Expansions, new String'(Expanded));
1085    end Define_Alias;
1086
1087    -------------------
1088    -- Define_Prefix --
1089    -------------------
1090
1091    procedure Define_Prefix
1092      (Config : in out Command_Line_Configuration;
1093       Prefix : String)
1094    is
1095    begin
1096       if Config = null then
1097          Config := new Command_Line_Configuration_Record;
1098       end if;
1099
1100       Append (Config.Prefixes, new String'(Prefix));
1101    end Define_Prefix;
1102
1103    -------------------
1104    -- Define_Switch --
1105    -------------------
1106
1107    procedure Define_Switch
1108      (Config : in out Command_Line_Configuration;
1109       Switch : String)
1110    is
1111    begin
1112       if Config = null then
1113          Config := new Command_Line_Configuration_Record;
1114       end if;
1115
1116       Append (Config.Switches, new String'(Switch));
1117    end Define_Switch;
1118
1119    --------------------
1120    -- Define_Section --
1121    --------------------
1122
1123    procedure Define_Section
1124      (Config : in out Command_Line_Configuration;
1125       Section : String)
1126    is
1127    begin
1128       if Config = null then
1129          Config := new Command_Line_Configuration_Record;
1130       end if;
1131
1132       Append (Config.Sections, new String'(Section));
1133    end Define_Section;
1134
1135    ------------------
1136    -- Get_Switches --
1137    ------------------
1138
1139    function Get_Switches
1140      (Config      : Command_Line_Configuration;
1141       Switch_Char : Character)
1142       return String
1143    is
1144       Ret : Ada.Strings.Unbounded.Unbounded_String;
1145       use type Ada.Strings.Unbounded.Unbounded_String;
1146    begin
1147       if Config = null or else Config.Switches = null then
1148          return "";
1149       end if;
1150
1151       for J in Config.Switches'Range loop
1152          if Config.Switches (J) (Config.Switches (J)'First) = Switch_Char then
1153             Ret := Ret & " " &
1154               Config.Switches (J)
1155                 (Config.Switches (J)'First + 1 .. Config.Switches (J)'Last);
1156          else
1157             Ret := Ret & " " & Config.Switches (J).all;
1158          end if;
1159       end loop;
1160
1161       return Ada.Strings.Unbounded.To_String (Ret);
1162    end Get_Switches;
1163
1164    -----------------------
1165    -- Set_Configuration --
1166    -----------------------
1167
1168    procedure Set_Configuration
1169      (Cmd    : in out Command_Line;
1170       Config : Command_Line_Configuration)
1171    is
1172    begin
1173       Cmd.Config := Config;
1174    end Set_Configuration;
1175
1176    -----------------------
1177    -- Get_Configuration --
1178    -----------------------
1179
1180    function Get_Configuration
1181      (Cmd : Command_Line) return Command_Line_Configuration is
1182    begin
1183       return Cmd.Config;
1184    end Get_Configuration;
1185
1186    ----------------------
1187    -- Set_Command_Line --
1188    ----------------------
1189
1190    procedure Set_Command_Line
1191      (Cmd                : in out Command_Line;
1192       Switches           : String;
1193       Getopt_Description : String := "";
1194       Switch_Char        : Character := '-')
1195    is
1196       Tmp     : Argument_List_Access;
1197       Parser  : Opt_Parser;
1198       S       : Character;
1199       Section : String_Access := null;
1200
1201       function Real_Full_Switch
1202         (S      : Character;
1203          Parser : Opt_Parser) return String;
1204       --  Ensure that the returned switch value contains the
1205       --  Switch_Char prefix if needed.
1206
1207       ----------------------
1208       -- Real_Full_Switch --
1209       ----------------------
1210
1211       function Real_Full_Switch
1212         (S      : Character;
1213          Parser : Opt_Parser) return String
1214       is
1215       begin
1216          if S = '*' then
1217             return Full_Switch (Parser);
1218          else
1219             return Switch_Char & Full_Switch (Parser);
1220          end if;
1221       end Real_Full_Switch;
1222
1223    --  Start of processing for Set_Command_Line
1224
1225    begin
1226       Free (Cmd.Expanded);
1227       Free (Cmd.Params);
1228
1229       if Switches /= "" then
1230          Tmp := Argument_String_To_List (Switches);
1231          Initialize_Option_Scan (Parser, Tmp, Switch_Char);
1232
1233          loop
1234             begin
1235                S := Getopt (Switches    => "* " & Getopt_Description,
1236                             Concatenate => False,
1237                             Parser      => Parser);
1238                exit when S = ASCII.NUL;
1239
1240                declare
1241                   Sw         : constant String :=
1242                                  Real_Full_Switch (S, Parser);
1243                   Is_Section : Boolean := False;
1244
1245                begin
1246                   if Cmd.Config /= null
1247                     and then Cmd.Config.Sections /= null
1248                   then
1249                      Section_Search :
1250                      for S in Cmd.Config.Sections'Range loop
1251                         if Sw = Cmd.Config.Sections (S).all then
1252                            Section := Cmd.Config.Sections (S);
1253                            Is_Section := True;
1254
1255                            exit Section_Search;
1256                         end if;
1257                      end loop Section_Search;
1258                   end if;
1259
1260                   if not Is_Section then
1261                      if Section = null then
1262                         --  Workaround some weird cases: some switches may
1263                         --  expect parameters, but have the same value as
1264                         --  longer switches: -gnaty3 (-gnaty, parameter=3) and
1265                         --  -gnatya (-gnatya, no parameter).
1266                         --  So we are calling add_switch here with parameter
1267                         --  attached. This will be anyway correctly handled by
1268                         --  Add_Switch if -gnaty3 is actually furnished.
1269                         if Separator (Parser) = ASCII.NUL then
1270                            Add_Switch
1271                              (Cmd, Sw & Parameter (Parser), "");
1272                         else
1273                            Add_Switch
1274                              (Cmd, Sw, Parameter (Parser), Separator (Parser));
1275                         end if;
1276                      else
1277                         if Separator (Parser) = ASCII.NUL then
1278                            Add_Switch
1279                              (Cmd, Sw & Parameter (Parser), "",
1280                               Separator (Parser),
1281                               Section.all);
1282                         else
1283                            Add_Switch
1284                              (Cmd, Sw,
1285                               Parameter (Parser),
1286                               Separator (Parser),
1287                               Section.all);
1288                         end if;
1289                      end if;
1290                   end if;
1291                end;
1292
1293             exception
1294                when Invalid_Parameter =>
1295
1296                   --  Add it with no parameter, if that's the way the user
1297                   --  wants it.
1298
1299                   if Section = null then
1300                      Add_Switch
1301                        (Cmd, Switch_Char & Full_Switch (Parser));
1302                   else
1303                      Add_Switch
1304                        (Cmd, Switch_Char & Full_Switch (Parser), Section.all);
1305                   end if;
1306             end;
1307          end loop;
1308
1309          Free (Parser);
1310       end if;
1311    end Set_Command_Line;
1312
1313    ----------------
1314    -- Looking_At --
1315    ----------------
1316
1317    function Looking_At
1318      (Type_Str  : String;
1319       Index     : Natural;
1320       Substring : String) return Boolean is
1321    begin
1322       return Index + Substring'Length - 1 <= Type_Str'Last
1323         and then Type_Str (Index .. Index + Substring'Length - 1) = Substring;
1324    end Looking_At;
1325
1326    ------------------------
1327    -- Can_Have_Parameter --
1328    ------------------------
1329
1330    function Can_Have_Parameter (S : String) return Boolean is
1331    begin
1332       if S'Length <= 1 then
1333          return False;
1334       end if;
1335
1336       case S (S'Last) is
1337          when '!' | ':' | '?' | '=' =>
1338             return True;
1339          when others =>
1340             return False;
1341       end case;
1342    end Can_Have_Parameter;
1343
1344    -----------------------
1345    -- Require_Parameter --
1346    -----------------------
1347
1348    function Require_Parameter (S : String) return Boolean is
1349    begin
1350       if S'Length <= 1 then
1351          return False;
1352       end if;
1353
1354       case S (S'Last) is
1355          when '!' | ':' | '=' =>
1356             return True;
1357          when others =>
1358             return False;
1359       end case;
1360    end Require_Parameter;
1361
1362    -------------------
1363    -- Actual_Switch --
1364    -------------------
1365
1366    function Actual_Switch (S : String) return String is
1367    begin
1368       if S'Length <= 1 then
1369          return S;
1370       end if;
1371
1372       case S (S'Last) is
1373          when '!' | ':' | '?' | '=' =>
1374             return S (S'First .. S'Last - 1);
1375          when others =>
1376             return S;
1377       end case;
1378    end Actual_Switch;
1379
1380    ----------------------------
1381    -- For_Each_Simple_Switch --
1382    ----------------------------
1383
1384    procedure For_Each_Simple_Switch
1385      (Cmd       : Command_Line;
1386       Switch    : String;
1387       Parameter : String := "";
1388       Unalias   : Boolean := True)
1389    is
1390       function Group_Analysis
1391         (Prefix : String;
1392          Group  : String) return Boolean;
1393       --  Perform the analysis of a group of switches.
1394
1395       --------------------
1396       -- Group_Analysis --
1397       --------------------
1398
1399       function Group_Analysis
1400         (Prefix : String;
1401          Group  : String) return Boolean
1402       is
1403          Idx   : Natural := Group'First;
1404          Found : Boolean;
1405       begin
1406          while Idx <= Group'Last loop
1407             Found := False;
1408
1409             for S in Cmd.Config.Switches'Range loop
1410                declare
1411                   Sw              : constant String :=
1412                                       Actual_Switch
1413                                         (Cmd.Config.Switches (S).all);
1414                   Full            : constant String :=
1415                                       Prefix & Group (Idx .. Group'Last);
1416                   Last            : Natural;
1417                   Param           : Natural;
1418
1419                begin
1420                   if Sw'Length >= Prefix'Length
1421                   --  Verify that sw starts with Prefix
1422                     and then Looking_At (Sw, Sw'First, Prefix)
1423                   --  Verify that the group starts with sw
1424                     and then Looking_At (Full, Full'First, Sw)
1425                   then
1426                      Last := Idx + Sw'Length - Prefix'Length - 1;
1427                      Param := Last + 1;
1428
1429                      if Can_Have_Parameter (Cmd.Config.Switches (S).all) then
1430                         --  Include potential parameter to the recursive call.
1431                         --  Only numbers are allowed.
1432                         while Last < Group'Last
1433                           and then Group (Last + 1) in '0' .. '9'
1434                         loop
1435                            Last := Last + 1;
1436                         end loop;
1437                      end if;
1438
1439                      if not Require_Parameter (Cmd.Config.Switches (S).all)
1440                        or else Last >= Param
1441                      then
1442                         if Idx = Group'First and then Last = Group'Last then
1443                            --  The group only concerns a single switch. Do not
1444                            --  perform recursive call.
1445                            return False;
1446                         end if;
1447
1448                         Found := True;
1449
1450                         --  Recursive call, using the detected parameter if any
1451                         if Last >= Param then
1452                            For_Each_Simple_Switch
1453                              (Cmd,
1454                               Prefix & Group (Idx .. Param - 1),
1455                               Group (Param .. Last));
1456                         else
1457                            For_Each_Simple_Switch
1458                              (Cmd, Prefix & Group (Idx .. Last), "");
1459                         end if;
1460
1461                         Idx := Last + 1;
1462                         exit;
1463                      end if;
1464                   end if;
1465                end;
1466             end loop;
1467
1468             if not Found then
1469                For_Each_Simple_Switch (Cmd, Prefix & Group (Idx), "");
1470                Idx := Idx + 1;
1471             end if;
1472          end loop;
1473
1474          return True;
1475       end Group_Analysis;
1476
1477    begin
1478       --  Are we adding a switch that can in fact be expanded through aliases ?
1479       --  If yes, we add separately each of its expansion.
1480
1481       --  This takes care of expansions like "-T" -> "-gnatwrs", where the
1482       --  alias and its expansion do not have the same prefix. Given the order
1483       --  in which we do things here, the expansion of the alias will itself
1484       --  be checked for a common prefix and further split into simple switches
1485
1486       if Unalias
1487         and then Cmd.Config /= null
1488         and then Cmd.Config.Aliases /= null
1489       then
1490          for A in Cmd.Config.Aliases'Range loop
1491             if Cmd.Config.Aliases (A).all = Switch
1492               and then Parameter = ""
1493             then
1494                For_Each_Simple_Switch
1495                  (Cmd, Cmd.Config.Expansions (A).all, "");
1496                return;
1497             end if;
1498          end loop;
1499       end if;
1500
1501       --  Are we adding a switch grouping several switches ? If yes, add each
1502       --  of the simple switches instead.
1503
1504       if Cmd.Config /= null
1505         and then Cmd.Config.Prefixes /= null
1506       then
1507          for P in Cmd.Config.Prefixes'Range loop
1508             if Switch'Length > Cmd.Config.Prefixes (P)'Length + 1
1509               and then Looking_At
1510                 (Switch, Switch'First, Cmd.Config.Prefixes (P).all)
1511             then
1512                --  Alias expansion will be done recursively
1513                if Cmd.Config.Switches = null then
1514                   for S in Switch'First + Cmd.Config.Prefixes (P)'Length
1515                             .. Switch'Last
1516                   loop
1517                      For_Each_Simple_Switch
1518                        (Cmd, Cmd.Config.Prefixes (P).all & Switch (S), "");
1519                   end loop;
1520
1521                   return;
1522
1523                elsif Group_Analysis
1524                  (Cmd.Config.Prefixes (P).all,
1525                   Switch
1526                     (Switch'First + Cmd.Config.Prefixes (P)'Length
1527                       .. Switch'Last))
1528                then
1529                   --  Recursive calls already done on each switch of the
1530                   --  group. Let's return to not call Callback.
1531                   return;
1532                end if;
1533             end if;
1534          end loop;
1535       end if;
1536
1537       Callback (Switch, Parameter);
1538    end For_Each_Simple_Switch;
1539
1540    ----------------
1541    -- Add_Switch --
1542    ----------------
1543
1544    procedure Add_Switch
1545      (Cmd       : in out Command_Line;
1546       Switch    : String;
1547       Parameter : String := "";
1548       Separator : Character := ' ';
1549       Section   : String := "")
1550    is
1551       Success : Boolean;
1552       pragma Unreferenced (Success);
1553    begin
1554       Add_Switch (Cmd, Switch, Parameter, Separator, Section, Success);
1555    end Add_Switch;
1556
1557    ----------------
1558    -- Add_Switch --
1559    ----------------
1560
1561    procedure Add_Switch
1562      (Cmd       : in out Command_Line;
1563       Switch    : String;
1564       Parameter : String := "";
1565       Separator : Character := ' ';
1566       Section   : String := "";
1567       Success   : out Boolean)
1568    is
1569       procedure Add_Simple_Switch (Simple : String; Param : String);
1570       --  Add a new switch that has had all its aliases expanded, and switches
1571       --  ungrouped. We know there is no more aliases in Switches
1572
1573       -----------------------
1574       -- Add_Simple_Switch --
1575       -----------------------
1576
1577       procedure Add_Simple_Switch (Simple : String; Param : String) is
1578       begin
1579          if Cmd.Expanded = null then
1580             Cmd.Expanded := new Argument_List'(1 .. 1 => new String'(Simple));
1581
1582             if Param /= "" then
1583                Cmd.Params := new Argument_List'
1584                  (1 .. 1 => new String'(Separator & Param));
1585
1586             else
1587                Cmd.Params := new Argument_List'(1 .. 1 => null);
1588             end if;
1589
1590             if Section = "" then
1591                Cmd.Sections := new Argument_List'(1 .. 1 => null);
1592
1593             else
1594                Cmd.Sections := new Argument_List'
1595                  (1 .. 1 => new String'(Section));
1596             end if;
1597
1598          else
1599             --  Do we already have this switch ?
1600
1601             for C in Cmd.Expanded'Range loop
1602                if Cmd.Expanded (C).all = Simple
1603                  and then
1604                    ((Cmd.Params (C) = null and then Param = "")
1605                     or else
1606                       (Cmd.Params (C) /= null
1607                        and then Cmd.Params (C).all = Separator & Param))
1608                  and then
1609                    ((Cmd.Sections (C) = null and then Section = "")
1610                     or else
1611                       (Cmd.Sections (C) /= null
1612                        and then Cmd.Sections (C).all = Section))
1613                then
1614                   return;
1615                end if;
1616             end loop;
1617
1618             --  Inserting at least one switch
1619             Success := True;
1620             Append (Cmd.Expanded, new String'(Simple));
1621
1622             if Param /= "" then
1623                Append (Cmd.Params, new String'(Separator & Param));
1624
1625             else
1626                Append (Cmd.Params, null);
1627             end if;
1628
1629             if Section = "" then
1630                Append (Cmd.Sections, null);
1631             else
1632                Append (Cmd.Sections, new String'(Section));
1633             end if;
1634          end if;
1635       end Add_Simple_Switch;
1636
1637       procedure Add_Simple_Switches is
1638          new For_Each_Simple_Switch (Add_Simple_Switch);
1639
1640    --  Start of processing for Add_Switch
1641
1642    begin
1643       Success := False;
1644       Add_Simple_Switches (Cmd, Switch, Parameter);
1645       Free (Cmd.Coalesce);
1646    end Add_Switch;
1647
1648    ------------
1649    -- Remove --
1650    ------------
1651
1652    procedure Remove (Line : in out Argument_List_Access; Index : Integer) is
1653       Tmp : Argument_List_Access := Line;
1654
1655    begin
1656       Line := new Argument_List (Tmp'First .. Tmp'Last - 1);
1657
1658       if Index /= Tmp'First then
1659          Line (Tmp'First .. Index - 1) := Tmp (Tmp'First .. Index - 1);
1660       end if;
1661
1662       Free (Tmp (Index));
1663
1664       if Index /= Tmp'Last then
1665          Line (Index .. Tmp'Last - 1) := Tmp (Index + 1 .. Tmp'Last);
1666       end if;
1667
1668       Unchecked_Free (Tmp);
1669    end Remove;
1670
1671    ------------
1672    -- Append --
1673    ------------
1674
1675    procedure Append
1676      (Line : in out Argument_List_Access;
1677       Str  : String_Access)
1678    is
1679       Tmp : Argument_List_Access := Line;
1680    begin
1681       if Tmp /= null then
1682          Line := new Argument_List (Tmp'First .. Tmp'Last + 1);
1683          Line (Tmp'Range) := Tmp.all;
1684          Unchecked_Free (Tmp);
1685       else
1686          Line := new Argument_List (1 .. 1);
1687       end if;
1688
1689       Line (Line'Last) := Str;
1690    end Append;
1691
1692    -------------------
1693    -- Remove_Switch --
1694    -------------------
1695
1696    procedure Remove_Switch
1697      (Cmd           : in out Command_Line;
1698       Switch        : String;
1699       Remove_All    : Boolean := False;
1700       Has_Parameter : Boolean := False;
1701       Section       : String := "")
1702    is
1703       Success : Boolean;
1704       pragma Unreferenced (Success);
1705    begin
1706       Remove_Switch (Cmd, Switch, Remove_All, Has_Parameter, Section, Success);
1707    end Remove_Switch;
1708
1709    -------------------
1710    -- Remove_Switch --
1711    -------------------
1712
1713    procedure Remove_Switch
1714      (Cmd           : in out Command_Line;
1715       Switch        : String;
1716       Remove_All    : Boolean := False;
1717       Has_Parameter : Boolean := False;
1718       Section       : String  := "";
1719       Success       : out Boolean)
1720    is
1721       procedure Remove_Simple_Switch (Simple : String; Param : String);
1722       --  Removes a simple switch, with no aliasing or grouping
1723
1724       --------------------------
1725       -- Remove_Simple_Switch --
1726       --------------------------
1727
1728       procedure Remove_Simple_Switch (Simple : String; Param : String) is
1729          C : Integer;
1730          pragma Unreferenced (Param);
1731
1732       begin
1733          if Cmd.Expanded /= null then
1734             C := Cmd.Expanded'First;
1735             while C <= Cmd.Expanded'Last loop
1736                if Cmd.Expanded (C).all = Simple
1737                  and then
1738                    (Remove_All
1739                     or else (Cmd.Sections (C) = null
1740                              and then Section = "")
1741                     or else (Cmd.Sections (C) /= null
1742                              and then Section = Cmd.Sections (C).all))
1743                  and then (not Has_Parameter or else Cmd.Params (C) /= null)
1744                then
1745                   Remove (Cmd.Expanded, C);
1746                   Remove (Cmd.Params, C);
1747                   Remove (Cmd.Sections, C);
1748                   Success := True;
1749
1750                   if not Remove_All then
1751                      return;
1752                   end if;
1753
1754                else
1755                   C := C + 1;
1756                end if;
1757             end loop;
1758          end if;
1759       end Remove_Simple_Switch;
1760
1761       procedure Remove_Simple_Switches is
1762          new For_Each_Simple_Switch (Remove_Simple_Switch);
1763
1764    --  Start of processing for Remove_Switch
1765
1766    begin
1767       Success := False;
1768       Remove_Simple_Switches (Cmd, Switch, "", Unalias => not Has_Parameter);
1769       Free (Cmd.Coalesce);
1770    end Remove_Switch;
1771
1772    -------------------
1773    -- Remove_Switch --
1774    -------------------
1775
1776    procedure Remove_Switch
1777      (Cmd       : in out Command_Line;
1778       Switch    : String;
1779       Parameter : String;
1780       Section   : String  := "")
1781    is
1782       procedure Remove_Simple_Switch (Simple : String; Param : String);
1783       --  Removes a simple switch, with no aliasing or grouping
1784
1785       --------------------------
1786       -- Remove_Simple_Switch --
1787       --------------------------
1788
1789       procedure Remove_Simple_Switch (Simple : String; Param : String) is
1790          C : Integer;
1791
1792       begin
1793          if Cmd.Expanded /= null then
1794             C := Cmd.Expanded'First;
1795             while C <= Cmd.Expanded'Last loop
1796                if Cmd.Expanded (C).all = Simple
1797                  and then
1798                    ((Cmd.Sections (C) = null
1799                      and then Section = "")
1800                     or else
1801                       (Cmd.Sections (C) /= null
1802                        and then Section = Cmd.Sections (C).all))
1803                  and then
1804                    ((Cmd.Params (C) = null and then Param = "")
1805                       or else
1806                         (Cmd.Params (C) /= null
1807                            and then
1808
1809                            --  Ignore the separator stored in Parameter
1810
1811                              Cmd.Params (C) (Cmd.Params (C)'First + 1
1812                                              .. Cmd.Params (C)'Last) =
1813                            Param))
1814                then
1815                   Remove (Cmd.Expanded, C);
1816                   Remove (Cmd.Params, C);
1817                   Remove (Cmd.Sections, C);
1818
1819                   --  The switch is necessarily unique by construction of
1820                   --  Add_Switch
1821
1822                   return;
1823
1824                else
1825                   C := C + 1;
1826                end if;
1827             end loop;
1828          end if;
1829       end Remove_Simple_Switch;
1830
1831       procedure Remove_Simple_Switches is
1832          new For_Each_Simple_Switch (Remove_Simple_Switch);
1833
1834    --  Start of processing for Remove_Switch
1835
1836    begin
1837       Remove_Simple_Switches (Cmd, Switch, Parameter);
1838       Free (Cmd.Coalesce);
1839    end Remove_Switch;
1840
1841    --------------------
1842    -- Group_Switches --
1843    --------------------
1844
1845    procedure Group_Switches
1846      (Cmd      : Command_Line;
1847       Result   : Argument_List_Access;
1848       Sections : Argument_List_Access;
1849       Params   : Argument_List_Access)
1850    is
1851       function Compatible_Parameter (Param : String_Access) return Boolean;
1852       --  Tell if the parameter can be part of a group
1853
1854       --------------------------
1855       -- Compatible_Parameter --
1856       --------------------------
1857
1858       function Compatible_Parameter (Param : String_Access) return Boolean is
1859       begin
1860          if Param = null then
1861             --  No parameter, OK
1862             return True;
1863
1864          elsif Param (Param'First) /= ASCII.NUL then
1865             --  We need parameters without separators...
1866             return False;
1867
1868          else
1869             --  We need number only parameters.
1870             for J in Param'First + 1 .. Param'Last loop
1871                if Param (J) not in '0' .. '9' then
1872                   return False;
1873                end if;
1874             end loop;
1875
1876             return True;
1877          end if;
1878
1879       end Compatible_Parameter;
1880
1881       Group   : Ada.Strings.Unbounded.Unbounded_String;
1882       First   : Natural;
1883       use type Ada.Strings.Unbounded.Unbounded_String;
1884
1885    begin
1886       if Cmd.Config = null
1887         or else Cmd.Config.Prefixes = null
1888       then
1889          return;
1890       end if;
1891
1892       for P in Cmd.Config.Prefixes'Range loop
1893          Group   := Ada.Strings.Unbounded.Null_Unbounded_String;
1894          First   := 0;
1895
1896          for C in Result'Range loop
1897             if Result (C) /= null
1898               and then Compatible_Parameter (Params (C))
1899               and then Looking_At
1900                 (Result (C).all, Result (C)'First, Cmd.Config.Prefixes (P).all)
1901             then
1902                --  If we are still in the same section, group the switches
1903                if First = 0
1904                  or else
1905                    (Sections (C) = null
1906                     and then Sections (First) = null)
1907                  or else
1908                    (Sections (C) /= null
1909                     and then Sections (First) /= null
1910                     and then Sections (C).all = Sections (First).all)
1911                then
1912                   Group :=
1913                     Group &
1914                       Result (C)
1915                         (Result (C)'First + Cmd.Config.Prefixes (P)'Length ..
1916                            Result (C)'Last);
1917
1918                   if Params (C) /= null then
1919                      Group := Group &
1920                        Params (C) (Params (C)'First + 1 .. Params (C)'Last);
1921                      Free (Params (C));
1922                   end if;
1923
1924                   if First = 0 then
1925                      First := C;
1926                   end if;
1927
1928                   Free (Result (C));
1929                else
1930                   --  We changed section: we put the grouped switches to the
1931                   --  first place, on continue with the new section.
1932                   Result (First) :=
1933                     new String'
1934                       (Cmd.Config.Prefixes (P).all &
1935                        Ada.Strings.Unbounded.To_String (Group));
1936                   Group :=
1937                     Ada.Strings.Unbounded.To_Unbounded_String
1938                       (Result (C)
1939                        (Result (C)'First + Cmd.Config.Prefixes (P)'Length ..
1940                             Result (C)'Last));
1941                   First := C;
1942                end if;
1943             end if;
1944          end loop;
1945
1946          if First > 0 then
1947             Result (First) :=
1948               new String'
1949                 (Cmd.Config.Prefixes (P).all &
1950                  Ada.Strings.Unbounded.To_String (Group));
1951          end if;
1952       end loop;
1953    end Group_Switches;
1954
1955    --------------------
1956    -- Alias_Switches --
1957    --------------------
1958
1959    procedure Alias_Switches
1960      (Cmd    : Command_Line;
1961       Result : Argument_List_Access;
1962       Params : Argument_List_Access)
1963    is
1964       Found : Boolean;
1965       First : Natural;
1966
1967       procedure Check_Cb (Switch : String; Param : String);
1968       --  Comment required ???
1969
1970       procedure Remove_Cb (Switch : String; Param : String);
1971       --  Comment required ???
1972
1973       --------------
1974       -- Check_Cb --
1975       --------------
1976
1977       procedure Check_Cb (Switch : String; Param : String) is
1978       begin
1979          if Found then
1980             for E in Result'Range loop
1981                if Result (E) /= null
1982                  and then
1983                    (Params (E) = null
1984                     or else Params (E) (Params (E)'First + 1
1985                                             .. Params (E)'Last) = Param)
1986                  and then Result (E).all = Switch
1987                then
1988                   return;
1989                end if;
1990             end loop;
1991
1992             Found := False;
1993          end if;
1994       end Check_Cb;
1995
1996       ---------------
1997       -- Remove_Cb --
1998       ---------------
1999
2000       procedure Remove_Cb (Switch : String; Param : String) is
2001       begin
2002          for E in Result'Range loop
2003             if Result (E) /= null
2004                  and then
2005                    (Params (E) = null
2006                     or else Params (E) (Params (E)'First + 1
2007                                             .. Params (E)'Last) = Param)
2008               and then Result (E).all = Switch
2009             then
2010                if First > E then
2011                   First := E;
2012                end if;
2013                Free (Result (E));
2014                Free (Params (E));
2015                return;
2016             end if;
2017          end loop;
2018       end Remove_Cb;
2019
2020       procedure Check_All is new For_Each_Simple_Switch (Check_Cb);
2021       procedure Remove_All is new For_Each_Simple_Switch (Remove_Cb);
2022
2023    --  Start of processing for Alias_Switches
2024
2025    begin
2026       if Cmd.Config = null
2027         or else Cmd.Config.Aliases = null
2028       then
2029          return;
2030       end if;
2031
2032       for A in Cmd.Config.Aliases'Range loop
2033
2034          --  Compute the various simple switches that make up the alias. We
2035          --  split the expansion into as many simple switches as possible, and
2036          --  then check whether the expanded command line has all of them.
2037
2038          Found := True;
2039          Check_All (Cmd, Cmd.Config.Expansions (A).all);
2040
2041          if Found then
2042             First := Integer'Last;
2043             Remove_All (Cmd, Cmd.Config.Expansions (A).all);
2044             Result (First) := new String'(Cmd.Config.Aliases (A).all);
2045          end if;
2046       end loop;
2047    end Alias_Switches;
2048
2049    -------------------
2050    -- Sort_Sections --
2051    -------------------
2052
2053    procedure Sort_Sections
2054      (Line     : GNAT.OS_Lib.Argument_List_Access;
2055       Sections : GNAT.OS_Lib.Argument_List_Access;
2056       Params   : GNAT.OS_Lib.Argument_List_Access)
2057    is
2058       Sections_List : Argument_List_Access :=
2059                         new Argument_List'(1 .. 1 => null);
2060       Found         : Boolean;
2061       Old_Line      : constant Argument_List := Line.all;
2062       Old_Sections  : constant Argument_List := Sections.all;
2063       Old_Params    : constant Argument_List := Params.all;
2064       Index         : Natural;
2065
2066    begin
2067       if Line = null then
2068          return;
2069       end if;
2070
2071       --  First construct a list of all sections
2072
2073       for E in Line'Range loop
2074          if Sections (E) /= null then
2075             Found := False;
2076             for S in Sections_List'Range loop
2077                if (Sections_List (S) = null and then Sections (E) = null)
2078                  or else
2079                    (Sections_List (S) /= null
2080                      and then Sections (E) /= null
2081                      and then Sections_List (S).all = Sections (E).all)
2082                then
2083                   Found := True;
2084                   exit;
2085                end if;
2086             end loop;
2087
2088             if not Found then
2089                Append (Sections_List, Sections (E));
2090             end if;
2091          end if;
2092       end loop;
2093
2094       Index := Line'First;
2095
2096       for S in Sections_List'Range loop
2097          for E in Old_Line'Range loop
2098             if (Sections_List (S) = null and then Old_Sections (E) = null)
2099               or else
2100                 (Sections_List (S) /= null
2101                   and then Old_Sections (E) /= null
2102                   and then Sections_List (S).all = Old_Sections (E).all)
2103             then
2104                Line (Index) := Old_Line (E);
2105                Sections (Index) := Old_Sections (E);
2106                Params (Index) := Old_Params (E);
2107                Index := Index + 1;
2108             end if;
2109          end loop;
2110       end loop;
2111    end Sort_Sections;
2112
2113    -----------
2114    -- Start --
2115    -----------
2116
2117    procedure Start
2118      (Cmd      : in out Command_Line;
2119       Iter     : in out Command_Line_Iterator;
2120       Expanded : Boolean)
2121    is
2122    begin
2123       if Cmd.Expanded = null then
2124          Iter.List := null;
2125          return;
2126       end if;
2127
2128       --  Reorder the expanded line so that sections are grouped
2129
2130       Sort_Sections (Cmd.Expanded, Cmd.Sections, Cmd.Params);
2131
2132       --  Coalesce the switches as much as possible
2133
2134       if not Expanded
2135         and then Cmd.Coalesce = null
2136       then
2137          Cmd.Coalesce := new Argument_List (Cmd.Expanded'Range);
2138          for E in Cmd.Expanded'Range loop
2139             Cmd.Coalesce (E) := new String'(Cmd.Expanded (E).all);
2140          end loop;
2141
2142          Cmd.Coalesce_Sections := new Argument_List (Cmd.Sections'Range);
2143          for E in Cmd.Sections'Range loop
2144             if Cmd.Sections (E) = null then
2145                Cmd.Coalesce_Sections (E) := null;
2146             else
2147                Cmd.Coalesce_Sections (E) := new String'(Cmd.Sections (E).all);
2148             end if;
2149          end loop;
2150
2151          Cmd.Coalesce_Params := new Argument_List (Cmd.Params'Range);
2152          for E in Cmd.Params'Range loop
2153             if Cmd.Params (E) = null then
2154                Cmd.Coalesce_Params (E) := null;
2155             else
2156                Cmd.Coalesce_Params (E) := new String'(Cmd.Params (E).all);
2157             end if;
2158          end loop;
2159
2160          --  Not a clone, since we will not modify the parameters anyway
2161
2162          Alias_Switches (Cmd, Cmd.Coalesce, Cmd.Coalesce_Params);
2163          Group_Switches
2164            (Cmd, Cmd.Coalesce, Cmd.Coalesce_Sections, Cmd.Coalesce_Params);
2165       end if;
2166
2167       if Expanded then
2168          Iter.List     := Cmd.Expanded;
2169          Iter.Params   := Cmd.Params;
2170          Iter.Sections := Cmd.Sections;
2171       else
2172          Iter.List     := Cmd.Coalesce;
2173          Iter.Params   := Cmd.Coalesce_Params;
2174          Iter.Sections := Cmd.Coalesce_Sections;
2175       end if;
2176
2177       if Iter.List = null then
2178          Iter.Current := Integer'Last;
2179       else
2180          Iter.Current := Iter.List'First;
2181
2182          while Iter.Current <= Iter.List'Last
2183            and then Iter.List (Iter.Current) = null
2184          loop
2185             Iter.Current := Iter.Current + 1;
2186          end loop;
2187       end if;
2188    end Start;
2189
2190    --------------------
2191    -- Current_Switch --
2192    --------------------
2193
2194    function Current_Switch (Iter : Command_Line_Iterator) return String is
2195    begin
2196       return Iter.List (Iter.Current).all;
2197    end Current_Switch;
2198
2199    --------------------
2200    -- Is_New_Section --
2201    --------------------
2202
2203    function Is_New_Section    (Iter : Command_Line_Iterator) return Boolean is
2204       Section : constant String := Current_Section (Iter);
2205    begin
2206       if Iter.Sections = null then
2207          return False;
2208       elsif Iter.Current = Iter.Sections'First
2209         or else Iter.Sections (Iter.Current - 1) = null
2210       then
2211          return Section /= "";
2212       end if;
2213
2214       return Section /= Iter.Sections (Iter.Current - 1).all;
2215    end Is_New_Section;
2216
2217    ---------------------
2218    -- Current_Section --
2219    ---------------------
2220
2221    function Current_Section (Iter : Command_Line_Iterator) return String is
2222    begin
2223       if Iter.Sections = null
2224         or else Iter.Current > Iter.Sections'Last
2225         or else Iter.Sections (Iter.Current) = null
2226       then
2227          return "";
2228       end if;
2229
2230       return Iter.Sections (Iter.Current).all;
2231    end Current_Section;
2232
2233    -----------------------
2234    -- Current_Separator --
2235    -----------------------
2236
2237    function Current_Separator (Iter : Command_Line_Iterator) return String is
2238    begin
2239       if Iter.Params = null
2240         or else Iter.Current > Iter.Params'Last
2241         or else Iter.Params (Iter.Current) = null
2242       then
2243          return "";
2244
2245       else
2246          declare
2247             Sep : constant Character :=
2248               Iter.Params (Iter.Current) (Iter.Params (Iter.Current)'First);
2249          begin
2250             if Sep = ASCII.NUL then
2251                return "";
2252             else
2253                return "" & Sep;
2254             end if;
2255          end;
2256       end if;
2257    end Current_Separator;
2258
2259    -----------------------
2260    -- Current_Parameter --
2261    -----------------------
2262
2263    function Current_Parameter (Iter : Command_Line_Iterator) return String is
2264    begin
2265       if Iter.Params = null
2266         or else Iter.Current > Iter.Params'Last
2267         or else Iter.Params (Iter.Current) = null
2268       then
2269          return "";
2270
2271       else
2272          declare
2273             P : constant String := Iter.Params (Iter.Current).all;
2274
2275          begin
2276             --  Skip separator
2277
2278             return P (P'First + 1 .. P'Last);
2279          end;
2280       end if;
2281    end Current_Parameter;
2282
2283    --------------
2284    -- Has_More --
2285    --------------
2286
2287    function Has_More (Iter : Command_Line_Iterator) return Boolean is
2288    begin
2289       return Iter.List /= null and then Iter.Current <= Iter.List'Last;
2290    end Has_More;
2291
2292    ----------
2293    -- Next --
2294    ----------
2295
2296    procedure Next (Iter : in out Command_Line_Iterator) is
2297    begin
2298       Iter.Current := Iter.Current + 1;
2299       while Iter.Current <= Iter.List'Last
2300         and then Iter.List (Iter.Current) = null
2301       loop
2302          Iter.Current := Iter.Current + 1;
2303       end loop;
2304    end Next;
2305
2306    ----------
2307    -- Free --
2308    ----------
2309
2310    procedure Free (Config : in out Command_Line_Configuration) is
2311    begin
2312       if Config /= null then
2313          Free (Config.Aliases);
2314          Free (Config.Expansions);
2315          Free (Config.Prefixes);
2316          Unchecked_Free (Config);
2317       end if;
2318    end Free;
2319
2320    ----------
2321    -- Free --
2322    ----------
2323
2324    procedure Free (Cmd : in out Command_Line) is
2325    begin
2326       Free (Cmd.Expanded);
2327       Free (Cmd.Coalesce);
2328       Free (Cmd.Params);
2329    end Free;
2330
2331 end GNAT.Command_Line;