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-2003 Free Software Foundation, Inc.          --
10--                                                                          --
11-- GNAT is free software;  you can  redistribute it  and/or modify it under --
12-- terms of the  GNU General Public License as published  by the Free Soft- --
13-- ware  Foundation;  either version 2,  or (at your option) any later ver- --
14-- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
15-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
16-- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
17-- for  more details.  You should have  received  a copy of the GNU General --
18-- Public License  distributed with GNAT;  see file COPYING.  If not, write --
19-- to  the Free Software Foundation,  59 Temple Place - Suite 330,  Boston, --
20-- MA 02111-1307, USA.                                                      --
21--                                                                          --
22-- As a special exception,  if other files  instantiate  generics from this --
23-- unit, or you link  this unit with other files  to produce an executable, --
24-- this  unit  does not  by itself cause  the resulting  executable  to  be --
25-- covered  by the  GNU  General  Public  License.  This exception does not --
26-- however invalidate  any other reasons why  the executable file  might be --
27-- covered by the  GNU Public License.                                      --
28--                                                                          --
29-- GNAT was originally developed  by the GNAT team at  New York University. --
30-- Extensive contributions were provided by Ada Core Technologies Inc.      --
31--                                                                          --
32------------------------------------------------------------------------------
33
34with Ada.Command_Line;
35with GNAT.OS_Lib; use GNAT.OS_Lib;
36
37package 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 : constant 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
335     (Switches    : String;
336      Concatenate : Boolean := True) return Character
337   is
338      Dummy : Boolean;
339      pragma Unreferenced (Dummy);
340
341   begin
342      --  If we have finished parsing the current command line item (there
343      --  might be multiple switches in a single item), then go to the next
344      --  element
345
346      if Current_Argument > CL.Argument_Count
347        or else (Current_Index > CL.Argument (Current_Argument)'Last
348                   and then not Goto_Next_Argument_In_Section)
349      then
350         return ASCII.NUL;
351      end if;
352
353      --  If we are on a new item, test if this might be a switch
354
355      if Current_Index = 1 then
356         if CL.Argument (Current_Argument)(1) /= Switch_Character then
357            if Switches (Switches'First) = '*' then
358               Set_Parameter (The_Switch,
359                              Arg_Num => Current_Argument,
360                              First   => 1,
361                              Last    => CL.Argument (Current_Argument)'Last);
362               Is_Switch (Current_Argument) := True;
363               Dummy := Goto_Next_Argument_In_Section;
364               return '*';
365            end if;
366
367            if Stop_At_First then
368               Current_Argument := Positive'Last;
369               return ASCII.NUL;
370
371            elsif not Goto_Next_Argument_In_Section then
372               return ASCII.NUL;
373
374            else
375               return Getopt (Switches);
376            end if;
377         end if;
378
379         Current_Index := 2;
380         Is_Switch (Current_Argument) := True;
381      end if;
382
383      declare
384         Arg            : String renames CL.Argument (Current_Argument);
385         Index_Switches : Natural := 0;
386         Max_Length     : Natural := 0;
387         Index          : Natural := Switches'First;
388         Length         : Natural := 1;
389         End_Index      : Natural;
390
391      begin
392         while Index <= Switches'Last loop
393
394            --  Search the length of the parameter at this position in Switches
395
396            Length := Index;
397            while Length <= Switches'Last
398              and then Switches (Length) /= ' '
399            loop
400               Length := Length + 1;
401            end loop;
402
403            if (Switches (Length - 1) = ':'  or else
404                Switches (Length - 1) = '='  or else
405                Switches (Length - 1) = '?'  or else
406                Switches (Length - 1) = '!')
407              and then Length > Index + 1
408            then
409               Length := Length - 1;
410            end if;
411
412            --  If it is the one we searched, it may be a candidate
413
414            if Current_Index + Length - 1 - Index <= Arg'Last
415              and then
416                Switches (Index .. Length - 1) =
417                  Arg (Current_Index .. Current_Index + Length - 1 - Index)
418              and then Length - Index > Max_Length
419            then
420               Index_Switches := Index;
421               Max_Length     := Length - Index;
422            end if;
423
424            --  Look for the next switch in Switches
425
426            while Index <= Switches'Last
427              and then Switches (Index) /= ' ' loop
428               Index := Index + 1;
429            end loop;
430
431            Index := Index + 1;
432         end loop;
433
434         End_Index := Current_Index + Max_Length - 1;
435
436         --  If switch is not accepted, skip it, unless we had '*' in Switches
437
438         if Index_Switches = 0 then
439            if Switches (Switches'First) = '*' then
440               Set_Parameter (The_Switch,
441                              Arg_Num => Current_Argument,
442                              First   => 1,
443                              Last    => CL.Argument (Current_Argument)'Last);
444               Is_Switch (Current_Argument) := True;
445               Dummy := Goto_Next_Argument_In_Section;
446               return '*';
447            end if;
448
449            --  Depending on the value of Concatenate, the full switch is
450            --  a single character (True) or the rest of the argument (False).
451
452            if Concatenate then
453               End_Index := Current_Index;
454            else
455               End_Index := Arg'Last;
456            end if;
457
458            Set_Parameter (The_Switch,
459                           Arg_Num => Current_Argument,
460                           First   => Current_Index,
461                           Last    => End_Index);
462            Current_Index := End_Index + 1;
463            raise Invalid_Switch;
464         end if;
465
466         Set_Parameter (The_Switch,
467                        Arg_Num => Current_Argument,
468                        First   => Current_Index,
469                        Last    => End_Index);
470
471         --  Case of switch needs an argument
472
473         if Index_Switches + Max_Length <= Switches'Last then
474
475            case Switches (Index_Switches + Max_Length) is
476
477               when ':' =>
478
479                  if End_Index < Arg'Last then
480                     Set_Parameter (The_Parameter,
481                                    Arg_Num => Current_Argument,
482                                    First   => End_Index + 1,
483                                    Last    => Arg'Last);
484                     Dummy := Goto_Next_Argument_In_Section;
485
486                  elsif Section (Current_Argument + 1) /= 0 then
487                     Set_Parameter
488                       (The_Parameter,
489                        Arg_Num => Current_Argument + 1,
490                        First   => 1,
491                        Last    => CL.Argument (Current_Argument + 1)'Last);
492                     Current_Argument := Current_Argument + 1;
493                     Is_Switch (Current_Argument) := True;
494                     Dummy := Goto_Next_Argument_In_Section;
495
496                  else
497                     Current_Index := End_Index + 1;
498                     raise Invalid_Parameter;
499                  end if;
500
501               when '=' =>
502
503                  --  If the switch is of the form <switch>=xxx
504
505                  if End_Index < Arg'Last then
506
507                     if Arg (End_Index + 1) = '='
508                       and then End_Index + 1 < Arg'Last
509                     then
510                        Set_Parameter (The_Parameter,
511                                       Arg_Num => Current_Argument,
512                                       First   => End_Index + 2,
513                                       Last    => Arg'Last);
514                        Dummy := Goto_Next_Argument_In_Section;
515
516                     else
517                        Current_Index := End_Index + 1;
518                        raise Invalid_Parameter;
519                     end if;
520
521                  --  If the switch is of the form <switch> xxx
522
523                  elsif Section (Current_Argument + 1) /= 0 then
524                     Set_Parameter
525                       (The_Parameter,
526                        Arg_Num => Current_Argument + 1,
527                        First   => 1,
528                        Last    => CL.Argument (Current_Argument + 1)'Last);
529                     Current_Argument := Current_Argument + 1;
530                     Is_Switch (Current_Argument) := True;
531                     Dummy := Goto_Next_Argument_In_Section;
532
533                  else
534                     Current_Index := End_Index + 1;
535                     raise Invalid_Parameter;
536                  end if;
537
538               when '!' =>
539
540                  if End_Index < Arg'Last then
541                     Set_Parameter (The_Parameter,
542                                    Arg_Num => Current_Argument,
543                                    First   => End_Index + 1,
544                                    Last    => Arg'Last);
545                     Dummy := Goto_Next_Argument_In_Section;
546
547                  else
548                     Current_Index := End_Index + 1;
549                     raise Invalid_Parameter;
550                  end if;
551
552               when '?' =>
553
554                  if End_Index < Arg'Last then
555                     Set_Parameter (The_Parameter,
556                                    Arg_Num => Current_Argument,
557                                    First   => End_Index + 1,
558                                    Last    => Arg'Last);
559
560                  else
561                     Set_Parameter (The_Parameter,
562                                    Arg_Num => Current_Argument,
563                                    First   => 2,
564                                    Last    => 1);
565                  end if;
566                  Dummy := Goto_Next_Argument_In_Section;
567
568               when others =>
569                  if Concatenate or else End_Index = Arg'Last then
570                     Current_Index := End_Index + 1;
571
572                  else
573                     --  If Concatenate is False and the full argument is not
574                     --  recognized as a switch, this is an invalid switch.
575
576                     Set_Parameter (The_Switch,
577                                    Arg_Num => Current_Argument,
578                                    First   => Current_Index,
579                                    Last    => Arg'Last);
580                     Current_Index := Arg'Last + 1;
581                     raise Invalid_Switch;
582                  end if;
583            end case;
584
585         elsif Concatenate or else End_Index = Arg'Last then
586            Current_Index := End_Index + 1;
587
588         else
589            --  If Concatenate is False and the full argument is not
590            --  recognized as a switch, this is an invalid switch.
591
592            Set_Parameter (The_Switch,
593                           Arg_Num => Current_Argument,
594                           First   => Current_Index,
595                           Last    => Arg'Last);
596            Current_Index := Arg'Last + 1;
597            raise Invalid_Switch;
598         end if;
599
600         return Switches (Index_Switches);
601      end;
602   end Getopt;
603
604   -----------------------------------
605   -- Goto_Next_Argument_In_Section --
606   -----------------------------------
607
608   function Goto_Next_Argument_In_Section return Boolean is
609   begin
610      Current_Index := 1;
611      Current_Argument := Current_Argument + 1;
612
613      if Section (Current_Argument) = 0 then
614         loop
615            if Current_Argument > CL.Argument_Count then
616               return False;
617            end if;
618
619            Current_Argument := Current_Argument + 1;
620            exit when Section (Current_Argument) = Current_Section;
621         end loop;
622      end if;
623      return True;
624   end Goto_Next_Argument_In_Section;
625
626   ------------------
627   -- Goto_Section --
628   ------------------
629
630   procedure Goto_Section (Name : String := "") is
631      Index : Integer := 1;
632
633   begin
634      In_Expansion := False;
635
636      if Name = "" then
637         Current_Argument := 1;
638         Current_Index    := 1;
639         Current_Section  := 1;
640         return;
641      end if;
642
643      while Index <= CL.Argument_Count loop
644
645         if Section (Index) = 0
646           and then CL.Argument (Index) = Switch_Character & Name
647         then
648            Current_Argument := Index + 1;
649            Current_Index    := 1;
650
651            if Current_Argument <= CL.Argument_Count then
652               Current_Section := Section (Current_Argument);
653            end if;
654            return;
655         end if;
656
657         Index := Index + 1;
658      end loop;
659
660      Current_Argument := Positive'Last;
661      Current_Index := 2;   --  so that Get_Argument returns nothing
662   end Goto_Section;
663
664   ----------------------------
665   -- Initialize_Option_Scan --
666   ----------------------------
667
668   procedure Initialize_Option_Scan
669     (Switch_Char              : Character := '-';
670      Stop_At_First_Non_Switch : Boolean := False;
671      Section_Delimiters       : String := "")
672   is
673      Section_Num     : Section_Number := 1;
674      Section_Index   : Integer        := Section_Delimiters'First;
675      Last            : Integer;
676      Delimiter_Found : Boolean;
677
678   begin
679      Current_Argument := 0;
680      Current_Index := 0;
681      In_Expansion := False;
682      Switch_Character := Switch_Char;
683      Stop_At_First := Stop_At_First_Non_Switch;
684
685      --  If we are using sections, we have to preprocess the command line
686      --  to delimit them. A section can be repeated, so we just give each
687      --  item on the command line a section number
688
689      while Section_Index <= Section_Delimiters'Last loop
690
691         Last := Section_Index;
692         while Last <= Section_Delimiters'Last
693           and then Section_Delimiters (Last) /= ' '
694         loop
695            Last := Last + 1;
696         end loop;
697
698         Delimiter_Found := False;
699         Section_Num := Section_Num + 1;
700
701         for Index in 1 .. CL.Argument_Count loop
702            if CL.Argument (Index)(1) = Switch_Character
703              and then
704                CL.Argument (Index) = Switch_Character &
705                                        Section_Delimiters
706                                          (Section_Index .. Last - 1)
707            then
708               Section (Index) := 0;
709               Delimiter_Found := True;
710
711            elsif Section (Index) = 0 then
712               Delimiter_Found := False;
713
714            elsif Delimiter_Found then
715               Section (Index) := Section_Num;
716            end if;
717         end loop;
718
719         Section_Index := Last + 1;
720         while Section_Index <= Section_Delimiters'Last
721           and then Section_Delimiters (Section_Index) = ' '
722         loop
723            Section_Index := Section_Index + 1;
724         end loop;
725      end loop;
726
727      Delimiter_Found := Goto_Next_Argument_In_Section;
728   end Initialize_Option_Scan;
729
730   ---------------
731   -- Parameter --
732   ---------------
733
734   function Parameter return String is
735   begin
736      if The_Parameter.First > The_Parameter.Last then
737         return String'(1 .. 0 => ' ');
738      else
739         return CL.Argument (The_Parameter.Arg_Num)
740           (The_Parameter.First .. The_Parameter.Last);
741      end if;
742   end Parameter;
743
744   -------------------
745   -- Set_Parameter --
746   -------------------
747
748   procedure Set_Parameter
749     (Variable : out Parameter_Type;
750      Arg_Num  : Positive;
751      First    : Positive;
752      Last     : Positive)
753   is
754   begin
755      Variable.Arg_Num := Arg_Num;
756      Variable.First   := First;
757      Variable.Last    := Last;
758   end Set_Parameter;
759
760   ---------------------
761   -- Start_Expansion --
762   ---------------------
763
764   procedure Start_Expansion
765     (Iterator     : out Expansion_Iterator;
766      Pattern      : String;
767      Directory    : String := "";
768      Basic_Regexp : Boolean := True)
769   is
770      Directory_Separator : Character;
771      pragma Import (C, Directory_Separator, "__gnat_dir_separator");
772      First : Positive := Pattern'First;
773
774      Pat : String := Pattern;
775
776   begin
777      Canonical_Case_File_Name (Pat);
778      Iterator.Current_Depth := 1;
779
780      --  If Directory is unspecified, use the current directory ("./" or ".\")
781
782      if Directory = "" then
783         Iterator.Dir_Name (1 .. 2) := "." & Directory_Separator;
784         Iterator.Start := 3;
785
786      else
787         Iterator.Dir_Name (1 .. Directory'Length) := Directory;
788         Iterator.Start := Directory'Length + 1;
789         Canonical_Case_File_Name (Iterator.Dir_Name (1 .. Directory'Length));
790
791         --  Make sure that the last character is a directory separator
792
793         if Directory (Directory'Last) /= Directory_Separator then
794            Iterator.Dir_Name (Iterator.Start) := Directory_Separator;
795            Iterator.Start := Iterator.Start + 1;
796         end if;
797      end if;
798
799      Iterator.Levels (1).Name_Last := Iterator.Start - 1;
800
801      --  Open the initial Directory, at depth 1
802
803      GNAT.Directory_Operations.Open
804        (Iterator.Levels (1).Dir, Iterator.Dir_Name (1 .. Iterator.Start - 1));
805
806      --  If in the current directory and the pattern starts with "./" or ".\",
807      --  drop the "./" or ".\" from the pattern.
808
809      if Directory = "" and then Pat'Length > 2
810        and then Pat (Pat'First) = '.'
811        and then Pat (Pat'First + 1) = Directory_Separator
812      then
813         First := Pat'First + 2;
814      end if;
815
816      Iterator.Regexp :=
817        GNAT.Regexp.Compile (Pat (First .. Pat'Last), Basic_Regexp, True);
818
819      Iterator.Maximum_Depth := 1;
820
821      --  Maximum_Depth is equal to 1 plus the number of directory separators
822      --  in the pattern.
823
824      for Index in First .. Pat'Last loop
825         if Pat (Index) = Directory_Separator then
826            Iterator.Maximum_Depth := Iterator.Maximum_Depth + 1;
827            exit when Iterator.Maximum_Depth = Max_Depth;
828         end if;
829      end loop;
830
831   end Start_Expansion;
832
833begin
834   Section (CL.Argument_Count + 1) := 0;
835end GNAT.Command_Line;
836