OSDN Git Service

Nathanael Nerode <neroden@gcc.gnu.org>
[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 --                                                                          --
10 --          Copyright (C) 1999-2002 Free Software Foundation, Inc.          --
11 --                                                                          --
12 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
13 -- terms of the  GNU General Public License as published  by the Free Soft- --
14 -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
15 -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
16 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
17 -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
18 -- for  more details.  You should have  received  a copy of the GNU General --
19 -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
20 -- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
21 -- MA 02111-1307, USA.                                                      --
22 --                                                                          --
23 -- As a special exception,  if other files  instantiate  generics from this --
24 -- unit, or you link  this unit with other files  to produce an executable, --
25 -- this  unit  does not  by itself cause  the resulting  executable  to  be --
26 -- covered  by the  GNU  General  Public  License.  This exception does not --
27 -- however invalidate  any other reasons why  the executable file  might be --
28 -- covered by the  GNU Public License.                                      --
29 --                                                                          --
30 -- Extensive contributions were provided by Ada Core Technologies Inc.   --
31 --                                                                          --
32 ------------------------------------------------------------------------------
33
34 with Ada.Command_Line;
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 Section_Number is new Natural range 0 .. 65534;
42    for Section_Number'Size use 16;
43
44    type Parameter_Type is record
45       Arg_Num : Positive;
46       First   : Positive;
47       Last    : Positive;
48    end record;
49    The_Parameter : Parameter_Type;
50    The_Switch    : Parameter_Type;
51    --  This type and this variable are provided to store the current switch
52    --  and parameter
53
54    type Is_Switch_Type is array (1 .. CL.Argument_Count) of Boolean;
55    pragma Pack (Is_Switch_Type);
56
57    Is_Switch : Is_Switch_Type := (others => False);
58    --  Indicates wich arguments on the command line are considered not be
59    --  switches or parameters to switches (this leaves e.g. the filenames...)
60
61    type Section_Type is array (1 .. CL.Argument_Count + 1) of Section_Number;
62    pragma Pack (Section_Type);
63    Section : Section_Type := (others => 1);
64    --  Contains the number of the section associated with the current
65    --  switch.  If this number is 0, then it is a section delimiter, which
66    --  is never returns by GetOpt.
67    --  The last element of this array is set to 0 to avoid the need to test for
68    --  if we have reached the end of the command line in loops.
69
70    Current_Argument : Natural := 1;
71    --  Number of the current argument parsed on the command line
72
73    Current_Index : Natural := 1;
74    --  Index in the current argument of the character to be processed
75
76    Current_Section : Section_Number := 1;
77
78    Expansion_It : aliased Expansion_Iterator;
79    --  When Get_Argument is expanding a file name, this is the iterator used
80
81    In_Expansion : Boolean := False;
82    --  True if we are expanding a file
83
84    Switch_Character : Character := '-';
85    --  The character at the beginning of the command line arguments,
86    --  indicating the beginning of a switch
87
88    Stop_At_First : Boolean := False;
89    --  If it is True then Getopt stops at the first non-switch argument
90
91    procedure Set_Parameter
92      (Variable : out Parameter_Type;
93       Arg_Num  : Positive;
94       First    : Positive;
95       Last     : Positive);
96    pragma Inline (Set_Parameter);
97    --  Set the parameter that will be returned by Parameter below
98
99    function Goto_Next_Argument_In_Section return Boolean;
100    --  Go to the next argument on the command line. If we are at the end
101    --  of the current section, we want to make sure there is no other
102    --  identical section on the command line (there might be multiple
103    --  instances of -largs). Returns True iff there is another argument.
104
105    function Get_File_Names_Case_Sensitive return Integer;
106    pragma Import (C, Get_File_Names_Case_Sensitive,
107                   "__gnat_get_file_names_case_sensitive");
108    File_Names_Case_Sensitive : constant Boolean :=
109                                  Get_File_Names_Case_Sensitive /= 0;
110
111    procedure Canonical_Case_File_Name (S : in out String);
112    --  Given a file name, converts it to canonical case form. For systems
113    --  where file names are case sensitive, this procedure has no effect.
114    --  If file names are not case sensitive (i.e. for example if you have
115    --  the file "xyz.adb", you can refer to it as XYZ.adb or XyZ.AdB), then
116    --  this call converts the given string to canonical all lower case form,
117    --  so that two file names compare equal if they refer to the same file.
118
119    ------------------------------
120    -- Canonical_Case_File_Name --
121    ------------------------------
122
123    procedure Canonical_Case_File_Name (S : in out String) is
124    begin
125       if not File_Names_Case_Sensitive then
126          for J in S'Range loop
127             if S (J) in 'A' .. 'Z' then
128                S (J) := Character'Val (
129                           Character'Pos (S (J)) +
130                           Character'Pos ('a')   -
131                           Character'Pos ('A'));
132             end if;
133          end loop;
134       end if;
135    end Canonical_Case_File_Name;
136
137    ---------------
138    -- Expansion --
139    ---------------
140
141    function Expansion (Iterator : Expansion_Iterator) return String is
142       use GNAT.Directory_Operations;
143       type Pointer is access all Expansion_Iterator;
144
145       S    : String (1 .. 1024);
146       Last : Natural;
147       It   : Pointer := Iterator'Unrestricted_Access;
148
149       Current : Depth := It.Current_Depth;
150       NL      : Positive;
151
152    begin
153       --  It is assumed that a directory is opened at the current level;
154       --  otherwise, GNAT.Directory_Operations.Directory_Error will be raised
155       --  at the first call to Read.
156
157       loop
158          Read (It.Levels (Current).Dir, S, Last);
159
160          --  If we have exhausted the directory, close it and go back one level
161
162          if Last = 0 then
163             Close (It.Levels (Current).Dir);
164
165             --  If we are at level 1, we are finished; return an empty string.
166
167             if Current = 1 then
168                return String'(1 .. 0 => ' ');
169             else
170                --  Otherwise, continue with the directory at the previous level
171
172                Current := Current - 1;
173                It.Current_Depth := Current;
174             end if;
175
176          --  If this is a directory, that is neither "." or "..", attempt to
177          --  go to the next level.
178
179          elsif Is_Directory
180            (It.Dir_Name (1 .. It.Levels (Current).Name_Last) & S (1 .. Last))
181            and then S (1 .. Last) /= "."
182            and then S (1 .. Last) /= ".."
183          then
184             --  We can go to the next level only if we have not reached the
185             --  maximum depth,
186
187             if Current < It.Maximum_Depth then
188                NL := It.Levels (Current).Name_Last;
189
190                --  And if relative path of this new directory is not too long
191
192                if NL + Last + 1 < Max_Path_Length then
193                   Current := Current + 1;
194                   It.Current_Depth := Current;
195                   It.Dir_Name (NL + 1 .. NL + Last) := S (1 .. Last);
196                   NL := NL + Last + 1;
197                   It.Dir_Name (NL) := Directory_Separator;
198                   It.Levels (Current).Name_Last := NL;
199                   Canonical_Case_File_Name (It.Dir_Name (1 .. NL));
200
201                   --  Open the new directory, and read from it
202
203                   GNAT.Directory_Operations.Open
204                     (It.Levels (Current).Dir, It.Dir_Name (1 .. NL));
205                end if;
206             end if;
207
208          --  If not a directory, check the relative path against the pattern
209
210          else
211             declare
212                Name : String :=
213                  It.Dir_Name (It.Start .. It.Levels (Current).Name_Last) &
214                  S (1 .. Last);
215             begin
216                Canonical_Case_File_Name (Name);
217
218                --  If it matches, return the relative path
219
220                if GNAT.Regexp.Match (Name, Iterator.Regexp) then
221                   return Name;
222                end if;
223             end;
224          end if;
225
226       end loop;
227
228       return String'(1 .. 0 => ' ');
229    end Expansion;
230
231    -----------------
232    -- Full_Switch --
233    -----------------
234
235    function Full_Switch return String is
236    begin
237       return CL.Argument (The_Switch.Arg_Num)
238         (The_Switch.First .. The_Switch.Last);
239    end Full_Switch;
240
241    ------------------
242    -- Get_Argument --
243    ------------------
244
245    function Get_Argument (Do_Expansion : Boolean := False) return String is
246       Total : constant Natural := CL.Argument_Count;
247
248    begin
249       if In_Expansion then
250          declare
251             S : String := Expansion (Expansion_It);
252
253          begin
254             if S'Length /= 0 then
255                return S;
256             else
257                In_Expansion := False;
258             end if;
259          end;
260       end if;
261
262       if Current_Argument > Total then
263
264          --  If this is the first time this function is called
265
266          if Current_Index = 1 then
267             Current_Argument := 1;
268             while Current_Argument <= CL.Argument_Count
269               and then Section (Current_Argument) /= Current_Section
270             loop
271                Current_Argument := Current_Argument + 1;
272             end loop;
273          else
274             return String'(1 .. 0 => ' ');
275          end if;
276
277       elsif Section (Current_Argument) = 0 then
278          while Current_Argument <= CL.Argument_Count
279            and then Section (Current_Argument) /= Current_Section
280          loop
281             Current_Argument := Current_Argument + 1;
282          end loop;
283       end if;
284
285       Current_Index := 2;
286
287       while Current_Argument <= Total
288         and then Is_Switch (Current_Argument)
289       loop
290          Current_Argument := Current_Argument + 1;
291       end loop;
292
293       if Current_Argument > Total then
294          return String'(1 .. 0 => ' ');
295       end if;
296
297       if Section (Current_Argument) = 0 then
298          return Get_Argument (Do_Expansion);
299       end if;
300
301       Current_Argument := Current_Argument + 1;
302
303       --  Could it be a file name with wild cards to expand?
304
305       if Do_Expansion then
306          declare
307             Arg       : String renames CL.Argument (Current_Argument - 1);
308             Index     : Positive := Arg'First;
309
310          begin
311             while Index <= Arg'Last loop
312
313                if Arg (Index) = '*'
314                  or else Arg (Index) = '?'
315                  or else Arg (Index) = '['
316                then
317                   In_Expansion := True;
318                   Start_Expansion (Expansion_It, Arg);
319                   return Get_Argument (Do_Expansion);
320                end if;
321
322                Index := Index + 1;
323             end loop;
324          end;
325       end if;
326
327       return CL.Argument (Current_Argument - 1);
328    end Get_Argument;
329
330    ------------
331    -- Getopt --
332    ------------
333
334    function Getopt (Switches : String) return Character is
335       Dummy : Boolean;
336
337    begin
338       --  If we have finished parsing the current command line item (there
339       --  might be multiple switches in a single item), then go to the next
340       --  element
341
342       if Current_Argument > CL.Argument_Count
343         or else (Current_Index > CL.Argument (Current_Argument)'Last
344                    and then not Goto_Next_Argument_In_Section)
345       then
346          return ASCII.NUL;
347       end if;
348
349       --  If we are on a new item, test if this might be a switch
350
351       if Current_Index = 1 then
352          if CL.Argument (Current_Argument)(1) /= Switch_Character then
353             if Switches (Switches'First) = '*' then
354                Set_Parameter (The_Switch,
355                               Arg_Num => Current_Argument,
356                               First   => 1,
357                               Last    => CL.Argument (Current_Argument)'Last);
358                Is_Switch (Current_Argument) := True;
359                Dummy := Goto_Next_Argument_In_Section;
360                return '*';
361             end if;
362
363             if Stop_At_First then
364                Current_Argument := Positive'Last;
365                return ASCII.NUL;
366
367             elsif not Goto_Next_Argument_In_Section then
368                return ASCII.NUL;
369
370             else
371                return Getopt (Switches);
372             end if;
373          end if;
374
375          Current_Index := 2;
376          Is_Switch (Current_Argument) := True;
377       end if;
378
379       declare
380          Arg            : String renames CL.Argument (Current_Argument);
381          Index_Switches : Natural := 0;
382          Max_Length     : Natural := 0;
383          Index          : Natural := Switches'First;
384          Length         : Natural := 1;
385          End_Index      : Natural;
386
387       begin
388          while Index <= Switches'Last loop
389
390             --  Search the length of the parameter at this position in Switches
391
392             Length := Index;
393             while Length <= Switches'Last
394               and then Switches (Length) /= ' '
395             loop
396                Length := Length + 1;
397             end loop;
398
399             if (Switches (Length - 1) = ':'  or else
400                 Switches (Length - 1) = '='  or else
401                 Switches (Length - 1) = '?'  or else
402                 Switches (Length - 1) = '!')
403               and then Length > Index + 1
404             then
405                Length := Length - 1;
406             end if;
407
408             --  If it is the one we searched, it may be a candidate
409
410             if Current_Index + Length - 1 - Index <= Arg'Last
411               and then
412                 Switches (Index .. Length - 1) =
413                   Arg (Current_Index .. Current_Index + Length - 1 - Index)
414               and then Length - Index > Max_Length
415             then
416                Index_Switches := Index;
417                Max_Length     := Length - Index;
418             end if;
419
420             --  Look for the next switch in Switches
421
422             while Index <= Switches'Last
423               and then Switches (Index) /= ' ' loop
424                Index := Index + 1;
425             end loop;
426
427             Index := Index + 1;
428          end loop;
429
430          End_Index := Current_Index + Max_Length - 1;
431
432          --  If switch is not accepted, skip it, unless we had '*' in Switches
433
434          if Index_Switches = 0 then
435             if Switches (Switches'First) = '*' then
436                Set_Parameter (The_Switch,
437                               Arg_Num => Current_Argument,
438                               First   => 1,
439                               Last    => CL.Argument (Current_Argument)'Last);
440                Is_Switch (Current_Argument) := True;
441                Dummy := Goto_Next_Argument_In_Section;
442                return '*';
443             end if;
444
445             Set_Parameter (The_Switch,
446                            Arg_Num => Current_Argument,
447                            First   => Current_Index,
448                            Last    => Current_Index);
449             Current_Index := Current_Index + 1;
450             raise Invalid_Switch;
451          end if;
452
453          Set_Parameter (The_Switch,
454                         Arg_Num => Current_Argument,
455                         First   => Current_Index,
456                         Last    => End_Index);
457
458          --  Case of switch needs an argument
459
460          if Index_Switches + Max_Length <= Switches'Last then
461
462             case Switches (Index_Switches + Max_Length) is
463
464                when ':' =>
465
466                   if End_Index < Arg'Last then
467                      Set_Parameter (The_Parameter,
468                                     Arg_Num => Current_Argument,
469                                     First   => End_Index + 1,
470                                     Last    => Arg'Last);
471                      Dummy := Goto_Next_Argument_In_Section;
472
473                   elsif Section (Current_Argument + 1) /= 0 then
474                      Set_Parameter
475                        (The_Parameter,
476                         Arg_Num => Current_Argument + 1,
477                         First   => 1,
478                         Last    => CL.Argument (Current_Argument + 1)'Last);
479                      Current_Argument := Current_Argument + 1;
480                      Is_Switch (Current_Argument) := True;
481                      Dummy := Goto_Next_Argument_In_Section;
482
483                   else
484                      Current_Index := End_Index + 1;
485                      raise Invalid_Parameter;
486                   end if;
487
488                when '=' =>
489
490                   --  If the switch is of the form <switch>=xxx
491
492                   if End_Index < Arg'Last then
493
494                      if Arg (End_Index + 1) = '='
495                        and then End_Index + 1 < Arg'Last
496                      then
497                         Set_Parameter (The_Parameter,
498                                        Arg_Num => Current_Argument,
499                                        First   => End_Index + 2,
500                                        Last    => Arg'Last);
501                         Dummy := Goto_Next_Argument_In_Section;
502
503                      else
504                         Current_Index := End_Index + 1;
505                         raise Invalid_Parameter;
506                      end if;
507
508                   --  If the switch is of the form <switch> xxx
509
510                   elsif Section (Current_Argument + 1) /= 0 then
511                      Set_Parameter
512                        (The_Parameter,
513                         Arg_Num => Current_Argument + 1,
514                         First   => 1,
515                         Last    => CL.Argument (Current_Argument + 1)'Last);
516                      Current_Argument := Current_Argument + 1;
517                      Is_Switch (Current_Argument) := True;
518                      Dummy := Goto_Next_Argument_In_Section;
519
520                   else
521                      Current_Index := End_Index + 1;
522                      raise Invalid_Parameter;
523                   end if;
524
525                when '!' =>
526
527                   if End_Index < Arg'Last then
528                      Set_Parameter (The_Parameter,
529                                     Arg_Num => Current_Argument,
530                                     First   => End_Index + 1,
531                                     Last    => Arg'Last);
532                      Dummy := Goto_Next_Argument_In_Section;
533
534                   else
535                      Current_Index := End_Index + 1;
536                      raise Invalid_Parameter;
537                   end if;
538
539                when '?' =>
540
541                   if End_Index < Arg'Last then
542                      Set_Parameter (The_Parameter,
543                                     Arg_Num => Current_Argument,
544                                     First   => End_Index + 1,
545                                     Last    => Arg'Last);
546
547                   else
548                      Set_Parameter (The_Parameter,
549                                     Arg_Num => Current_Argument,
550                                     First   => 2,
551                                     Last    => 1);
552                   end if;
553                   Dummy := Goto_Next_Argument_In_Section;
554
555                when others =>
556
557                   Current_Index := End_Index + 1;
558
559             end case;
560          else
561             Current_Index := End_Index + 1;
562          end if;
563
564          return Switches (Index_Switches);
565       end;
566    end Getopt;
567
568    -----------------------------------
569    -- Goto_Next_Argument_In_Section --
570    -----------------------------------
571
572    function Goto_Next_Argument_In_Section return Boolean is
573    begin
574       Current_Index := 1;
575       Current_Argument := Current_Argument + 1;
576
577       if Section (Current_Argument) = 0 then
578          loop
579             if Current_Argument > CL.Argument_Count then
580                return False;
581             end if;
582
583             Current_Argument := Current_Argument + 1;
584             exit when Section (Current_Argument) = Current_Section;
585          end loop;
586       end if;
587       return True;
588    end Goto_Next_Argument_In_Section;
589
590    ------------------
591    -- Goto_Section --
592    ------------------
593
594    procedure Goto_Section (Name : String := "") is
595       Index : Integer := 1;
596
597    begin
598       In_Expansion := False;
599
600       if Name = "" then
601          Current_Argument := 1;
602          Current_Index    := 1;
603          Current_Section  := 1;
604          return;
605       end if;
606
607       while Index <= CL.Argument_Count loop
608
609          if Section (Index) = 0
610            and then CL.Argument (Index) = Switch_Character & Name
611          then
612             Current_Argument := Index + 1;
613             Current_Index    := 1;
614
615             if Current_Argument <= CL.Argument_Count then
616                Current_Section := Section (Current_Argument);
617             end if;
618             return;
619          end if;
620
621          Index := Index + 1;
622       end loop;
623
624       Current_Argument := Positive'Last;
625       Current_Index := 2;   --  so that Get_Argument returns nothing
626    end Goto_Section;
627
628    ----------------------------
629    -- Initialize_Option_Scan --
630    ----------------------------
631
632    procedure Initialize_Option_Scan
633      (Switch_Char              : Character := '-';
634       Stop_At_First_Non_Switch : Boolean := False;
635       Section_Delimiters       : String := "")
636    is
637       Section_Num     : Section_Number := 1;
638       Section_Index   : Integer        := Section_Delimiters'First;
639       Last            : Integer;
640       Delimiter_Found : Boolean;
641
642    begin
643       Current_Argument := 0;
644       Current_Index := 0;
645       In_Expansion := False;
646       Switch_Character := Switch_Char;
647       Stop_At_First := Stop_At_First_Non_Switch;
648
649       --  If we are using sections, we have to preprocess the command line
650       --  to delimit them. A section can be repeated, so we just give each
651       --  item on the command line a section number
652
653       while Section_Index <= Section_Delimiters'Last loop
654
655          Last := Section_Index;
656          while Last <= Section_Delimiters'Last
657            and then Section_Delimiters (Last) /= ' '
658          loop
659             Last := Last + 1;
660          end loop;
661
662          Delimiter_Found := False;
663          Section_Num := Section_Num + 1;
664
665          for Index in 1 .. CL.Argument_Count loop
666             if CL.Argument (Index)(1) = Switch_Character
667               and then
668                 CL.Argument (Index) = Switch_Character &
669                                         Section_Delimiters
670                                           (Section_Index .. Last - 1)
671             then
672                Section (Index) := 0;
673                Delimiter_Found := True;
674
675             elsif Section (Index) = 0 then
676                Delimiter_Found := False;
677
678             elsif Delimiter_Found then
679                Section (Index) := Section_Num;
680             end if;
681          end loop;
682
683          Section_Index := Last + 1;
684          while Section_Index <= Section_Delimiters'Last
685            and then Section_Delimiters (Section_Index) = ' '
686          loop
687             Section_Index := Section_Index + 1;
688          end loop;
689       end loop;
690
691       Delimiter_Found := Goto_Next_Argument_In_Section;
692    end Initialize_Option_Scan;
693
694    ---------------
695    -- Parameter --
696    ---------------
697
698    function Parameter return String is
699    begin
700       if The_Parameter.First > The_Parameter.Last then
701          return String'(1 .. 0 => ' ');
702       else
703          return CL.Argument (The_Parameter.Arg_Num)
704            (The_Parameter.First .. The_Parameter.Last);
705       end if;
706    end Parameter;
707
708    -------------------
709    -- Set_Parameter --
710    -------------------
711
712    procedure Set_Parameter
713      (Variable : out Parameter_Type;
714       Arg_Num  : Positive;
715       First    : Positive;
716       Last     : Positive)
717    is
718    begin
719       Variable.Arg_Num := Arg_Num;
720       Variable.First   := First;
721       Variable.Last    := Last;
722    end Set_Parameter;
723
724    ---------------------
725    -- Start_Expansion --
726    ---------------------
727
728    procedure Start_Expansion
729      (Iterator     : out Expansion_Iterator;
730       Pattern      : String;
731       Directory    : String := "";
732       Basic_Regexp : Boolean := True)
733    is
734       Directory_Separator : Character;
735       pragma Import (C, Directory_Separator, "__gnat_dir_separator");
736       First : Positive := Pattern'First;
737
738       Pat : String := Pattern;
739
740    begin
741       Canonical_Case_File_Name (Pat);
742       Iterator.Current_Depth := 1;
743
744       --  If Directory is unspecified, use the current directory ("./" or ".\")
745
746       if Directory = "" then
747          Iterator.Dir_Name (1 .. 2) := "." & Directory_Separator;
748          Iterator.Start := 3;
749
750       else
751          Iterator.Dir_Name (1 .. Directory'Length) := Directory;
752          Iterator.Start := Directory'Length + 1;
753          Canonical_Case_File_Name (Iterator.Dir_Name (1 .. Directory'Length));
754
755          --  Make sure that the last character is a directory separator
756
757          if Directory (Directory'Last) /= Directory_Separator then
758             Iterator.Dir_Name (Iterator.Start) := Directory_Separator;
759             Iterator.Start := Iterator.Start + 1;
760          end if;
761       end if;
762
763       Iterator.Levels (1).Name_Last := Iterator.Start - 1;
764
765       --  Open the initial Directory, at depth 1
766
767       GNAT.Directory_Operations.Open
768         (Iterator.Levels (1).Dir, Iterator.Dir_Name (1 .. Iterator.Start - 1));
769
770       --  If in the current directory and the pattern starts with "./",
771       --  drop the "./" from the pattern.
772
773       if Directory = "" and then Pat'Length > 2
774         and then Pat (Pat'First .. Pat'First + 1) = "./"
775       then
776          First := Pat'First + 2;
777       end if;
778
779       Iterator.Regexp :=
780         GNAT.Regexp.Compile (Pat (First .. Pat'Last), Basic_Regexp, True);
781
782       Iterator.Maximum_Depth := 1;
783
784       --  Maximum_Depth is equal to 1 plus the number of directory separators
785       --  in the pattern.
786
787       for Index in First .. Pat'Last loop
788          if Pat (Index) = Directory_Separator then
789             Iterator.Maximum_Depth := Iterator.Maximum_Depth + 1;
790             exit when Iterator.Maximum_Depth = Max_Depth;
791          end if;
792       end loop;
793
794    end Start_Expansion;
795
796 begin
797    Section (CL.Argument_Count + 1) := 0;
798 end GNAT.Command_Line;