| ------------------------------------------------------------------------------ |
| -- -- |
| -- GNAT COMPILER COMPONENTS -- |
| -- -- |
| -- G N A T . C O M M A N D _ L I N E -- |
| -- -- |
| -- B o d y -- |
| -- -- |
| -- Copyright (C) 1999-2004 Free Software Foundation, Inc. -- |
| -- -- |
| -- GNAT is free software; you can redistribute it and/or modify it under -- |
| -- terms of the GNU General Public License as published by the Free Soft- -- |
| -- ware Foundation; either version 2, or (at your option) any later ver- -- |
| -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- |
| -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- |
| -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -- |
| -- for more details. You should have received a copy of the GNU General -- |
| -- Public License distributed with GNAT; see file COPYING. If not, write -- |
| -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, -- |
| -- MA 02111-1307, USA. -- |
| -- -- |
| -- As a special exception, if other files instantiate generics from this -- |
| -- unit, or you link this unit with other files to produce an executable, -- |
| -- this unit does not by itself cause the resulting executable to be -- |
| -- covered by the GNU General Public License. This exception does not -- |
| -- however invalidate any other reasons why the executable file might be -- |
| -- covered by the GNU Public License. -- |
| -- -- |
| -- GNAT was originally developed by the GNAT team at New York University. -- |
| -- Extensive contributions were provided by Ada Core Technologies Inc. -- |
| -- -- |
| ------------------------------------------------------------------------------ |
| |
| with Ada.Command_Line; |
| with GNAT.OS_Lib; use GNAT.OS_Lib; |
| |
| package body GNAT.Command_Line is |
| |
| package CL renames Ada.Command_Line; |
| |
| type Section_Number is new Natural range 0 .. 65534; |
| for Section_Number'Size use 16; |
| |
| type Parameter_Type is record |
| Arg_Num : Positive; |
| First : Positive; |
| Last : Positive; |
| end record; |
| The_Parameter : Parameter_Type; |
| The_Switch : Parameter_Type; |
| -- This type and this variable are provided to store the current switch |
| -- and parameter |
| |
| type Is_Switch_Type is array (1 .. CL.Argument_Count) of Boolean; |
| pragma Pack (Is_Switch_Type); |
| |
| Is_Switch : Is_Switch_Type := (others => False); |
| -- Indicates wich arguments on the command line are considered not be |
| -- switches or parameters to switches (this leaves e.g. the filenames...) |
| |
| type Section_Type is array (1 .. CL.Argument_Count + 1) of Section_Number; |
| pragma Pack (Section_Type); |
| Section : Section_Type := (others => 1); |
| -- Contains the number of the section associated with the current |
| -- switch. If this number is 0, then it is a section delimiter, which |
| -- is never returns by GetOpt. |
| -- The last element of this array is set to 0 to avoid the need to test for |
| -- if we have reached the end of the command line in loops. |
| |
| Current_Argument : Natural := 1; |
| -- Number of the current argument parsed on the command line |
| |
| Current_Index : Natural := 1; |
| -- Index in the current argument of the character to be processed |
| |
| Current_Section : Section_Number := 1; |
| |
| Expansion_It : aliased Expansion_Iterator; |
| -- When Get_Argument is expanding a file name, this is the iterator used |
| |
| In_Expansion : Boolean := False; |
| -- True if we are expanding a file |
| |
| Switch_Character : Character := '-'; |
| -- The character at the beginning of the command line arguments, |
| -- indicating the beginning of a switch |
| |
| Stop_At_First : Boolean := False; |
| -- If it is True then Getopt stops at the first non-switch argument |
| |
| procedure Set_Parameter |
| (Variable : out Parameter_Type; |
| Arg_Num : Positive; |
| First : Positive; |
| Last : Positive); |
| pragma Inline (Set_Parameter); |
| -- Set the parameter that will be returned by Parameter below |
| |
| function Goto_Next_Argument_In_Section return Boolean; |
| -- Go to the next argument on the command line. If we are at the end |
| -- of the current section, we want to make sure there is no other |
| -- identical section on the command line (there might be multiple |
| -- instances of -largs). Returns True iff there is another argument. |
| |
| function Get_File_Names_Case_Sensitive return Integer; |
| pragma Import (C, Get_File_Names_Case_Sensitive, |
| "__gnat_get_file_names_case_sensitive"); |
| File_Names_Case_Sensitive : constant Boolean := |
| Get_File_Names_Case_Sensitive /= 0; |
| |
| procedure Canonical_Case_File_Name (S : in out String); |
| -- Given a file name, converts it to canonical case form. For systems |
| -- where file names are case sensitive, this procedure has no effect. |
| -- If file names are not case sensitive (i.e. for example if you have |
| -- the file "xyz.adb", you can refer to it as XYZ.adb or XyZ.AdB), then |
| -- this call converts the given string to canonical all lower case form, |
| -- so that two file names compare equal if they refer to the same file. |
| |
| ------------------------------ |
| -- Canonical_Case_File_Name -- |
| ------------------------------ |
| |
| procedure Canonical_Case_File_Name (S : in out String) is |
| begin |
| if not File_Names_Case_Sensitive then |
| for J in S'Range loop |
| if S (J) in 'A' .. 'Z' then |
| S (J) := Character'Val ( |
| Character'Pos (S (J)) + |
| Character'Pos ('a') - |
| Character'Pos ('A')); |
| end if; |
| end loop; |
| end if; |
| end Canonical_Case_File_Name; |
| |
| --------------- |
| -- Expansion -- |
| --------------- |
| |
| function Expansion (Iterator : Expansion_Iterator) return String is |
| use GNAT.Directory_Operations; |
| type Pointer is access all Expansion_Iterator; |
| |
| S : String (1 .. 1024); |
| Last : Natural; |
| It : constant Pointer := Iterator'Unrestricted_Access; |
| |
| Current : Depth := It.Current_Depth; |
| NL : Positive; |
| |
| begin |
| -- It is assumed that a directory is opened at the current level; |
| -- otherwise, GNAT.Directory_Operations.Directory_Error will be raised |
| -- at the first call to Read. |
| |
| loop |
| Read (It.Levels (Current).Dir, S, Last); |
| |
| -- If we have exhausted the directory, close it and go back one level |
| |
| if Last = 0 then |
| Close (It.Levels (Current).Dir); |
| |
| -- If we are at level 1, we are finished; return an empty string. |
| |
| if Current = 1 then |
| return String'(1 .. 0 => ' '); |
| else |
| -- Otherwise, continue with the directory at the previous level |
| |
| Current := Current - 1; |
| It.Current_Depth := Current; |
| end if; |
| |
| -- If this is a directory, that is neither "." or "..", attempt to |
| -- go to the next level. |
| |
| elsif Is_Directory |
| (It.Dir_Name (1 .. It.Levels (Current).Name_Last) & S (1 .. Last)) |
| and then S (1 .. Last) /= "." |
| and then S (1 .. Last) /= ".." |
| then |
| -- We can go to the next level only if we have not reached the |
| -- maximum depth, |
| |
| if Current < It.Maximum_Depth then |
| NL := It.Levels (Current).Name_Last; |
| |
| -- And if relative path of this new directory is not too long |
| |
| if NL + Last + 1 < Max_Path_Length then |
| Current := Current + 1; |
| It.Current_Depth := Current; |
| It.Dir_Name (NL + 1 .. NL + Last) := S (1 .. Last); |
| NL := NL + Last + 1; |
| It.Dir_Name (NL) := Directory_Separator; |
| It.Levels (Current).Name_Last := NL; |
| Canonical_Case_File_Name (It.Dir_Name (1 .. NL)); |
| |
| -- Open the new directory, and read from it |
| |
| GNAT.Directory_Operations.Open |
| (It.Levels (Current).Dir, It.Dir_Name (1 .. NL)); |
| end if; |
| end if; |
| |
| -- If not a directory, check the relative path against the pattern |
| |
| else |
| declare |
| Name : String := |
| It.Dir_Name (It.Start .. It.Levels (Current).Name_Last) & |
| S (1 .. Last); |
| begin |
| Canonical_Case_File_Name (Name); |
| |
| -- If it matches, return the relative path |
| |
| if GNAT.Regexp.Match (Name, Iterator.Regexp) then |
| return Name; |
| end if; |
| end; |
| end if; |
| |
| end loop; |
| |
| return String'(1 .. 0 => ' '); |
| end Expansion; |
| |
| ----------------- |
| -- Full_Switch -- |
| ----------------- |
| |
| function Full_Switch return String is |
| begin |
| return CL.Argument (The_Switch.Arg_Num) |
| (The_Switch.First .. The_Switch.Last); |
| end Full_Switch; |
| |
| ------------------ |
| -- Get_Argument -- |
| ------------------ |
| |
| function Get_Argument (Do_Expansion : Boolean := False) return String is |
| Total : constant Natural := CL.Argument_Count; |
| |
| begin |
| if In_Expansion then |
| declare |
| S : constant String := Expansion (Expansion_It); |
| |
| begin |
| if S'Length /= 0 then |
| return S; |
| else |
| In_Expansion := False; |
| end if; |
| end; |
| end if; |
| |
| if Current_Argument > Total then |
| |
| -- If this is the first time this function is called |
| |
| if Current_Index = 1 then |
| Current_Argument := 1; |
| while Current_Argument <= CL.Argument_Count |
| and then Section (Current_Argument) /= Current_Section |
| loop |
| Current_Argument := Current_Argument + 1; |
| end loop; |
| else |
| return String'(1 .. 0 => ' '); |
| end if; |
| |
| elsif Section (Current_Argument) = 0 then |
| while Current_Argument <= CL.Argument_Count |
| and then Section (Current_Argument) /= Current_Section |
| loop |
| Current_Argument := Current_Argument + 1; |
| end loop; |
| end if; |
| |
| Current_Index := 2; |
| |
| while Current_Argument <= Total |
| and then Is_Switch (Current_Argument) |
| loop |
| Current_Argument := Current_Argument + 1; |
| end loop; |
| |
| if Current_Argument > Total then |
| return String'(1 .. 0 => ' '); |
| end if; |
| |
| if Section (Current_Argument) = 0 then |
| return Get_Argument (Do_Expansion); |
| end if; |
| |
| Current_Argument := Current_Argument + 1; |
| |
| -- Could it be a file name with wild cards to expand? |
| |
| if Do_Expansion then |
| declare |
| Arg : String renames CL.Argument (Current_Argument - 1); |
| Index : Positive := Arg'First; |
| |
| begin |
| while Index <= Arg'Last loop |
| |
| if Arg (Index) = '*' |
| or else Arg (Index) = '?' |
| or else Arg (Index) = '[' |
| then |
| In_Expansion := True; |
| Start_Expansion (Expansion_It, Arg); |
| return Get_Argument (Do_Expansion); |
| end if; |
| |
| Index := Index + 1; |
| end loop; |
| end; |
| end if; |
| |
| return CL.Argument (Current_Argument - 1); |
| end Get_Argument; |
| |
| ------------ |
| -- Getopt -- |
| ------------ |
| |
| function Getopt |
| (Switches : String; |
| Concatenate : Boolean := True) return Character |
| is |
| Dummy : Boolean; |
| pragma Unreferenced (Dummy); |
| |
| begin |
| -- If we have finished parsing the current command line item (there |
| -- might be multiple switches in a single item), then go to the next |
| -- element |
| |
| if Current_Argument > CL.Argument_Count |
| or else (Current_Index > CL.Argument (Current_Argument)'Last |
| and then not Goto_Next_Argument_In_Section) |
| then |
| return ASCII.NUL; |
| end if; |
| |
| -- If we are on a new item, test if this might be a switch |
| |
| if Current_Index = 1 then |
| if CL.Argument (Current_Argument)(1) /= Switch_Character then |
| if Switches (Switches'First) = '*' then |
| Set_Parameter (The_Switch, |
| Arg_Num => Current_Argument, |
| First => 1, |
| Last => CL.Argument (Current_Argument)'Last); |
| Is_Switch (Current_Argument) := True; |
| Dummy := Goto_Next_Argument_In_Section; |
| return '*'; |
| end if; |
| |
| if Stop_At_First then |
| Current_Argument := Positive'Last; |
| return ASCII.NUL; |
| |
| elsif not Goto_Next_Argument_In_Section then |
| return ASCII.NUL; |
| |
| else |
| return Getopt (Switches); |
| end if; |
| end if; |
| |
| Current_Index := 2; |
| Is_Switch (Current_Argument) := True; |
| end if; |
| |
| declare |
| Arg : String renames CL.Argument (Current_Argument); |
| Index_Switches : Natural := 0; |
| Max_Length : Natural := 0; |
| Index : Natural := Switches'First; |
| Length : Natural := 1; |
| End_Index : Natural; |
| |
| begin |
| while Index <= Switches'Last loop |
| |
| -- Search the length of the parameter at this position in Switches |
| |
| Length := Index; |
| while Length <= Switches'Last |
| and then Switches (Length) /= ' ' |
| loop |
| Length := Length + 1; |
| end loop; |
| |
| if (Switches (Length - 1) = ':' or else |
| Switches (Length - 1) = '=' or else |
| Switches (Length - 1) = '?' or else |
| Switches (Length - 1) = '!') |
| and then Length > Index + 1 |
| then |
| Length := Length - 1; |
| end if; |
| |
| -- If it is the one we searched, it may be a candidate |
| |
| if Current_Index + Length - 1 - Index <= Arg'Last |
| and then |
| Switches (Index .. Length - 1) = |
| Arg (Current_Index .. Current_Index + Length - 1 - Index) |
| and then Length - Index > Max_Length |
| then |
| Index_Switches := Index; |
| Max_Length := Length - Index; |
| end if; |
| |
| -- Look for the next switch in Switches |
| |
| while Index <= Switches'Last |
| and then Switches (Index) /= ' ' loop |
| Index := Index + 1; |
| end loop; |
| |
| Index := Index + 1; |
| end loop; |
| |
| End_Index := Current_Index + Max_Length - 1; |
| |
| -- If switch is not accepted, skip it, unless we had '*' in Switches |
| |
| if Index_Switches = 0 then |
| if Switches (Switches'First) = '*' then |
| Set_Parameter (The_Switch, |
| Arg_Num => Current_Argument, |
| First => 1, |
| Last => CL.Argument (Current_Argument)'Last); |
| Is_Switch (Current_Argument) := True; |
| Dummy := Goto_Next_Argument_In_Section; |
| return '*'; |
| end if; |
| |
| -- Depending on the value of Concatenate, the full switch is |
| -- a single character (True) or the rest of the argument (False). |
| |
| if Concatenate then |
| End_Index := Current_Index; |
| else |
| End_Index := Arg'Last; |
| end if; |
| |
| Set_Parameter (The_Switch, |
| Arg_Num => Current_Argument, |
| First => Current_Index, |
| Last => End_Index); |
| Current_Index := End_Index + 1; |
| raise Invalid_Switch; |
| end if; |
| |
| Set_Parameter (The_Switch, |
| Arg_Num => Current_Argument, |
| First => Current_Index, |
| Last => End_Index); |
| |
| -- Case of switch needs an argument |
| |
| if Index_Switches + Max_Length <= Switches'Last then |
| |
| case Switches (Index_Switches + Max_Length) is |
| |
| when ':' => |
| |
| if End_Index < Arg'Last then |
| Set_Parameter (The_Parameter, |
| Arg_Num => Current_Argument, |
| First => End_Index + 1, |
| Last => Arg'Last); |
| Dummy := Goto_Next_Argument_In_Section; |
| |
| elsif Section (Current_Argument + 1) /= 0 then |
| Set_Parameter |
| (The_Parameter, |
| Arg_Num => Current_Argument + 1, |
| First => 1, |
| Last => CL.Argument (Current_Argument + 1)'Last); |
| Current_Argument := Current_Argument + 1; |
| Is_Switch (Current_Argument) := True; |
| Dummy := Goto_Next_Argument_In_Section; |
| |
| else |
| Current_Index := End_Index + 1; |
| raise Invalid_Parameter; |
| end if; |
| |
| when '=' => |
| |
| -- If the switch is of the form <switch>=xxx |
| |
| if End_Index < Arg'Last then |
| |
| if Arg (End_Index + 1) = '=' |
| and then End_Index + 1 < Arg'Last |
| then |
| Set_Parameter (The_Parameter, |
| Arg_Num => Current_Argument, |
| First => End_Index + 2, |
| Last => Arg'Last); |
| Dummy := Goto_Next_Argument_In_Section; |
| |
| else |
| Current_Index := End_Index + 1; |
| raise Invalid_Parameter; |
| end if; |
| |
| -- If the switch is of the form <switch> xxx |
| |
| elsif Section (Current_Argument + 1) /= 0 then |
| Set_Parameter |
| (The_Parameter, |
| Arg_Num => Current_Argument + 1, |
| First => 1, |
| Last => CL.Argument (Current_Argument + 1)'Last); |
| Current_Argument := Current_Argument + 1; |
| Is_Switch (Current_Argument) := True; |
| Dummy := Goto_Next_Argument_In_Section; |
| |
| else |
| Current_Index := End_Index + 1; |
| raise Invalid_Parameter; |
| end if; |
| |
| when '!' => |
| |
| if End_Index < Arg'Last then |
| Set_Parameter (The_Parameter, |
| Arg_Num => Current_Argument, |
| First => End_Index + 1, |
| Last => Arg'Last); |
| Dummy := Goto_Next_Argument_In_Section; |
| |
| else |
| Current_Index := End_Index + 1; |
| raise Invalid_Parameter; |
| end if; |
| |
| when '?' => |
| |
| if End_Index < Arg'Last then |
| Set_Parameter (The_Parameter, |
| Arg_Num => Current_Argument, |
| First => End_Index + 1, |
| Last => Arg'Last); |
| |
| else |
| Set_Parameter (The_Parameter, |
| Arg_Num => Current_Argument, |
| First => 2, |
| Last => 1); |
| end if; |
| Dummy := Goto_Next_Argument_In_Section; |
| |
| when others => |
| if Concatenate or else End_Index = Arg'Last then |
| Current_Index := End_Index + 1; |
| |
| else |
| -- If Concatenate is False and the full argument is not |
| -- recognized as a switch, this is an invalid switch. |
| |
| Set_Parameter (The_Switch, |
| Arg_Num => Current_Argument, |
| First => Current_Index, |
| Last => Arg'Last); |
| Current_Index := Arg'Last + 1; |
| raise Invalid_Switch; |
| end if; |
| end case; |
| |
| elsif Concatenate or else End_Index = Arg'Last then |
| Current_Index := End_Index + 1; |
| |
| else |
| -- If Concatenate is False and the full argument is not |
| -- recognized as a switch, this is an invalid switch. |
| |
| Set_Parameter (The_Switch, |
| Arg_Num => Current_Argument, |
| First => Current_Index, |
| Last => Arg'Last); |
| Current_Index := Arg'Last + 1; |
| raise Invalid_Switch; |
| end if; |
| |
| return Switches (Index_Switches); |
| end; |
| end Getopt; |
| |
| ----------------------------------- |
| -- Goto_Next_Argument_In_Section -- |
| ----------------------------------- |
| |
| function Goto_Next_Argument_In_Section return Boolean is |
| begin |
| Current_Index := 1; |
| Current_Argument := Current_Argument + 1; |
| |
| if Section (Current_Argument) = 0 then |
| loop |
| if Current_Argument > CL.Argument_Count then |
| return False; |
| end if; |
| |
| Current_Argument := Current_Argument + 1; |
| exit when Section (Current_Argument) = Current_Section; |
| end loop; |
| end if; |
| return True; |
| end Goto_Next_Argument_In_Section; |
| |
| ------------------ |
| -- Goto_Section -- |
| ------------------ |
| |
| procedure Goto_Section (Name : String := "") is |
| Index : Integer := 1; |
| |
| begin |
| In_Expansion := False; |
| |
| if Name = "" then |
| Current_Argument := 1; |
| Current_Index := 1; |
| Current_Section := 1; |
| return; |
| end if; |
| |
| while Index <= CL.Argument_Count loop |
| |
| if Section (Index) = 0 |
| and then CL.Argument (Index) = Switch_Character & Name |
| then |
| Current_Argument := Index + 1; |
| Current_Index := 1; |
| |
| if Current_Argument <= CL.Argument_Count then |
| Current_Section := Section (Current_Argument); |
| end if; |
| return; |
| end if; |
| |
| Index := Index + 1; |
| end loop; |
| |
| Current_Argument := Positive'Last; |
| Current_Index := 2; -- so that Get_Argument returns nothing |
| end Goto_Section; |
| |
| ---------------------------- |
| -- Initialize_Option_Scan -- |
| ---------------------------- |
| |
| procedure Initialize_Option_Scan |
| (Switch_Char : Character := '-'; |
| Stop_At_First_Non_Switch : Boolean := False; |
| Section_Delimiters : String := "") |
| is |
| Section_Num : Section_Number := 1; |
| Section_Index : Integer := Section_Delimiters'First; |
| Last : Integer; |
| Delimiter_Found : Boolean; |
| |
| begin |
| Current_Argument := 0; |
| Current_Index := 0; |
| In_Expansion := False; |
| Switch_Character := Switch_Char; |
| Stop_At_First := Stop_At_First_Non_Switch; |
| |
| -- If we are using sections, we have to preprocess the command line |
| -- to delimit them. A section can be repeated, so we just give each |
| -- item on the command line a section number |
| |
| while Section_Index <= Section_Delimiters'Last loop |
| |
| Last := Section_Index; |
| while Last <= Section_Delimiters'Last |
| and then Section_Delimiters (Last) /= ' ' |
| loop |
| Last := Last + 1; |
| end loop; |
| |
| Delimiter_Found := False; |
| Section_Num := Section_Num + 1; |
| |
| for Index in 1 .. CL.Argument_Count loop |
| if CL.Argument (Index)(1) = Switch_Character |
| and then |
| CL.Argument (Index) = Switch_Character & |
| Section_Delimiters |
| (Section_Index .. Last - 1) |
| then |
| Section (Index) := 0; |
| Delimiter_Found := True; |
| |
| elsif Section (Index) = 0 then |
| Delimiter_Found := False; |
| |
| elsif Delimiter_Found then |
| Section (Index) := Section_Num; |
| end if; |
| end loop; |
| |
| Section_Index := Last + 1; |
| while Section_Index <= Section_Delimiters'Last |
| and then Section_Delimiters (Section_Index) = ' ' |
| loop |
| Section_Index := Section_Index + 1; |
| end loop; |
| end loop; |
| |
| Delimiter_Found := Goto_Next_Argument_In_Section; |
| end Initialize_Option_Scan; |
| |
| --------------- |
| -- Parameter -- |
| --------------- |
| |
| function Parameter return String is |
| begin |
| if The_Parameter.First > The_Parameter.Last then |
| return String'(1 .. 0 => ' '); |
| else |
| return CL.Argument (The_Parameter.Arg_Num) |
| (The_Parameter.First .. The_Parameter.Last); |
| end if; |
| end Parameter; |
| |
| ------------------- |
| -- Set_Parameter -- |
| ------------------- |
| |
| procedure Set_Parameter |
| (Variable : out Parameter_Type; |
| Arg_Num : Positive; |
| First : Positive; |
| Last : Positive) |
| is |
| begin |
| Variable.Arg_Num := Arg_Num; |
| Variable.First := First; |
| Variable.Last := Last; |
| end Set_Parameter; |
| |
| --------------------- |
| -- Start_Expansion -- |
| --------------------- |
| |
| procedure Start_Expansion |
| (Iterator : out Expansion_Iterator; |
| Pattern : String; |
| Directory : String := ""; |
| Basic_Regexp : Boolean := True) |
| is |
| Directory_Separator : Character; |
| pragma Import (C, Directory_Separator, "__gnat_dir_separator"); |
| First : Positive := Pattern'First; |
| |
| Pat : String := Pattern; |
| |
| begin |
| Canonical_Case_File_Name (Pat); |
| Iterator.Current_Depth := 1; |
| |
| -- If Directory is unspecified, use the current directory ("./" or ".\") |
| |
| if Directory = "" then |
| Iterator.Dir_Name (1 .. 2) := "." & Directory_Separator; |
| Iterator.Start := 3; |
| |
| else |
| Iterator.Dir_Name (1 .. Directory'Length) := Directory; |
| Iterator.Start := Directory'Length + 1; |
| Canonical_Case_File_Name (Iterator.Dir_Name (1 .. Directory'Length)); |
| |
| -- Make sure that the last character is a directory separator |
| |
| if Directory (Directory'Last) /= Directory_Separator then |
| Iterator.Dir_Name (Iterator.Start) := Directory_Separator; |
| Iterator.Start := Iterator.Start + 1; |
| end if; |
| end if; |
| |
| Iterator.Levels (1).Name_Last := Iterator.Start - 1; |
| |
| -- Open the initial Directory, at depth 1 |
| |
| GNAT.Directory_Operations.Open |
| (Iterator.Levels (1).Dir, Iterator.Dir_Name (1 .. Iterator.Start - 1)); |
| |
| -- If in the current directory and the pattern starts with "./" or ".\", |
| -- drop the "./" or ".\" from the pattern. |
| |
| if Directory = "" and then Pat'Length > 2 |
| and then Pat (Pat'First) = '.' |
| and then Pat (Pat'First + 1) = Directory_Separator |
| then |
| First := Pat'First + 2; |
| end if; |
| |
| Iterator.Regexp := |
| GNAT.Regexp.Compile (Pat (First .. Pat'Last), Basic_Regexp, True); |
| |
| Iterator.Maximum_Depth := 1; |
| |
| -- Maximum_Depth is equal to 1 plus the number of directory separators |
| -- in the pattern. |
| |
| for Index in First .. Pat'Last loop |
| if Pat (Index) = Directory_Separator then |
| Iterator.Maximum_Depth := Iterator.Maximum_Depth + 1; |
| exit when Iterator.Maximum_Depth = Max_Depth; |
| end if; |
| end loop; |
| |
| end Start_Expansion; |
| |
| begin |
| Section (CL.Argument_Count + 1) := 0; |
| end GNAT.Command_Line; |