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                   --  Specify the separator in all cases, as the switch might
1299                   --  need to be unaliased, and the alias might contain
1300                   --  switches with parameters.
1301
1302                   if Section = null then
1303                      Add_Switch
1304                        (Cmd, Switch_Char & Full_Switch (Parser),
1305                         Separator => Separator (Parser));
1306                   else
1307                      Add_Switch
1308                        (Cmd, Switch_Char & Full_Switch (Parser),
1309                         Separator => Separator (Parser),
1310                         Section   => Section.all);
1311                   end if;
1312             end;
1313          end loop;
1314
1315          Free (Parser);
1316       end if;
1317    end Set_Command_Line;
1318
1319    ----------------
1320    -- Looking_At --
1321    ----------------
1322
1323    function Looking_At
1324      (Type_Str  : String;
1325       Index     : Natural;
1326       Substring : String) return Boolean is
1327    begin
1328       return Index + Substring'Length - 1 <= Type_Str'Last
1329         and then Type_Str (Index .. Index + Substring'Length - 1) = Substring;
1330    end Looking_At;
1331
1332    ------------------------
1333    -- Can_Have_Parameter --
1334    ------------------------
1335
1336    function Can_Have_Parameter (S : String) return Boolean is
1337    begin
1338       if S'Length <= 1 then
1339          return False;
1340       end if;
1341
1342       case S (S'Last) is
1343          when '!' | ':' | '?' | '=' =>
1344             return True;
1345          when others =>
1346             return False;
1347       end case;
1348    end Can_Have_Parameter;
1349
1350    -----------------------
1351    -- Require_Parameter --
1352    -----------------------
1353
1354    function Require_Parameter (S : String) return Boolean is
1355    begin
1356       if S'Length <= 1 then
1357          return False;
1358       end if;
1359
1360       case S (S'Last) is
1361          when '!' | ':' | '=' =>
1362             return True;
1363          when others =>
1364             return False;
1365       end case;
1366    end Require_Parameter;
1367
1368    -------------------
1369    -- Actual_Switch --
1370    -------------------
1371
1372    function Actual_Switch (S : String) return String is
1373    begin
1374       if S'Length <= 1 then
1375          return S;
1376       end if;
1377
1378       case S (S'Last) is
1379          when '!' | ':' | '?' | '=' =>
1380             return S (S'First .. S'Last - 1);
1381          when others =>
1382             return S;
1383       end case;
1384    end Actual_Switch;
1385
1386    ----------------------------
1387    -- For_Each_Simple_Switch --
1388    ----------------------------
1389
1390    procedure For_Each_Simple_Switch
1391      (Cmd       : Command_Line;
1392       Switch    : String;
1393       Parameter : String := "";
1394       Unalias   : Boolean := True)
1395    is
1396       function Group_Analysis
1397         (Prefix : String;
1398          Group  : String) return Boolean;
1399       --  Perform the analysis of a group of switches.
1400
1401       --------------------
1402       -- Group_Analysis --
1403       --------------------
1404
1405       function Group_Analysis
1406         (Prefix : String;
1407          Group  : String) return Boolean
1408       is
1409          Idx   : Natural := Group'First;
1410          Found : Boolean;
1411       begin
1412          while Idx <= Group'Last loop
1413             Found := False;
1414
1415             for S in Cmd.Config.Switches'Range loop
1416                declare
1417                   Sw              : constant String :=
1418                                       Actual_Switch
1419                                         (Cmd.Config.Switches (S).all);
1420                   Full            : constant String :=
1421                                       Prefix & Group (Idx .. Group'Last);
1422                   Last            : Natural;
1423                   Param           : Natural;
1424
1425                begin
1426                   if Sw'Length >= Prefix'Length
1427                   --  Verify that sw starts with Prefix
1428                     and then Looking_At (Sw, Sw'First, Prefix)
1429                   --  Verify that the group starts with sw
1430                     and then Looking_At (Full, Full'First, Sw)
1431                   then
1432                      Last := Idx + Sw'Length - Prefix'Length - 1;
1433                      Param := Last + 1;
1434
1435                      if Can_Have_Parameter (Cmd.Config.Switches (S).all) then
1436                         --  Include potential parameter to the recursive call.
1437                         --  Only numbers are allowed.
1438                         while Last < Group'Last
1439                           and then Group (Last + 1) in '0' .. '9'
1440                         loop
1441                            Last := Last + 1;
1442                         end loop;
1443                      end if;
1444
1445                      if not Require_Parameter (Cmd.Config.Switches (S).all)
1446                        or else Last >= Param
1447                      then
1448                         if Idx = Group'First
1449                           and then Last = Group'Last
1450                           and then Last < Param
1451                         then
1452                            --  The group only concerns a single switch. Do not
1453                            --  perform recursive call.
1454
1455                            --  Note that we still perform a recursive call if
1456                            --  a parameter is detected in the switch, as this
1457                            --  is a way to correctly identify such a parameter
1458                            --  in aliases.
1459                            return False;
1460                         end if;
1461
1462                         Found := True;
1463
1464                         --  Recursive call, using the detected parameter if any
1465                         if Last >= Param then
1466                            For_Each_Simple_Switch
1467                              (Cmd,
1468                               Prefix & Group (Idx .. Param - 1),
1469                               Group (Param .. Last));
1470                         else
1471                            For_Each_Simple_Switch
1472                              (Cmd, Prefix & Group (Idx .. Last), "");
1473                         end if;
1474
1475                         Idx := Last + 1;
1476                         exit;
1477                      end if;
1478                   end if;
1479                end;
1480             end loop;
1481
1482             if not Found then
1483                For_Each_Simple_Switch (Cmd, Prefix & Group (Idx), "");
1484                Idx := Idx + 1;
1485             end if;
1486          end loop;
1487
1488          return True;
1489       end Group_Analysis;
1490
1491    begin
1492       --  Are we adding a switch that can in fact be expanded through aliases ?
1493       --  If yes, we add separately each of its expansion.
1494
1495       --  This takes care of expansions like "-T" -> "-gnatwrs", where the
1496       --  alias and its expansion do not have the same prefix. Given the order
1497       --  in which we do things here, the expansion of the alias will itself
1498       --  be checked for a common prefix and further split into simple switches
1499
1500       if Unalias
1501         and then Cmd.Config /= null
1502         and then Cmd.Config.Aliases /= null
1503       then
1504          for A in Cmd.Config.Aliases'Range loop
1505             if Cmd.Config.Aliases (A).all = Switch
1506               and then Parameter = ""
1507             then
1508                For_Each_Simple_Switch
1509                  (Cmd, Cmd.Config.Expansions (A).all, "");
1510                return;
1511             end if;
1512          end loop;
1513       end if;
1514
1515       --  Are we adding a switch grouping several switches ? If yes, add each
1516       --  of the simple switches instead.
1517
1518       if Cmd.Config /= null
1519         and then Cmd.Config.Prefixes /= null
1520       then
1521          for P in Cmd.Config.Prefixes'Range loop
1522             if Switch'Length > Cmd.Config.Prefixes (P)'Length + 1
1523               and then Looking_At
1524                 (Switch, Switch'First, Cmd.Config.Prefixes (P).all)
1525             then
1526                --  Alias expansion will be done recursively
1527                if Cmd.Config.Switches = null then
1528                   for S in Switch'First + Cmd.Config.Prefixes (P)'Length
1529                             .. Switch'Last
1530                   loop
1531                      For_Each_Simple_Switch
1532                        (Cmd, Cmd.Config.Prefixes (P).all & Switch (S), "");
1533                   end loop;
1534
1535                   return;
1536
1537                elsif Group_Analysis
1538                  (Cmd.Config.Prefixes (P).all,
1539                   Switch
1540                     (Switch'First + Cmd.Config.Prefixes (P)'Length
1541                       .. Switch'Last))
1542                then
1543                   --  Recursive calls already done on each switch of the
1544                   --  group. Let's return to not call Callback.
1545                   return;
1546                end if;
1547             end if;
1548          end loop;
1549       end if;
1550
1551       Callback (Switch, Parameter);
1552    end For_Each_Simple_Switch;
1553
1554    ----------------
1555    -- Add_Switch --
1556    ----------------
1557
1558    procedure Add_Switch
1559      (Cmd       : in out Command_Line;
1560       Switch    : String;
1561       Parameter : String := "";
1562       Separator : Character := ' ';
1563       Section   : String := "")
1564    is
1565       Success : Boolean;
1566       pragma Unreferenced (Success);
1567    begin
1568       Add_Switch (Cmd, Switch, Parameter, Separator, Section, Success);
1569    end Add_Switch;
1570
1571    ----------------
1572    -- Add_Switch --
1573    ----------------
1574
1575    procedure Add_Switch
1576      (Cmd       : in out Command_Line;
1577       Switch    : String;
1578       Parameter : String := "";
1579       Separator : Character := ' ';
1580       Section   : String := "";
1581       Success   : out Boolean)
1582    is
1583       procedure Add_Simple_Switch (Simple : String; Param : String);
1584       --  Add a new switch that has had all its aliases expanded, and switches
1585       --  ungrouped. We know there is no more aliases in Switches
1586
1587       -----------------------
1588       -- Add_Simple_Switch --
1589       -----------------------
1590
1591       procedure Add_Simple_Switch (Simple : String; Param : String) is
1592       begin
1593          if Cmd.Expanded = null then
1594             Cmd.Expanded := new Argument_List'(1 .. 1 => new String'(Simple));
1595
1596             if Param /= "" then
1597                Cmd.Params := new Argument_List'
1598                  (1 .. 1 => new String'(Separator & Param));
1599
1600             else
1601                Cmd.Params := new Argument_List'(1 .. 1 => null);
1602             end if;
1603
1604             if Section = "" then
1605                Cmd.Sections := new Argument_List'(1 .. 1 => null);
1606
1607             else
1608                Cmd.Sections := new Argument_List'
1609                  (1 .. 1 => new String'(Section));
1610             end if;
1611
1612          else
1613             --  Do we already have this switch ?
1614
1615             for C in Cmd.Expanded'Range loop
1616                if Cmd.Expanded (C).all = Simple
1617                  and then
1618                    ((Cmd.Params (C) = null and then Param = "")
1619                     or else
1620                       (Cmd.Params (C) /= null
1621                        and then Cmd.Params (C).all = Separator & Param))
1622                  and then
1623                    ((Cmd.Sections (C) = null and then Section = "")
1624                     or else
1625                       (Cmd.Sections (C) /= null
1626                        and then Cmd.Sections (C).all = Section))
1627                then
1628                   return;
1629                end if;
1630             end loop;
1631
1632             --  Inserting at least one switch
1633             Success := True;
1634             Append (Cmd.Expanded, new String'(Simple));
1635
1636             if Param /= "" then
1637                Append (Cmd.Params, new String'(Separator & Param));
1638
1639             else
1640                Append (Cmd.Params, null);
1641             end if;
1642
1643             if Section = "" then
1644                Append (Cmd.Sections, null);
1645             else
1646                Append (Cmd.Sections, new String'(Section));
1647             end if;
1648          end if;
1649       end Add_Simple_Switch;
1650
1651       procedure Add_Simple_Switches is
1652          new For_Each_Simple_Switch (Add_Simple_Switch);
1653
1654    --  Start of processing for Add_Switch
1655
1656    begin
1657       Success := False;
1658       Add_Simple_Switches (Cmd, Switch, Parameter);
1659       Free (Cmd.Coalesce);
1660    end Add_Switch;
1661
1662    ------------
1663    -- Remove --
1664    ------------
1665
1666    procedure Remove (Line : in out Argument_List_Access; Index : Integer) is
1667       Tmp : Argument_List_Access := Line;
1668
1669    begin
1670       Line := new Argument_List (Tmp'First .. Tmp'Last - 1);
1671
1672       if Index /= Tmp'First then
1673          Line (Tmp'First .. Index - 1) := Tmp (Tmp'First .. Index - 1);
1674       end if;
1675
1676       Free (Tmp (Index));
1677
1678       if Index /= Tmp'Last then
1679          Line (Index .. Tmp'Last - 1) := Tmp (Index + 1 .. Tmp'Last);
1680       end if;
1681
1682       Unchecked_Free (Tmp);
1683    end Remove;
1684
1685    ------------
1686    -- Append --
1687    ------------
1688
1689    procedure Append
1690      (Line : in out Argument_List_Access;
1691       Str  : String_Access)
1692    is
1693       Tmp : Argument_List_Access := Line;
1694    begin
1695       if Tmp /= null then
1696          Line := new Argument_List (Tmp'First .. Tmp'Last + 1);
1697          Line (Tmp'Range) := Tmp.all;
1698          Unchecked_Free (Tmp);
1699       else
1700          Line := new Argument_List (1 .. 1);
1701       end if;
1702
1703       Line (Line'Last) := Str;
1704    end Append;
1705
1706    -------------------
1707    -- Remove_Switch --
1708    -------------------
1709
1710    procedure Remove_Switch
1711      (Cmd           : in out Command_Line;
1712       Switch        : String;
1713       Remove_All    : Boolean := False;
1714       Has_Parameter : Boolean := False;
1715       Section       : String := "")
1716    is
1717       Success : Boolean;
1718       pragma Unreferenced (Success);
1719    begin
1720       Remove_Switch (Cmd, Switch, Remove_All, Has_Parameter, Section, Success);
1721    end Remove_Switch;
1722
1723    -------------------
1724    -- Remove_Switch --
1725    -------------------
1726
1727    procedure Remove_Switch
1728      (Cmd           : in out Command_Line;
1729       Switch        : String;
1730       Remove_All    : Boolean := False;
1731       Has_Parameter : Boolean := False;
1732       Section       : String  := "";
1733       Success       : out Boolean)
1734    is
1735       procedure Remove_Simple_Switch (Simple : String; Param : String);
1736       --  Removes a simple switch, with no aliasing or grouping
1737
1738       --------------------------
1739       -- Remove_Simple_Switch --
1740       --------------------------
1741
1742       procedure Remove_Simple_Switch (Simple : String; Param : String) is
1743          C : Integer;
1744          pragma Unreferenced (Param);
1745
1746       begin
1747          if Cmd.Expanded /= null then
1748             C := Cmd.Expanded'First;
1749             while C <= Cmd.Expanded'Last loop
1750                if Cmd.Expanded (C).all = Simple
1751                  and then
1752                    (Remove_All
1753                     or else (Cmd.Sections (C) = null
1754                              and then Section = "")
1755                     or else (Cmd.Sections (C) /= null
1756                              and then Section = Cmd.Sections (C).all))
1757                  and then (not Has_Parameter or else Cmd.Params (C) /= null)
1758                then
1759                   Remove (Cmd.Expanded, C);
1760                   Remove (Cmd.Params, C);
1761                   Remove (Cmd.Sections, C);
1762                   Success := True;
1763
1764                   if not Remove_All then
1765                      return;
1766                   end if;
1767
1768                else
1769                   C := C + 1;
1770                end if;
1771             end loop;
1772          end if;
1773       end Remove_Simple_Switch;
1774
1775       procedure Remove_Simple_Switches is
1776          new For_Each_Simple_Switch (Remove_Simple_Switch);
1777
1778    --  Start of processing for Remove_Switch
1779
1780    begin
1781       Success := False;
1782       Remove_Simple_Switches (Cmd, Switch, "", Unalias => not Has_Parameter);
1783       Free (Cmd.Coalesce);
1784    end Remove_Switch;
1785
1786    -------------------
1787    -- Remove_Switch --
1788    -------------------
1789
1790    procedure Remove_Switch
1791      (Cmd       : in out Command_Line;
1792       Switch    : String;
1793       Parameter : String;
1794       Section   : String  := "")
1795    is
1796       procedure Remove_Simple_Switch (Simple : String; Param : String);
1797       --  Removes a simple switch, with no aliasing or grouping
1798
1799       --------------------------
1800       -- Remove_Simple_Switch --
1801       --------------------------
1802
1803       procedure Remove_Simple_Switch (Simple : String; Param : String) is
1804          C : Integer;
1805
1806       begin
1807          if Cmd.Expanded /= null then
1808             C := Cmd.Expanded'First;
1809             while C <= Cmd.Expanded'Last loop
1810                if Cmd.Expanded (C).all = Simple
1811                  and then
1812                    ((Cmd.Sections (C) = null
1813                      and then Section = "")
1814                     or else
1815                       (Cmd.Sections (C) /= null
1816                        and then Section = Cmd.Sections (C).all))
1817                  and then
1818                    ((Cmd.Params (C) = null and then Param = "")
1819                       or else
1820                         (Cmd.Params (C) /= null
1821                            and then
1822
1823                            --  Ignore the separator stored in Parameter
1824
1825                              Cmd.Params (C) (Cmd.Params (C)'First + 1
1826                                              .. Cmd.Params (C)'Last) =
1827                            Param))
1828                then
1829                   Remove (Cmd.Expanded, C);
1830                   Remove (Cmd.Params, C);
1831                   Remove (Cmd.Sections, C);
1832
1833                   --  The switch is necessarily unique by construction of
1834                   --  Add_Switch
1835
1836                   return;
1837
1838                else
1839                   C := C + 1;
1840                end if;
1841             end loop;
1842          end if;
1843       end Remove_Simple_Switch;
1844
1845       procedure Remove_Simple_Switches is
1846          new For_Each_Simple_Switch (Remove_Simple_Switch);
1847
1848    --  Start of processing for Remove_Switch
1849
1850    begin
1851       Remove_Simple_Switches (Cmd, Switch, Parameter);
1852       Free (Cmd.Coalesce);
1853    end Remove_Switch;
1854
1855    --------------------
1856    -- Group_Switches --
1857    --------------------
1858
1859    procedure Group_Switches
1860      (Cmd      : Command_Line;
1861       Result   : Argument_List_Access;
1862       Sections : Argument_List_Access;
1863       Params   : Argument_List_Access)
1864    is
1865       function Compatible_Parameter (Param : String_Access) return Boolean;
1866       --  Tell if the parameter can be part of a group
1867
1868       --------------------------
1869       -- Compatible_Parameter --
1870       --------------------------
1871
1872       function Compatible_Parameter (Param : String_Access) return Boolean is
1873       begin
1874          if Param = null then
1875             --  No parameter, OK
1876             return True;
1877
1878          elsif Param (Param'First) /= ASCII.NUL then
1879             --  We need parameters without separators...
1880             return False;
1881
1882          else
1883             --  We need number only parameters.
1884             for J in Param'First + 1 .. Param'Last loop
1885                if Param (J) not in '0' .. '9' then
1886                   return False;
1887                end if;
1888             end loop;
1889
1890             return True;
1891          end if;
1892
1893       end Compatible_Parameter;
1894
1895       Group   : Ada.Strings.Unbounded.Unbounded_String;
1896       First   : Natural;
1897       use type Ada.Strings.Unbounded.Unbounded_String;
1898
1899    begin
1900       if Cmd.Config = null
1901         or else Cmd.Config.Prefixes = null
1902       then
1903          return;
1904       end if;
1905
1906       for P in Cmd.Config.Prefixes'Range loop
1907          Group   := Ada.Strings.Unbounded.Null_Unbounded_String;
1908          First   := 0;
1909
1910          for C in Result'Range loop
1911             if Result (C) /= null
1912               and then Compatible_Parameter (Params (C))
1913               and then Looking_At
1914                 (Result (C).all, Result (C)'First, Cmd.Config.Prefixes (P).all)
1915             then
1916                --  If we are still in the same section, group the switches
1917                if First = 0
1918                  or else
1919                    (Sections (C) = null
1920                     and then Sections (First) = null)
1921                  or else
1922                    (Sections (C) /= null
1923                     and then Sections (First) /= null
1924                     and then Sections (C).all = Sections (First).all)
1925                then
1926                   Group :=
1927                     Group &
1928                       Result (C)
1929                         (Result (C)'First + Cmd.Config.Prefixes (P)'Length ..
1930                            Result (C)'Last);
1931
1932                   if Params (C) /= null then
1933                      Group := Group &
1934                        Params (C) (Params (C)'First + 1 .. Params (C)'Last);
1935                      Free (Params (C));
1936                   end if;
1937
1938                   if First = 0 then
1939                      First := C;
1940                   end if;
1941
1942                   Free (Result (C));
1943                else
1944                   --  We changed section: we put the grouped switches to the
1945                   --  first place, on continue with the new section.
1946                   Result (First) :=
1947                     new String'
1948                       (Cmd.Config.Prefixes (P).all &
1949                        Ada.Strings.Unbounded.To_String (Group));
1950                   Group :=
1951                     Ada.Strings.Unbounded.To_Unbounded_String
1952                       (Result (C)
1953                        (Result (C)'First + Cmd.Config.Prefixes (P)'Length ..
1954                             Result (C)'Last));
1955                   First := C;
1956                end if;
1957             end if;
1958          end loop;
1959
1960          if First > 0 then
1961             Result (First) :=
1962               new String'
1963                 (Cmd.Config.Prefixes (P).all &
1964                  Ada.Strings.Unbounded.To_String (Group));
1965          end if;
1966       end loop;
1967    end Group_Switches;
1968
1969    --------------------
1970    -- Alias_Switches --
1971    --------------------
1972
1973    procedure Alias_Switches
1974      (Cmd    : Command_Line;
1975       Result : Argument_List_Access;
1976       Params : Argument_List_Access)
1977    is
1978       Found : Boolean;
1979       First : Natural;
1980
1981       procedure Check_Cb (Switch : String; Param : String);
1982       --  Comment required ???
1983
1984       procedure Remove_Cb (Switch : String; Param : String);
1985       --  Comment required ???
1986
1987       --------------
1988       -- Check_Cb --
1989       --------------
1990
1991       procedure Check_Cb (Switch : String; Param : String) is
1992       begin
1993          if Found then
1994             for E in Result'Range loop
1995                if Result (E) /= null
1996                  and then
1997                    (Params (E) = null
1998                     or else Params (E) (Params (E)'First + 1
1999                                             .. Params (E)'Last) = Param)
2000                  and then Result (E).all = Switch
2001                then
2002                   return;
2003                end if;
2004             end loop;
2005
2006             Found := False;
2007          end if;
2008       end Check_Cb;
2009
2010       ---------------
2011       -- Remove_Cb --
2012       ---------------
2013
2014       procedure Remove_Cb (Switch : String; Param : String) is
2015       begin
2016          for E in Result'Range loop
2017             if Result (E) /= null
2018                  and then
2019                    (Params (E) = null
2020                     or else Params (E) (Params (E)'First + 1
2021                                             .. Params (E)'Last) = Param)
2022               and then Result (E).all = Switch
2023             then
2024                if First > E then
2025                   First := E;
2026                end if;
2027                Free (Result (E));
2028                Free (Params (E));
2029                return;
2030             end if;
2031          end loop;
2032       end Remove_Cb;
2033
2034       procedure Check_All is new For_Each_Simple_Switch (Check_Cb);
2035       procedure Remove_All is new For_Each_Simple_Switch (Remove_Cb);
2036
2037    --  Start of processing for Alias_Switches
2038
2039    begin
2040       if Cmd.Config = null
2041         or else Cmd.Config.Aliases = null
2042       then
2043          return;
2044       end if;
2045
2046       for A in Cmd.Config.Aliases'Range loop
2047
2048          --  Compute the various simple switches that make up the alias. We
2049          --  split the expansion into as many simple switches as possible, and
2050          --  then check whether the expanded command line has all of them.
2051
2052          Found := True;
2053          Check_All (Cmd, Cmd.Config.Expansions (A).all);
2054
2055          if Found then
2056             First := Integer'Last;
2057             Remove_All (Cmd, Cmd.Config.Expansions (A).all);
2058             Result (First) := new String'(Cmd.Config.Aliases (A).all);
2059          end if;
2060       end loop;
2061    end Alias_Switches;
2062
2063    -------------------
2064    -- Sort_Sections --
2065    -------------------
2066
2067    procedure Sort_Sections
2068      (Line     : GNAT.OS_Lib.Argument_List_Access;
2069       Sections : GNAT.OS_Lib.Argument_List_Access;
2070       Params   : GNAT.OS_Lib.Argument_List_Access)
2071    is
2072       Sections_List : Argument_List_Access :=
2073                         new Argument_List'(1 .. 1 => null);
2074       Found         : Boolean;
2075       Old_Line      : constant Argument_List := Line.all;
2076       Old_Sections  : constant Argument_List := Sections.all;
2077       Old_Params    : constant Argument_List := Params.all;
2078       Index         : Natural;
2079
2080    begin
2081       if Line = null then
2082          return;
2083       end if;
2084
2085       --  First construct a list of all sections
2086
2087       for E in Line'Range loop
2088          if Sections (E) /= null then
2089             Found := False;
2090             for S in Sections_List'Range loop
2091                if (Sections_List (S) = null and then Sections (E) = null)
2092                  or else
2093                    (Sections_List (S) /= null
2094                      and then Sections (E) /= null
2095                      and then Sections_List (S).all = Sections (E).all)
2096                then
2097                   Found := True;
2098                   exit;
2099                end if;
2100             end loop;
2101
2102             if not Found then
2103                Append (Sections_List, Sections (E));
2104             end if;
2105          end if;
2106       end loop;
2107
2108       Index := Line'First;
2109
2110       for S in Sections_List'Range loop
2111          for E in Old_Line'Range loop
2112             if (Sections_List (S) = null and then Old_Sections (E) = null)
2113               or else
2114                 (Sections_List (S) /= null
2115                   and then Old_Sections (E) /= null
2116                   and then Sections_List (S).all = Old_Sections (E).all)
2117             then
2118                Line (Index) := Old_Line (E);
2119                Sections (Index) := Old_Sections (E);
2120                Params (Index) := Old_Params (E);
2121                Index := Index + 1;
2122             end if;
2123          end loop;
2124       end loop;
2125    end Sort_Sections;
2126
2127    -----------
2128    -- Start --
2129    -----------
2130
2131    procedure Start
2132      (Cmd      : in out Command_Line;
2133       Iter     : in out Command_Line_Iterator;
2134       Expanded : Boolean)
2135    is
2136    begin
2137       if Cmd.Expanded = null then
2138          Iter.List := null;
2139          return;
2140       end if;
2141
2142       --  Reorder the expanded line so that sections are grouped
2143
2144       Sort_Sections (Cmd.Expanded, Cmd.Sections, Cmd.Params);
2145
2146       --  Coalesce the switches as much as possible
2147
2148       if not Expanded
2149         and then Cmd.Coalesce = null
2150       then
2151          Cmd.Coalesce := new Argument_List (Cmd.Expanded'Range);
2152          for E in Cmd.Expanded'Range loop
2153             Cmd.Coalesce (E) := new String'(Cmd.Expanded (E).all);
2154          end loop;
2155
2156          Cmd.Coalesce_Sections := new Argument_List (Cmd.Sections'Range);
2157          for E in Cmd.Sections'Range loop
2158             if Cmd.Sections (E) = null then
2159                Cmd.Coalesce_Sections (E) := null;
2160             else
2161                Cmd.Coalesce_Sections (E) := new String'(Cmd.Sections (E).all);
2162             end if;
2163          end loop;
2164
2165          Cmd.Coalesce_Params := new Argument_List (Cmd.Params'Range);
2166          for E in Cmd.Params'Range loop
2167             if Cmd.Params (E) = null then
2168                Cmd.Coalesce_Params (E) := null;
2169             else
2170                Cmd.Coalesce_Params (E) := new String'(Cmd.Params (E).all);
2171             end if;
2172          end loop;
2173
2174          --  Not a clone, since we will not modify the parameters anyway
2175
2176          Alias_Switches (Cmd, Cmd.Coalesce, Cmd.Coalesce_Params);
2177          Group_Switches
2178            (Cmd, Cmd.Coalesce, Cmd.Coalesce_Sections, Cmd.Coalesce_Params);
2179       end if;
2180
2181       if Expanded then
2182          Iter.List     := Cmd.Expanded;
2183          Iter.Params   := Cmd.Params;
2184          Iter.Sections := Cmd.Sections;
2185       else
2186          Iter.List     := Cmd.Coalesce;
2187          Iter.Params   := Cmd.Coalesce_Params;
2188          Iter.Sections := Cmd.Coalesce_Sections;
2189       end if;
2190
2191       if Iter.List = null then
2192          Iter.Current := Integer'Last;
2193       else
2194          Iter.Current := Iter.List'First;
2195
2196          while Iter.Current <= Iter.List'Last
2197            and then Iter.List (Iter.Current) = null
2198          loop
2199             Iter.Current := Iter.Current + 1;
2200          end loop;
2201       end if;
2202    end Start;
2203
2204    --------------------
2205    -- Current_Switch --
2206    --------------------
2207
2208    function Current_Switch (Iter : Command_Line_Iterator) return String is
2209    begin
2210       return Iter.List (Iter.Current).all;
2211    end Current_Switch;
2212
2213    --------------------
2214    -- Is_New_Section --
2215    --------------------
2216
2217    function Is_New_Section    (Iter : Command_Line_Iterator) return Boolean is
2218       Section : constant String := Current_Section (Iter);
2219    begin
2220       if Iter.Sections = null then
2221          return False;
2222       elsif Iter.Current = Iter.Sections'First
2223         or else Iter.Sections (Iter.Current - 1) = null
2224       then
2225          return Section /= "";
2226       end if;
2227
2228       return Section /= Iter.Sections (Iter.Current - 1).all;
2229    end Is_New_Section;
2230
2231    ---------------------
2232    -- Current_Section --
2233    ---------------------
2234
2235    function Current_Section (Iter : Command_Line_Iterator) return String is
2236    begin
2237       if Iter.Sections = null
2238         or else Iter.Current > Iter.Sections'Last
2239         or else Iter.Sections (Iter.Current) = null
2240       then
2241          return "";
2242       end if;
2243
2244       return Iter.Sections (Iter.Current).all;
2245    end Current_Section;
2246
2247    -----------------------
2248    -- Current_Separator --
2249    -----------------------
2250
2251    function Current_Separator (Iter : Command_Line_Iterator) return String is
2252    begin
2253       if Iter.Params = null
2254         or else Iter.Current > Iter.Params'Last
2255         or else Iter.Params (Iter.Current) = null
2256       then
2257          return "";
2258
2259       else
2260          declare
2261             Sep : constant Character :=
2262               Iter.Params (Iter.Current) (Iter.Params (Iter.Current)'First);
2263          begin
2264             if Sep = ASCII.NUL then
2265                return "";
2266             else
2267                return "" & Sep;
2268             end if;
2269          end;
2270       end if;
2271    end Current_Separator;
2272
2273    -----------------------
2274    -- Current_Parameter --
2275    -----------------------
2276
2277    function Current_Parameter (Iter : Command_Line_Iterator) return String is
2278    begin
2279       if Iter.Params = null
2280         or else Iter.Current > Iter.Params'Last
2281         or else Iter.Params (Iter.Current) = null
2282       then
2283          return "";
2284
2285       else
2286          declare
2287             P : constant String := Iter.Params (Iter.Current).all;
2288
2289          begin
2290             --  Skip separator
2291
2292             return P (P'First + 1 .. P'Last);
2293          end;
2294       end if;
2295    end Current_Parameter;
2296
2297    --------------
2298    -- Has_More --
2299    --------------
2300
2301    function Has_More (Iter : Command_Line_Iterator) return Boolean is
2302    begin
2303       return Iter.List /= null and then Iter.Current <= Iter.List'Last;
2304    end Has_More;
2305
2306    ----------
2307    -- Next --
2308    ----------
2309
2310    procedure Next (Iter : in out Command_Line_Iterator) is
2311    begin
2312       Iter.Current := Iter.Current + 1;
2313       while Iter.Current <= Iter.List'Last
2314         and then Iter.List (Iter.Current) = null
2315       loop
2316          Iter.Current := Iter.Current + 1;
2317       end loop;
2318    end Next;
2319
2320    ----------
2321    -- Free --
2322    ----------
2323
2324    procedure Free (Config : in out Command_Line_Configuration) is
2325    begin
2326       if Config /= null then
2327          Free (Config.Aliases);
2328          Free (Config.Expansions);
2329          Free (Config.Prefixes);
2330          Unchecked_Free (Config);
2331       end if;
2332    end Free;
2333
2334    ----------
2335    -- Free --
2336    ----------
2337
2338    procedure Free (Cmd : in out Command_Line) is
2339    begin
2340       Free (Cmd.Expanded);
2341       Free (Cmd.Coalesce);
2342       Free (Cmd.Params);
2343    end Free;
2344
2345 end GNAT.Command_Line;