1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
5 -- G N A T . C O M M A N D _ L I N E --
9 -- Copyright (C) 1999-2006, Free Software Foundation, Inc. --
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. --
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. --
29 -- GNAT was originally developed by the GNAT team at New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc. --
32 ------------------------------------------------------------------------------
34 with Ada
.Command_Line
;
35 with GNAT
.OS_Lib
; use GNAT
.OS_Lib
;
37 package body GNAT
.Command_Line
is
39 package CL
renames Ada
.Command_Line
;
41 type Section_Number
is new Natural range 0 .. 65534;
42 for Section_Number
'Size use 16;
44 type Parameter_Type
is record
49 The_Parameter
: Parameter_Type
;
50 The_Switch
: Parameter_Type
;
51 -- This type and this variable are provided to store the current switch
54 type Is_Switch_Type
is array (1 .. CL
.Argument_Count
) of Boolean;
55 pragma Pack
(Is_Switch_Type
);
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...).
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.
69 Current_Argument
: Natural := 1;
70 -- Number of the current argument parsed on the command line
72 Current_Index
: Natural := 1;
73 -- Index in the current argument of the character to be processed
75 Current_Section
: Section_Number
:= 1;
77 Expansion_It
: aliased Expansion_Iterator
;
78 -- When Get_Argument is expanding a file name, this is the iterator used
80 In_Expansion
: Boolean := False;
81 -- True if we are expanding a file
83 Switch_Character
: Character := '-';
84 -- The character at the beginning of the command line arguments, indicating
85 -- the beginning of a switch.
87 Stop_At_First
: Boolean := False;
88 -- If it is True then Getopt stops at the first non-switch argument
90 procedure Set_Parameter
91 (Variable
: out Parameter_Type
;
95 pragma Inline
(Set_Parameter
);
96 -- Set the parameter that will be returned by Parameter below
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.
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");
108 File_Names_Case_Sensitive
: constant Boolean :=
109 Get_File_Names_Case_Sensitive
/= 0;
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.
119 ------------------------------
120 -- Canonical_Case_File_Name --
121 ------------------------------
123 procedure Canonical_Case_File_Name
(S
: in out String) is
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'));
135 end Canonical_Case_File_Name
;
141 function Expansion
(Iterator
: Expansion_Iterator
) return String is
142 use GNAT
.Directory_Operations
;
143 type Pointer
is access all Expansion_Iterator
;
145 S
: String (1 .. 1024);
147 It
: constant Pointer
:= Iterator
'Unrestricted_Access;
149 Current
: Depth
:= It
.Current_Depth
;
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.
158 Read
(It
.Levels
(Current
).Dir
, S
, Last
);
160 -- If we have exhausted the directory, close it and go back one level
163 Close
(It
.Levels
(Current
).Dir
);
165 -- If we are at level 1, we are finished; return an empty string
168 return String'(1 .. 0 => ' ');
170 -- Otherwise, continue with the directory at the previous level
172 Current := Current - 1;
173 It.Current_Depth := Current;
176 -- If this is a directory, that is neither "." or "..", attempt to
177 -- go to the next level.
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) /= ".."
184 -- We can go to the next level only if we have not reached the
187 if Current < It.Maximum_Depth then
188 NL := It.Levels (Current).Name_Last;
190 -- And if relative path of this new directory is not too long
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);
197 It.Dir_Name (NL) := Directory_Separator;
198 It.Levels (Current).Name_Last := NL;
199 Canonical_Case_File_Name (It.Dir_Name (1 .. NL));
201 -- Open the new directory, and read from it
203 GNAT.Directory_Operations.Open
204 (It.Levels (Current).Dir, It.Dir_Name (1 .. NL));
208 -- If not a directory, check the relative path against the pattern
213 It.Dir_Name (It.Start .. It.Levels (Current).Name_Last) &
216 Canonical_Case_File_Name (Name);
218 -- If it matches, return the relative path
220 if GNAT.Regexp.Match (Name, Iterator.Regexp) then
228 return String'(1 .. 0 => ' ');
235 function Full_Switch
return String is
237 return CL
.Argument
(The_Switch
.Arg_Num
)
238 (The_Switch
.First
.. The_Switch
.Last
);
245 function Get_Argument
(Do_Expansion
: Boolean := False) return String is
246 Total
: constant Natural := CL
.Argument_Count
;
251 S
: constant String := Expansion
(Expansion_It
);
254 if S
'Length /= 0 then
257 In_Expansion
:= False;
262 if Current_Argument
> Total
then
264 -- If this is the first time this function is called
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
271 Current_Argument
:= Current_Argument
+ 1;
274 return String'(1 .. 0 => ' ');
277 elsif Section (Current_Argument) = 0 then
278 while Current_Argument <= CL.Argument_Count
279 and then Section (Current_Argument) /= Current_Section
281 Current_Argument := Current_Argument + 1;
287 while Current_Argument <= Total
288 and then Is_Switch (Current_Argument)
290 Current_Argument := Current_Argument + 1;
293 if Current_Argument > Total then
294 return String'(1 .. 0 => ' ');
297 if Section
(Current_Argument
) = 0 then
298 return Get_Argument
(Do_Expansion
);
301 Current_Argument
:= Current_Argument
+ 1;
303 -- Could it be a file name with wild cards to expand?
307 Arg
: String renames CL
.Argument
(Current_Argument
- 1);
308 Index
: Positive := Arg
'First;
311 while Index
<= Arg
'Last loop
314 or else Arg
(Index
) = '?'
315 or else Arg
(Index
) = '['
317 In_Expansion
:= True;
318 Start_Expansion
(Expansion_It
, Arg
);
319 return Get_Argument
(Do_Expansion
);
327 return CL
.Argument
(Current_Argument
- 1);
336 Concatenate
: Boolean := True) return Character
339 pragma Unreferenced
(Dummy
);
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
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
)
353 -- If we are on a new item, test if this might be a switch
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
,
361 Last
=> CL
.Argument
(Current_Argument
)'Last);
362 Is_Switch
(Current_Argument
) := True;
363 Dummy
:= Goto_Next_Argument_In_Section
;
367 if Stop_At_First
then
368 Current_Argument
:= Positive'Last;
371 elsif not Goto_Next_Argument_In_Section
then
375 return Getopt
(Switches
);
380 Is_Switch
(Current_Argument
) := True;
384 Arg
: String renames CL
.Argument
(Current_Argument
);
385 Index_Switches
: Natural := 0;
386 Max_Length
: Natural := 0;
388 Length
: Natural := 1;
392 -- Remove all leading spaces first to make sure that Index points
393 -- at the start of the first switch.
395 Index
:= Switches
'First;
396 while Index
<= Switches
'Last and then Switches
(Index
) = ' ' loop
400 while Index
<= Switches
'Last loop
402 -- Search the length of the parameter at this position in Switches
405 while Length
<= Switches
'Last
406 and then Switches
(Length
) /= ' '
408 Length
:= Length
+ 1;
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
417 Length
:= Length
- 1;
420 -- If it is the one we searched, it may be a candidate
422 if Current_Index
+ Length
- 1 - Index
<= Arg
'Last
424 Switches
(Index
.. Length
- 1) =
425 Arg
(Current_Index
.. Current_Index
+ Length
- 1 - Index
)
426 and then Length
- Index
> Max_Length
428 Index_Switches
:= Index
;
429 Max_Length
:= Length
- Index
;
432 -- Look for the next switch in Switches
434 while Index
<= Switches
'Last
435 and then Switches
(Index
) /= ' ' loop
442 End_Index
:= Current_Index
+ Max_Length
- 1;
444 -- If switch is not accepted, skip it, unless we had '*' in Switches
446 if Index_Switches
= 0 then
447 if Switches
(Switches
'First) = '*' then
448 Set_Parameter
(The_Switch
,
449 Arg_Num
=> Current_Argument
,
451 Last
=> CL
.Argument
(Current_Argument
)'Last);
452 Is_Switch
(Current_Argument
) := True;
453 Dummy
:= Goto_Next_Argument_In_Section
;
457 -- Depending on the value of Concatenate, the full switch is
458 -- a single character (True) or the rest of the argument (False).
461 End_Index
:= Current_Index
;
463 End_Index
:= Arg
'Last;
466 Set_Parameter
(The_Switch
,
467 Arg_Num
=> Current_Argument
,
468 First
=> Current_Index
,
470 Current_Index
:= End_Index
+ 1;
471 raise Invalid_Switch
;
474 Set_Parameter
(The_Switch
,
475 Arg_Num
=> Current_Argument
,
476 First
=> Current_Index
,
479 -- Case of switch needs an argument
481 if Index_Switches
+ Max_Length
<= Switches
'Last then
483 case Switches
(Index_Switches
+ Max_Length
) is
487 if End_Index
< Arg
'Last then
488 Set_Parameter
(The_Parameter
,
489 Arg_Num
=> Current_Argument
,
490 First
=> End_Index
+ 1,
492 Dummy
:= Goto_Next_Argument_In_Section
;
494 elsif Section
(Current_Argument
+ 1) /= 0 then
497 Arg_Num
=> Current_Argument
+ 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
;
505 Current_Index
:= End_Index
+ 1;
506 raise Invalid_Parameter
;
511 -- If the switch is of the form <switch>=xxx
513 if End_Index
< Arg
'Last then
515 if Arg
(End_Index
+ 1) = '='
516 and then End_Index
+ 1 < Arg
'Last
518 Set_Parameter
(The_Parameter
,
519 Arg_Num
=> Current_Argument
,
520 First
=> End_Index
+ 2,
522 Dummy
:= Goto_Next_Argument_In_Section
;
525 Current_Index
:= End_Index
+ 1;
526 raise Invalid_Parameter
;
529 -- If the switch is of the form <switch> xxx
531 elsif Section
(Current_Argument
+ 1) /= 0 then
534 Arg_Num
=> Current_Argument
+ 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
;
542 Current_Index
:= End_Index
+ 1;
543 raise Invalid_Parameter
;
548 if End_Index
< Arg
'Last then
549 Set_Parameter
(The_Parameter
,
550 Arg_Num
=> Current_Argument
,
551 First
=> End_Index
+ 1,
553 Dummy
:= Goto_Next_Argument_In_Section
;
556 Current_Index
:= End_Index
+ 1;
557 raise Invalid_Parameter
;
562 if End_Index
< Arg
'Last then
563 Set_Parameter
(The_Parameter
,
564 Arg_Num
=> Current_Argument
,
565 First
=> End_Index
+ 1,
569 Set_Parameter
(The_Parameter
,
570 Arg_Num
=> Current_Argument
,
574 Dummy
:= Goto_Next_Argument_In_Section
;
577 if Concatenate
or else End_Index
= Arg
'Last then
578 Current_Index
:= End_Index
+ 1;
581 -- If Concatenate is False and the full argument is not
582 -- recognized as a switch, this is an invalid switch.
584 Set_Parameter
(The_Switch
,
585 Arg_Num
=> Current_Argument
,
586 First
=> Current_Index
,
588 Current_Index
:= Arg
'Last + 1;
589 raise Invalid_Switch
;
593 elsif Concatenate
or else End_Index
= Arg
'Last then
594 Current_Index
:= End_Index
+ 1;
597 -- If Concatenate is False and the full argument is not
598 -- recognized as a switch, this is an invalid switch.
600 Set_Parameter
(The_Switch
,
601 Arg_Num
=> Current_Argument
,
602 First
=> Current_Index
,
604 Current_Index
:= Arg
'Last + 1;
605 raise Invalid_Switch
;
608 return Switches
(Index_Switches
);
612 -----------------------------------
613 -- Goto_Next_Argument_In_Section --
614 -----------------------------------
616 function Goto_Next_Argument_In_Section
return Boolean is
619 Current_Argument
:= Current_Argument
+ 1;
621 if Section
(Current_Argument
) = 0 then
623 if Current_Argument
> CL
.Argument_Count
then
627 Current_Argument
:= Current_Argument
+ 1;
628 exit when Section
(Current_Argument
) = Current_Section
;
632 end Goto_Next_Argument_In_Section
;
638 procedure Goto_Section
(Name
: String := "") is
639 Index
: Integer := 1;
642 In_Expansion
:= False;
645 Current_Argument
:= 1;
647 Current_Section
:= 1;
651 while Index
<= CL
.Argument_Count
loop
653 if Section
(Index
) = 0
654 and then CL
.Argument
(Index
) = Switch_Character
& Name
656 Current_Argument
:= Index
+ 1;
659 if Current_Argument
<= CL
.Argument_Count
then
660 Current_Section
:= Section
(Current_Argument
);
668 Current_Argument
:= Positive'Last;
669 Current_Index
:= 2; -- so that Get_Argument returns nothing
672 ----------------------------
673 -- Initialize_Option_Scan --
674 ----------------------------
676 procedure Initialize_Option_Scan
677 (Switch_Char
: Character := '-';
678 Stop_At_First_Non_Switch
: Boolean := False;
679 Section_Delimiters
: String := "")
681 Section_Num
: Section_Number
:= 1;
682 Section_Index
: Integer := Section_Delimiters
'First;
684 Delimiter_Found
: Boolean;
687 pragma Warnings
(Off
, Discard
);
690 Current_Argument
:= 0;
692 In_Expansion
:= False;
693 Switch_Character
:= Switch_Char
;
694 Stop_At_First
:= Stop_At_First_Non_Switch
;
696 -- If we are using sections, we have to preprocess the command line
697 -- to delimit them. A section can be repeated, so we just give each
698 -- item on the command line a section number
700 while Section_Index
<= Section_Delimiters
'Last loop
702 Last
:= Section_Index
;
703 while Last
<= Section_Delimiters
'Last
704 and then Section_Delimiters
(Last
) /= ' '
709 Delimiter_Found
:= False;
710 Section_Num
:= Section_Num
+ 1;
712 for Index
in 1 .. CL
.Argument_Count
loop
713 if CL
.Argument
(Index
)(1) = Switch_Character
715 CL
.Argument
(Index
) = Switch_Character
&
717 (Section_Index
.. Last
- 1)
719 Section
(Index
) := 0;
720 Delimiter_Found
:= True;
722 elsif Section
(Index
) = 0 then
723 Delimiter_Found
:= False;
725 elsif Delimiter_Found
then
726 Section
(Index
) := Section_Num
;
730 Section_Index
:= Last
+ 1;
731 while Section_Index
<= Section_Delimiters
'Last
732 and then Section_Delimiters
(Section_Index
) = ' '
734 Section_Index
:= Section_Index
+ 1;
738 Discard
:= Goto_Next_Argument_In_Section
;
739 end Initialize_Option_Scan
;
745 function Parameter
return String is
747 if The_Parameter
.First
> The_Parameter
.Last
then
748 return String'(1 .. 0 => ' ');
750 return CL.Argument (The_Parameter.Arg_Num)
751 (The_Parameter.First .. The_Parameter.Last);
759 procedure Set_Parameter
760 (Variable : out Parameter_Type;
766 Variable.Arg_Num := Arg_Num;
767 Variable.First := First;
768 Variable.Last := Last;
771 ---------------------
772 -- Start_Expansion --
773 ---------------------
775 procedure Start_Expansion
776 (Iterator : out Expansion_Iterator;
778 Directory : String := "";
779 Basic_Regexp : Boolean := True)
781 Directory_Separator : Character;
782 pragma Import (C, Directory_Separator, "__gnat_dir_separator");
783 First : Positive := Pattern'First;
785 Pat : String := Pattern;
788 Canonical_Case_File_Name (Pat);
789 Iterator.Current_Depth := 1;
791 -- If Directory is unspecified, use the current directory ("./" or ".\")
793 if Directory = "" then
794 Iterator.Dir_Name (1 .. 2) := "." & Directory_Separator;
798 Iterator.Dir_Name (1 .. Directory'Length) := Directory;
799 Iterator.Start := Directory'Length + 1;
800 Canonical_Case_File_Name (Iterator.Dir_Name (1 .. Directory'Length));
802 -- Make sure that the last character is a directory separator
804 if Directory (Directory'Last) /= Directory_Separator then
805 Iterator.Dir_Name (Iterator.Start) := Directory_Separator;
806 Iterator.Start := Iterator.Start + 1;
810 Iterator.Levels (1).Name_Last := Iterator.Start - 1;
812 -- Open the initial Directory, at depth 1
814 GNAT.Directory_Operations.Open
815 (Iterator.Levels (1).Dir, Iterator.Dir_Name (1 .. Iterator.Start - 1));
817 -- If in the current directory and the pattern starts with "./" or ".\",
818 -- drop the "./" or ".\" from the pattern.
820 if Directory = "" and then Pat'Length > 2
821 and then Pat (Pat'First) = '.'
822 and then Pat (Pat'First + 1) = Directory_Separator
824 First := Pat'First + 2;
828 GNAT.Regexp.Compile (Pat (First .. Pat'Last), Basic_Regexp, True);
830 Iterator.Maximum_Depth := 1;
832 -- Maximum_Depth is equal to 1 plus the number of directory separators
835 for Index in First .. Pat'Last loop
836 if Pat (Index) = Directory_Separator then
837 Iterator.Maximum_Depth := Iterator.Maximum_Depth + 1;
838 exit when Iterator.Maximum_Depth = Max_Depth;
845 Section (CL.Argument_Count + 1) := 0;
846 end GNAT.Command_Line;