OSDN Git Service

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