OSDN Git Service

2006-10-31 Javier Miranda <miranda@adacore.com>
[pf3gnuchains/gcc-fork.git] / gcc / ada / g-comlin.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT COMPILER COMPONENTS                         --
4 --                                                                          --
5 --                    G N A T . C O M M A N D _ L I N E                     --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --          Copyright (C) 1999-2005, Free Software Foundation, Inc.         --
10 --                                                                          --
11 -- GNAT is free software;  you can  redistribute it  and/or modify it under --
12 -- terms of the  GNU General Public License as published  by the Free Soft- --
13 -- ware  Foundation;  either version 2,  or (at your option) any later ver- --
14 -- sion.  GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
17 -- for  more details.  You should have  received  a copy of the GNU General --
18 -- Public License  distributed with GNAT;  see file COPYING.  If not, write --
19 -- to  the  Free Software Foundation,  51  Franklin  Street,  Fifth  Floor, --
20 -- Boston, MA 02110-1301, USA.                                              --
21 --                                                                          --
22 -- As a special exception,  if other files  instantiate  generics from this --
23 -- unit, or you link  this unit with other files  to produce an executable, --
24 -- this  unit  does not  by itself cause  the resulting  executable  to  be --
25 -- covered  by the  GNU  General  Public  License.  This exception does not --
26 -- however invalidate  any other reasons why  the executable file  might be --
27 -- covered by the  GNU Public License.                                      --
28 --                                                                          --
29 -- GNAT was originally developed  by the GNAT team at  New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
31 --                                                                          --
32 ------------------------------------------------------------------------------
33
34 with Ada.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 switch.
65    --  If this number is 0, then it is a section delimiter, which is never
66    --  returns by GetOpt. The last element of this array is set to 0 to avoid
67    --  the need to test for reaching the end of the command line in loops.
68
69    Current_Argument : Natural := 1;
70    --  Number of the current argument parsed on the command line
71
72    Current_Index : Natural := 1;
73    --  Index in the current argument of the character to be processed
74
75    Current_Section : Section_Number := 1;
76
77    Expansion_It : aliased Expansion_Iterator;
78    --  When Get_Argument is expanding a file name, this is the iterator used
79
80    In_Expansion : Boolean := False;
81    --  True if we are expanding a file
82
83    Switch_Character : Character := '-';
84    --  The character at the beginning of the command line arguments, indicating
85    --  the beginning of a switch.
86
87    Stop_At_First : Boolean := False;
88    --  If it is True then Getopt stops at the first non-switch argument
89
90    procedure Set_Parameter
91      (Variable : out Parameter_Type;
92       Arg_Num  : Positive;
93       First    : Positive;
94       Last     : Positive);
95    pragma Inline (Set_Parameter);
96    --  Set the parameter that will be returned by Parameter below
97
98    function Goto_Next_Argument_In_Section return Boolean;
99    --  Go to the next argument on the command line. If we are at the end of the
100    --  current section, we want to make sure there is no other identical
101    --  section on the command line (there might be multiple instances of
102    --  -largs). Returns True iff there is another argument.
103
104    function Get_File_Names_Case_Sensitive return Integer;
105    pragma Import (C, Get_File_Names_Case_Sensitive,
106                   "__gnat_get_file_names_case_sensitive");
107
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 where
113    --  file names are case sensitive, this procedure has no effect. If file
114    --  names are not case sensitive (i.e. for example if you have the file
115    --  "xyz.adb", you can refer to it as XYZ.adb or XyZ.AdB), then this call
116    --  converts the given string to canonical all lower case form, so that two
117    --  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   : constant 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;
388          Length         : Natural := 1;
389          End_Index      : Natural;
390
391       begin
392          --  Remove all leading spaces first to make sure that Index points
393          --  at the start of the first switch.
394
395          Index := Switches'First;
396          while Index <= Switches'Last and then Switches (Index) = ' ' loop
397             Index := Index + 1;
398          end loop;
399
400          while Index <= Switches'Last loop
401
402             --  Search the length of the parameter at this position in Switches
403
404             Length := Index;
405             while Length <= Switches'Last
406               and then Switches (Length) /= ' '
407             loop
408                Length := Length + 1;
409             end loop;
410
411             if (Switches (Length - 1) = ':'  or else
412                 Switches (Length - 1) = '='  or else
413                 Switches (Length - 1) = '?'  or else
414                 Switches (Length - 1) = '!')
415               and then Length > Index + 1
416             then
417                Length := Length - 1;
418             end if;
419
420             --  If it is the one we searched, it may be a candidate
421
422             if Current_Index + Length - 1 - Index <= Arg'Last
423               and then
424                 Switches (Index .. Length - 1) =
425                   Arg (Current_Index .. Current_Index + Length - 1 - Index)
426               and then Length - Index > Max_Length
427             then
428                Index_Switches := Index;
429                Max_Length     := Length - Index;
430             end if;
431
432             --  Look for the next switch in Switches
433
434             while Index <= Switches'Last
435               and then Switches (Index) /= ' ' loop
436                Index := Index + 1;
437             end loop;
438
439             Index := Index + 1;
440          end loop;
441
442          End_Index := Current_Index + Max_Length - 1;
443
444          --  If switch is not accepted, skip it, unless we had '*' in Switches
445
446          if Index_Switches = 0 then
447             if Switches (Switches'First) = '*' then
448                Set_Parameter (The_Switch,
449                               Arg_Num => Current_Argument,
450                               First   => 1,
451                               Last    => CL.Argument (Current_Argument)'Last);
452                Is_Switch (Current_Argument) := True;
453                Dummy := Goto_Next_Argument_In_Section;
454                return '*';
455             end if;
456
457             --  Depending on the value of Concatenate, the full switch is
458             --  a single character (True) or the rest of the argument (False).
459
460             if Concatenate then
461                End_Index := Current_Index;
462             else
463                End_Index := Arg'Last;
464             end if;
465
466             Set_Parameter (The_Switch,
467                            Arg_Num => Current_Argument,
468                            First   => Current_Index,
469                            Last    => End_Index);
470             Current_Index := End_Index + 1;
471             raise Invalid_Switch;
472          end if;
473
474          Set_Parameter (The_Switch,
475                         Arg_Num => Current_Argument,
476                         First   => Current_Index,
477                         Last    => End_Index);
478
479          --  Case of switch needs an argument
480
481          if Index_Switches + Max_Length <= Switches'Last then
482
483             case Switches (Index_Switches + Max_Length) is
484
485                when ':' =>
486
487                   if End_Index < Arg'Last then
488                      Set_Parameter (The_Parameter,
489                                     Arg_Num => Current_Argument,
490                                     First   => End_Index + 1,
491                                     Last    => Arg'Last);
492                      Dummy := Goto_Next_Argument_In_Section;
493
494                   elsif Section (Current_Argument + 1) /= 0 then
495                      Set_Parameter
496                        (The_Parameter,
497                         Arg_Num => Current_Argument + 1,
498                         First   => 1,
499                         Last    => CL.Argument (Current_Argument + 1)'Last);
500                      Current_Argument := Current_Argument + 1;
501                      Is_Switch (Current_Argument) := True;
502                      Dummy := Goto_Next_Argument_In_Section;
503
504                   else
505                      Current_Index := End_Index + 1;
506                      raise Invalid_Parameter;
507                   end if;
508
509                when '=' =>
510
511                   --  If the switch is of the form <switch>=xxx
512
513                   if End_Index < Arg'Last then
514
515                      if Arg (End_Index + 1) = '='
516                        and then End_Index + 1 < Arg'Last
517                      then
518                         Set_Parameter (The_Parameter,
519                                        Arg_Num => Current_Argument,
520                                        First   => End_Index + 2,
521                                        Last    => Arg'Last);
522                         Dummy := Goto_Next_Argument_In_Section;
523
524                      else
525                         Current_Index := End_Index + 1;
526                         raise Invalid_Parameter;
527                      end if;
528
529                   --  If the switch is of the form <switch> xxx
530
531                   elsif Section (Current_Argument + 1) /= 0 then
532                      Set_Parameter
533                        (The_Parameter,
534                         Arg_Num => Current_Argument + 1,
535                         First   => 1,
536                         Last    => CL.Argument (Current_Argument + 1)'Last);
537                      Current_Argument := Current_Argument + 1;
538                      Is_Switch (Current_Argument) := True;
539                      Dummy := Goto_Next_Argument_In_Section;
540
541                   else
542                      Current_Index := End_Index + 1;
543                      raise Invalid_Parameter;
544                   end if;
545
546                when '!' =>
547
548                   if End_Index < Arg'Last then
549                      Set_Parameter (The_Parameter,
550                                     Arg_Num => Current_Argument,
551                                     First   => End_Index + 1,
552                                     Last    => Arg'Last);
553                      Dummy := Goto_Next_Argument_In_Section;
554
555                   else
556                      Current_Index := End_Index + 1;
557                      raise Invalid_Parameter;
558                   end if;
559
560                when '?' =>
561
562                   if End_Index < Arg'Last then
563                      Set_Parameter (The_Parameter,
564                                     Arg_Num => Current_Argument,
565                                     First   => End_Index + 1,
566                                     Last    => Arg'Last);
567
568                   else
569                      Set_Parameter (The_Parameter,
570                                     Arg_Num => Current_Argument,
571                                     First   => 2,
572                                     Last    => 1);
573                   end if;
574                   Dummy := Goto_Next_Argument_In_Section;
575
576                when others =>
577                   if Concatenate or else End_Index = Arg'Last then
578                      Current_Index := End_Index + 1;
579
580                   else
581                      --  If Concatenate is False and the full argument is not
582                      --  recognized as a switch, this is an invalid switch.
583
584                      Set_Parameter (The_Switch,
585                                     Arg_Num => Current_Argument,
586                                     First   => Current_Index,
587                                     Last    => Arg'Last);
588                      Current_Index := Arg'Last + 1;
589                      raise Invalid_Switch;
590                   end if;
591             end case;
592
593          elsif Concatenate or else End_Index = Arg'Last then
594             Current_Index := End_Index + 1;
595
596          else
597             --  If Concatenate is False and the full argument is not
598             --  recognized as a switch, this is an invalid switch.
599
600             Set_Parameter (The_Switch,
601                            Arg_Num => Current_Argument,
602                            First   => Current_Index,
603                            Last    => Arg'Last);
604             Current_Index := Arg'Last + 1;
605             raise Invalid_Switch;
606          end if;
607
608          return Switches (Index_Switches);
609       end;
610    end Getopt;
611
612    -----------------------------------
613    -- Goto_Next_Argument_In_Section --
614    -----------------------------------
615
616    function Goto_Next_Argument_In_Section return Boolean is
617    begin
618       Current_Index := 1;
619       Current_Argument := Current_Argument + 1;
620
621       if Section (Current_Argument) = 0 then
622          loop
623             if Current_Argument > CL.Argument_Count then
624                return False;
625             end if;
626
627             Current_Argument := Current_Argument + 1;
628             exit when Section (Current_Argument) = Current_Section;
629          end loop;
630       end if;
631       return True;
632    end Goto_Next_Argument_In_Section;
633
634    ------------------
635    -- Goto_Section --
636    ------------------
637
638    procedure Goto_Section (Name : String := "") is
639       Index : Integer := 1;
640
641    begin
642       In_Expansion := False;
643
644       if Name = "" then
645          Current_Argument := 1;
646          Current_Index    := 1;
647          Current_Section  := 1;
648          return;
649       end if;
650
651       while Index <= CL.Argument_Count loop
652
653          if Section (Index) = 0
654            and then CL.Argument (Index) = Switch_Character & Name
655          then
656             Current_Argument := Index + 1;
657             Current_Index    := 1;
658
659             if Current_Argument <= CL.Argument_Count then
660                Current_Section := Section (Current_Argument);
661             end if;
662             return;
663          end if;
664
665          Index := Index + 1;
666       end loop;
667
668       Current_Argument := Positive'Last;
669       Current_Index := 2;   --  so that Get_Argument returns nothing
670    end Goto_Section;
671
672    ----------------------------
673    -- Initialize_Option_Scan --
674    ----------------------------
675
676    procedure Initialize_Option_Scan
677      (Switch_Char              : Character := '-';
678       Stop_At_First_Non_Switch : Boolean := False;
679       Section_Delimiters       : String := "")
680    is
681       Section_Num     : Section_Number := 1;
682       Section_Index   : Integer        := Section_Delimiters'First;
683       Last            : Integer;
684       Delimiter_Found : Boolean;
685
686    begin
687       Current_Argument := 0;
688       Current_Index := 0;
689       In_Expansion := False;
690       Switch_Character := Switch_Char;
691       Stop_At_First := Stop_At_First_Non_Switch;
692
693       --  If we are using sections, we have to preprocess the command line
694       --  to delimit them. A section can be repeated, so we just give each
695       --  item on the command line a section number
696
697       while Section_Index <= Section_Delimiters'Last loop
698
699          Last := Section_Index;
700          while Last <= Section_Delimiters'Last
701            and then Section_Delimiters (Last) /= ' '
702          loop
703             Last := Last + 1;
704          end loop;
705
706          Delimiter_Found := False;
707          Section_Num := Section_Num + 1;
708
709          for Index in 1 .. CL.Argument_Count loop
710             if CL.Argument (Index)(1) = Switch_Character
711               and then
712                 CL.Argument (Index) = Switch_Character &
713                                         Section_Delimiters
714                                           (Section_Index .. Last - 1)
715             then
716                Section (Index) := 0;
717                Delimiter_Found := True;
718
719             elsif Section (Index) = 0 then
720                Delimiter_Found := False;
721
722             elsif Delimiter_Found then
723                Section (Index) := Section_Num;
724             end if;
725          end loop;
726
727          Section_Index := Last + 1;
728          while Section_Index <= Section_Delimiters'Last
729            and then Section_Delimiters (Section_Index) = ' '
730          loop
731             Section_Index := Section_Index + 1;
732          end loop;
733       end loop;
734
735       Delimiter_Found := Goto_Next_Argument_In_Section;
736    end Initialize_Option_Scan;
737
738    ---------------
739    -- Parameter --
740    ---------------
741
742    function Parameter return String is
743    begin
744       if The_Parameter.First > The_Parameter.Last then
745          return String'(1 .. 0 => ' ');
746       else
747          return CL.Argument (The_Parameter.Arg_Num)
748            (The_Parameter.First .. The_Parameter.Last);
749       end if;
750    end Parameter;
751
752    -------------------
753    -- Set_Parameter --
754    -------------------
755
756    procedure Set_Parameter
757      (Variable : out Parameter_Type;
758       Arg_Num  : Positive;
759       First    : Positive;
760       Last     : Positive)
761    is
762    begin
763       Variable.Arg_Num := Arg_Num;
764       Variable.First   := First;
765       Variable.Last    := Last;
766    end Set_Parameter;
767
768    ---------------------
769    -- Start_Expansion --
770    ---------------------
771
772    procedure Start_Expansion
773      (Iterator     : out Expansion_Iterator;
774       Pattern      : String;
775       Directory    : String := "";
776       Basic_Regexp : Boolean := True)
777    is
778       Directory_Separator : Character;
779       pragma Import (C, Directory_Separator, "__gnat_dir_separator");
780       First : Positive := Pattern'First;
781
782       Pat : String := Pattern;
783
784    begin
785       Canonical_Case_File_Name (Pat);
786       Iterator.Current_Depth := 1;
787
788       --  If Directory is unspecified, use the current directory ("./" or ".\")
789
790       if Directory = "" then
791          Iterator.Dir_Name (1 .. 2) := "." & Directory_Separator;
792          Iterator.Start := 3;
793
794       else
795          Iterator.Dir_Name (1 .. Directory'Length) := Directory;
796          Iterator.Start := Directory'Length + 1;
797          Canonical_Case_File_Name (Iterator.Dir_Name (1 .. Directory'Length));
798
799          --  Make sure that the last character is a directory separator
800
801          if Directory (Directory'Last) /= Directory_Separator then
802             Iterator.Dir_Name (Iterator.Start) := Directory_Separator;
803             Iterator.Start := Iterator.Start + 1;
804          end if;
805       end if;
806
807       Iterator.Levels (1).Name_Last := Iterator.Start - 1;
808
809       --  Open the initial Directory, at depth 1
810
811       GNAT.Directory_Operations.Open
812         (Iterator.Levels (1).Dir, Iterator.Dir_Name (1 .. Iterator.Start - 1));
813
814       --  If in the current directory and the pattern starts with "./" or ".\",
815       --  drop the "./" or ".\" from the pattern.
816
817       if Directory = "" and then Pat'Length > 2
818         and then Pat (Pat'First) = '.'
819         and then Pat (Pat'First + 1) = Directory_Separator
820       then
821          First := Pat'First + 2;
822       end if;
823
824       Iterator.Regexp :=
825         GNAT.Regexp.Compile (Pat (First .. Pat'Last), Basic_Regexp, True);
826
827       Iterator.Maximum_Depth := 1;
828
829       --  Maximum_Depth is equal to 1 plus the number of directory separators
830       --  in the pattern.
831
832       for Index in First .. Pat'Last loop
833          if Pat (Index) = Directory_Separator then
834             Iterator.Maximum_Depth := Iterator.Maximum_Depth + 1;
835             exit when Iterator.Maximum_Depth = Max_Depth;
836          end if;
837       end loop;
838
839    end Start_Expansion;
840
841 begin
842    Section (CL.Argument_Count + 1) := 0;
843 end GNAT.Command_Line;