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-2007, 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
.Unchecked_Deallocation
;
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 Switch_Parameter_Type
is
43 Parameter_With_Optional_Space
, -- ':' in getopt
44 Parameter_With_Space_Or_Equal
, -- '=' in getopt
45 Parameter_No_Space
, -- '!' in getopt
46 Parameter_Optional
); -- '?' in getop
48 procedure Set_Parameter
49 (Variable
: out Parameter_Type
;
53 Extra
: Character := ASCII
.NUL
);
54 pragma Inline
(Set_Parameter
);
55 -- Set the parameter that will be returned by Parameter below
56 -- Parameters need to be defined ???
58 function Goto_Next_Argument_In_Section
(Parser
: Opt_Parser
) return Boolean;
59 -- Go to the next argument on the command line. If we are at the end of
60 -- the current section, we want to make sure there is no other identical
61 -- section on the command line (there might be multiple instances of
62 -- -largs). Returns True iff there is another argument.
64 function Get_File_Names_Case_Sensitive
return Integer;
65 pragma Import
(C
, Get_File_Names_Case_Sensitive
,
66 "__gnat_get_file_names_case_sensitive");
68 File_Names_Case_Sensitive
: constant Boolean :=
69 Get_File_Names_Case_Sensitive
/= 0;
71 procedure Canonical_Case_File_Name
(S
: in out String);
72 -- Given a file name, converts it to canonical case form. For systems where
73 -- file names are case sensitive, this procedure has no effect. If file
74 -- names are not case sensitive (i.e. for example if you have the file
75 -- "xyz.adb", you can refer to it as XYZ.adb or XyZ.AdB), then this call
76 -- converts the given string to canonical all lower case form, so that two
77 -- file names compare equal if they refer to the same file.
79 procedure Internal_Initialize_Option_Scan
81 Switch_Char
: Character;
82 Stop_At_First_Non_Switch
: Boolean;
83 Section_Delimiters
: String);
84 -- Initialize Parser, which must have been allocated already
86 function Argument
(Parser
: Opt_Parser
; Index
: Integer) return String;
87 -- Return the index-th command line argument
89 procedure Find_Longest_Matching_Switch
92 Index_In_Switches
: out Integer;
93 Switch_Length
: out Integer;
94 Param
: out Switch_Parameter_Type
);
95 -- return the Longest switch from Switches that matches at least
96 -- partially Arg. Index_In_Switches is set to 0 if none matches
98 procedure Unchecked_Free
is new Ada
.Unchecked_Deallocation
99 (Argument_List
, Argument_List_Access
);
101 procedure Unchecked_Free
is new Ada
.Unchecked_Deallocation
102 (Command_Line_Configuration_Record
, Command_Line_Configuration
);
104 type Boolean_Chars
is array (Character) of Boolean;
106 procedure Remove
(Line
: in out Argument_List_Access
; Index
: Integer);
107 -- Remove a specific element from Line
110 (Line
: in out Argument_List_Access
;
111 Str
: String_Access
);
112 -- Append a new element to Line
114 function Args_From_Expanded
(Args
: Boolean_Chars
) return String;
115 -- Return the string made of all characters with True in Args
117 type Callback_Procedure
is access procedure (Simple_Switch
: String);
118 procedure For_Each_Simple_Switch
121 Callback
: Callback_Procedure
);
122 -- Breaks Switch into as simple switches as possible (expanding aliases and
123 -- ungrouping common prefixes when possible), and call Callback for each of
126 procedure Group_Switches
128 Result
: Argument_List_Access
;
129 Params
: Argument_List_Access
);
130 -- Group switches with common prefixes whenever possible.
131 -- Once they have been grouped, we also check items for possible aliasing
133 procedure Alias_Switches
135 Result
: Argument_List_Access
;
136 Params
: Argument_List_Access
);
137 -- When possible, replace or more switches by an alias, ie a shorter
143 Substring
: String) return Boolean;
144 -- Return True if the characters starting at Index in Type_Str are
145 -- equivalent to Substring.
151 function Argument
(Parser
: Opt_Parser
; Index
: Integer) return String is
153 if Parser
.Arguments
/= null then
154 return Parser
.Arguments
(Index
+ Parser
.Arguments
'First - 1).all;
156 return CL
.Argument
(Index
);
160 ------------------------------
161 -- Canonical_Case_File_Name --
162 ------------------------------
164 procedure Canonical_Case_File_Name
(S
: in out String) is
166 if not File_Names_Case_Sensitive
then
167 for J
in S
'Range loop
168 if S
(J
) in 'A' .. 'Z' then
169 S
(J
) := Character'Val
170 (Character'Pos (S
(J
)) +
171 Character'Pos ('a') -
172 Character'Pos ('A'));
176 end Canonical_Case_File_Name
;
182 function Expansion
(Iterator
: Expansion_Iterator
) return String is
183 use GNAT
.Directory_Operations
;
184 type Pointer
is access all Expansion_Iterator
;
186 It
: constant Pointer
:= Iterator
'Unrestricted_Access;
187 S
: String (1 .. 1024);
190 Current
: Depth
:= It
.Current_Depth
;
194 -- It is assumed that a directory is opened at the current level.
195 -- Otherwise GNAT.Directory_Operations.Directory_Error will be raised
196 -- at the first call to Read.
199 Read
(It
.Levels
(Current
).Dir
, S
, Last
);
201 -- If we have exhausted the directory, close it and go back one level
204 Close
(It
.Levels
(Current
).Dir
);
206 -- If we are at level 1, we are finished; return an empty string
209 return String'(1 .. 0 => ' ');
211 -- Otherwise continue with the directory at the previous level
213 Current := Current - 1;
214 It.Current_Depth := Current;
217 -- If this is a directory, that is neither "." or "..", attempt to
218 -- go to the next level.
221 (It.Dir_Name (1 .. It.Levels (Current).Name_Last) & S (1 .. Last))
222 and then S (1 .. Last) /= "."
223 and then S (1 .. Last) /= ".."
225 -- We can go to the next level only if we have not reached the
228 if Current < It.Maximum_Depth then
229 NL := It.Levels (Current).Name_Last;
231 -- And if relative path of this new directory is not too long
233 if NL + Last + 1 < Max_Path_Length then
234 Current := Current + 1;
235 It.Current_Depth := Current;
236 It.Dir_Name (NL + 1 .. NL + Last) := S (1 .. Last);
238 It.Dir_Name (NL) := Directory_Separator;
239 It.Levels (Current).Name_Last := NL;
240 Canonical_Case_File_Name (It.Dir_Name (1 .. NL));
242 -- Open the new directory, and read from it
244 GNAT.Directory_Operations.Open
245 (It.Levels (Current).Dir, It.Dir_Name (1 .. NL));
249 -- If not a directory, check the relative path against the pattern
254 It.Dir_Name (It.Start .. It.Levels (Current).Name_Last)
257 Canonical_Case_File_Name (Name);
259 -- If it matches return the relative path
261 if GNAT.Regexp.Match (Name, Iterator.Regexp) then
268 return String'(1 .. 0 => ' ');
276 (Parser
: Opt_Parser
:= Command_Line_Parser
) return String
279 if Parser
.The_Switch
.Extra
= ASCII
.NUL
then
280 return Argument
(Parser
, Parser
.The_Switch
.Arg_Num
)
281 (Parser
.The_Switch
.First
.. Parser
.The_Switch
.Last
);
283 return Parser
.The_Switch
.Extra
284 & Argument
(Parser
, Parser
.The_Switch
.Arg_Num
)
285 (Parser
.The_Switch
.First
.. Parser
.The_Switch
.Last
);
293 function Get_Argument
294 (Do_Expansion
: Boolean := False;
295 Parser
: Opt_Parser
:= Command_Line_Parser
) return String
298 if Parser
.In_Expansion
then
300 S
: constant String := Expansion
(Parser
.Expansion_It
);
302 if S
'Length /= 0 then
305 Parser
.In_Expansion
:= False;
310 if Parser
.Current_Argument
> Parser
.Arg_Count
then
312 -- If this is the first time this function is called
314 if Parser
.Current_Index
= 1 then
315 Parser
.Current_Argument
:= 1;
316 while Parser
.Current_Argument
<= Parser
.Arg_Count
317 and then Parser
.Section
(Parser
.Current_Argument
) /=
318 Parser
.Current_Section
320 Parser
.Current_Argument
:= Parser
.Current_Argument
+ 1;
323 return String'(1 .. 0 => ' ');
326 elsif Parser.Section (Parser.Current_Argument) = 0 then
327 while Parser.Current_Argument <= Parser.Arg_Count
328 and then Parser.Section (Parser.Current_Argument) /=
329 Parser.Current_Section
331 Parser.Current_Argument := Parser.Current_Argument + 1;
335 Parser.Current_Index := Integer'Last;
337 while Parser.Current_Argument <= Parser.Arg_Count
338 and then Parser.Is_Switch (Parser.Current_Argument)
340 Parser.Current_Argument := Parser.Current_Argument + 1;
343 if Parser.Current_Argument > Parser.Arg_Count then
344 return String'(1 .. 0 => ' ');
345 elsif Parser
.Section
(Parser
.Current_Argument
) = 0 then
346 return Get_Argument
(Do_Expansion
);
349 Parser
.Current_Argument
:= Parser
.Current_Argument
+ 1;
351 -- Could it be a file name with wild cards to expand?
355 Arg
: constant String :=
356 Argument
(Parser
, Parser
.Current_Argument
- 1);
361 while Index
<= Arg
'Last loop
363 or else Arg
(Index
) = '?'
364 or else Arg
(Index
) = '['
366 Parser
.In_Expansion
:= True;
367 Start_Expansion
(Parser
.Expansion_It
, Arg
);
368 return Get_Argument
(Do_Expansion
);
376 return Argument
(Parser
, Parser
.Current_Argument
- 1);
379 ----------------------------------
380 -- Find_Longest_Matching_Switch --
381 ----------------------------------
383 procedure Find_Longest_Matching_Switch
386 Index_In_Switches
: out Integer;
387 Switch_Length
: out Integer;
388 Param
: out Switch_Parameter_Type
)
391 Length
: Natural := 1;
392 P
: Switch_Parameter_Type
;
395 Index_In_Switches
:= 0;
398 -- Remove all leading spaces first to make sure that Index points
399 -- at the start of the first switch.
401 Index
:= Switches
'First;
402 while Index
<= Switches
'Last and then Switches
(Index
) = ' ' loop
406 while Index
<= Switches
'Last loop
408 -- Search the length of the parameter at this position in Switches
411 while Length
<= Switches
'Last
412 and then Switches
(Length
) /= ' '
414 Length
:= Length
+ 1;
417 if Length
= Index
+ 1 then
420 case Switches
(Length
- 1) is
422 P
:= Parameter_With_Optional_Space
;
423 Length
:= Length
- 1;
425 P
:= Parameter_With_Space_Or_Equal
;
426 Length
:= Length
- 1;
428 P
:= Parameter_No_Space
;
429 Length
:= Length
- 1;
431 P
:= Parameter_Optional
;
432 Length
:= Length
- 1;
438 -- If it is the one we searched, it may be a candidate
440 if Arg
'First + Length
- 1 - Index
<= Arg
'Last
441 and then Switches
(Index
.. Length
- 1) =
442 Arg
(Arg
'First .. Arg
'First + Length
- 1 - Index
)
443 and then Length
- Index
> Switch_Length
446 Index_In_Switches
:= Index
;
447 Switch_Length
:= Length
- Index
;
450 -- Look for the next switch in Switches
452 while Index
<= Switches
'Last
453 and then Switches
(Index
) /= ' '
460 end Find_Longest_Matching_Switch
;
468 Concatenate
: Boolean := True;
469 Parser
: Opt_Parser
:= Command_Line_Parser
) return Character
472 pragma Unreferenced
(Dummy
);
477 -- If we have finished parsing the current command line item (there
478 -- might be multiple switches in a single item), then go to the next
481 if Parser
.Current_Argument
> Parser
.Arg_Count
482 or else (Parser
.Current_Index
>
483 Argument
(Parser
, Parser
.Current_Argument
)'Last
484 and then not Goto_Next_Argument_In_Section
(Parser
))
489 -- By default, the switch will not have a parameter
491 Parser
.The_Parameter
:=
492 (Integer'Last, Integer'Last, Integer'Last - 1, ASCII
.NUL
);
493 Parser
.The_Separator
:= ASCII
.NUL
;
496 Arg
: constant String :=
497 Argument
(Parser
, Parser
.Current_Argument
);
498 Index_Switches
: Natural := 0;
499 Max_Length
: Natural := 0;
501 Param
: Switch_Parameter_Type
;
503 -- If we are on a new item, test if this might be a switch
505 if Parser
.Current_Index
= Arg
'First then
506 if Arg
(Arg
'First) /= Parser
.Switch_Character
then
508 -- If it isn't a switch, return it immediately. We also know it
509 -- isn't the parameter to a previous switch, since that has
510 -- already been handled
512 if Switches
(Switches
'First) = '*' then
515 Arg_Num
=> Parser
.Current_Argument
,
518 Parser
.Is_Switch
(Parser
.Current_Argument
) := True;
519 Dummy
:= Goto_Next_Argument_In_Section
(Parser
);
523 if Parser
.Stop_At_First
then
524 Parser
.Current_Argument
:= Positive'Last;
527 elsif not Goto_Next_Argument_In_Section
(Parser
) then
531 -- Recurse to get the next switch on the command line
537 -- We are on the first character of a new command line argument,
538 -- which starts with Switch_Character. Further analysis is needed.
540 Parser
.Current_Index
:= Parser
.Current_Index
+ 1;
541 Parser
.Is_Switch
(Parser
.Current_Argument
) := True;
544 Find_Longest_Matching_Switch
545 (Switches
=> Switches
,
546 Arg
=> Arg
(Parser
.Current_Index
.. Arg
'Last),
547 Index_In_Switches
=> Index_Switches
,
548 Switch_Length
=> Max_Length
,
551 -- If switch is not accepted, it is either invalid or is returned
552 -- in the context of '*'.
554 if Index_Switches
= 0 then
556 -- Depending on the value of Concatenate, the full switch is
557 -- a single character or the rest of the argument.
560 End_Index
:= Parser
.Current_Index
;
562 End_Index
:= Arg
'Last;
565 if Switches
(Switches
'First) = '*' then
567 -- Always prepend the switch character, so that users know that
568 -- this comes from a switch on the command line. This is
569 -- especially important when Concatenate is False, since
570 -- otherwise the currrent argument first character is lost.
574 Arg_Num
=> Parser
.Current_Argument
,
575 First
=> Parser
.Current_Index
,
577 Extra
=> Parser
.Switch_Character
);
578 Parser
.Is_Switch
(Parser
.Current_Argument
) := True;
579 Dummy
:= Goto_Next_Argument_In_Section
(Parser
);
585 Arg_Num
=> Parser
.Current_Argument
,
586 First
=> Parser
.Current_Index
,
588 Parser
.Current_Index
:= End_Index
+ 1;
589 raise Invalid_Switch
;
592 End_Index
:= Parser
.Current_Index
+ Max_Length
- 1;
595 Arg_Num
=> Parser
.Current_Argument
,
596 First
=> Parser
.Current_Index
,
600 when Parameter_With_Optional_Space
=>
601 if End_Index
< Arg
'Last then
603 (Parser
.The_Parameter
,
604 Arg_Num
=> Parser
.Current_Argument
,
605 First
=> End_Index
+ 1,
607 Dummy
:= Goto_Next_Argument_In_Section
(Parser
);
609 elsif Parser
.Current_Argument
< Parser
.Arg_Count
610 and then Parser
.Section
(Parser
.Current_Argument
+ 1) /= 0
612 Parser
.Current_Argument
:= Parser
.Current_Argument
+ 1;
613 Parser
.The_Separator
:= ' ';
615 (Parser
.The_Parameter
,
616 Arg_Num
=> Parser
.Current_Argument
,
617 First
=> Argument
(Parser
, Parser
.Current_Argument
)'First,
618 Last
=> Argument
(Parser
, Parser
.Current_Argument
)'Last);
619 Parser
.Is_Switch
(Parser
.Current_Argument
) := True;
620 Dummy
:= Goto_Next_Argument_In_Section
(Parser
);
623 Parser
.Current_Index
:= End_Index
+ 1;
624 raise Invalid_Parameter
;
627 when Parameter_With_Space_Or_Equal
=>
629 -- If the switch is of the form <switch>=xxx
631 if End_Index
< Arg
'Last then
633 if Arg
(End_Index
+ 1) = '='
634 and then End_Index
+ 1 < Arg
'Last
636 Parser
.The_Separator
:= '=';
638 (Parser
.The_Parameter
,
639 Arg_Num
=> Parser
.Current_Argument
,
640 First
=> End_Index
+ 2,
642 Dummy
:= Goto_Next_Argument_In_Section
(Parser
);
644 Parser
.Current_Index
:= End_Index
+ 1;
645 raise Invalid_Parameter
;
648 -- If the switch is of the form <switch> xxx
650 elsif Parser
.Current_Argument
< Parser
.Arg_Count
651 and then Parser
.Section
(Parser
.Current_Argument
+ 1) /= 0
653 Parser
.Current_Argument
:= Parser
.Current_Argument
+ 1;
654 Parser
.The_Separator
:= ' ';
656 (Parser
.The_Parameter
,
657 Arg_Num
=> Parser
.Current_Argument
,
658 First
=> Argument
(Parser
, Parser
.Current_Argument
)'First,
659 Last
=> Argument
(Parser
, Parser
.Current_Argument
)'Last);
660 Parser
.Is_Switch
(Parser
.Current_Argument
) := True;
661 Dummy
:= Goto_Next_Argument_In_Section
(Parser
);
664 Parser
.Current_Index
:= End_Index
+ 1;
665 raise Invalid_Parameter
;
668 when Parameter_No_Space
=>
670 if End_Index
< Arg
'Last then
672 (Parser
.The_Parameter
,
673 Arg_Num
=> Parser
.Current_Argument
,
674 First
=> End_Index
+ 1,
676 Dummy
:= Goto_Next_Argument_In_Section
(Parser
);
679 Parser
.Current_Index
:= End_Index
+ 1;
680 raise Invalid_Parameter
;
683 when Parameter_Optional
=>
685 if End_Index
< Arg
'Last then
687 (Parser
.The_Parameter
,
688 Arg_Num
=> Parser
.Current_Argument
,
689 First
=> End_Index
+ 1,
693 Dummy
:= Goto_Next_Argument_In_Section
(Parser
);
695 when Parameter_None
=>
697 if Concatenate
or else End_Index
= Arg
'Last then
698 Parser
.Current_Index
:= End_Index
+ 1;
701 -- If Concatenate is False and the full argument is not
702 -- recognized as a switch, this is an invalid switch.
704 if Switches
(Switches
'First) = '*' then
707 Arg_Num
=> Parser
.Current_Argument
,
710 Parser
.Is_Switch
(Parser
.Current_Argument
) := True;
711 Dummy
:= Goto_Next_Argument_In_Section
(Parser
);
717 Arg_Num
=> Parser
.Current_Argument
,
718 First
=> Parser
.Current_Index
,
720 Parser
.Current_Index
:= Arg
'Last + 1;
721 raise Invalid_Switch
;
725 return Switches
(Index_Switches
);
729 -----------------------------------
730 -- Goto_Next_Argument_In_Section --
731 -----------------------------------
733 function Goto_Next_Argument_In_Section
734 (Parser
: Opt_Parser
) return Boolean
737 Parser
.Current_Argument
:= Parser
.Current_Argument
+ 1;
739 if Parser
.Current_Argument
> Parser
.Arg_Count
740 or else Parser
.Section
(Parser
.Current_Argument
) = 0
743 Parser
.Current_Argument
:= Parser
.Current_Argument
+ 1;
745 if Parser
.Current_Argument
> Parser
.Arg_Count
then
746 Parser
.Current_Index
:= 1;
750 exit when Parser
.Section
(Parser
.Current_Argument
) =
751 Parser
.Current_Section
;
755 Parser
.Current_Index
:=
756 Argument
(Parser
, Parser
.Current_Argument
)'First;
759 end Goto_Next_Argument_In_Section
;
765 procedure Goto_Section
766 (Name
: String := "";
767 Parser
: Opt_Parser
:= Command_Line_Parser
)
772 Parser
.In_Expansion
:= False;
775 Parser
.Current_Argument
:= 1;
776 Parser
.Current_Index
:= 1;
777 Parser
.Current_Section
:= 1;
782 while Index
<= Parser
.Arg_Count
loop
783 if Parser
.Section
(Index
) = 0
784 and then Argument
(Parser
, Index
) = Parser
.Switch_Character
& Name
786 Parser
.Current_Argument
:= Index
+ 1;
787 Parser
.Current_Index
:= 1;
789 if Parser
.Current_Argument
<= Parser
.Arg_Count
then
790 Parser
.Current_Section
:=
791 Parser
.Section
(Parser
.Current_Argument
);
799 Parser
.Current_Argument
:= Positive'Last;
800 Parser
.Current_Index
:= 2; -- so that Get_Argument returns nothing
803 ----------------------------
804 -- Initialize_Option_Scan --
805 ----------------------------
807 procedure Initialize_Option_Scan
808 (Switch_Char
: Character := '-';
809 Stop_At_First_Non_Switch
: Boolean := False;
810 Section_Delimiters
: String := "")
813 Internal_Initialize_Option_Scan
814 (Parser
=> Command_Line_Parser
,
815 Switch_Char
=> Switch_Char
,
816 Stop_At_First_Non_Switch
=> Stop_At_First_Non_Switch
,
817 Section_Delimiters
=> Section_Delimiters
);
818 end Initialize_Option_Scan
;
820 ----------------------------
821 -- Initialize_Option_Scan --
822 ----------------------------
824 procedure Initialize_Option_Scan
825 (Parser
: out Opt_Parser
;
826 Command_Line
: GNAT
.OS_Lib
.Argument_List_Access
;
827 Switch_Char
: Character := '-';
828 Stop_At_First_Non_Switch
: Boolean := False;
829 Section_Delimiters
: String := "")
834 if Command_Line
= null then
835 Parser
:= new Opt_Parser_Data
(CL
.Argument_Count
);
836 Initialize_Option_Scan
837 (Switch_Char
=> Switch_Char
,
838 Stop_At_First_Non_Switch
=> Stop_At_First_Non_Switch
,
839 Section_Delimiters
=> Section_Delimiters
);
841 Parser
:= new Opt_Parser_Data
(Command_Line
'Length);
842 Parser
.Arguments
:= Command_Line
;
843 Internal_Initialize_Option_Scan
845 Switch_Char
=> Switch_Char
,
846 Stop_At_First_Non_Switch
=> Stop_At_First_Non_Switch
,
847 Section_Delimiters
=> Section_Delimiters
);
849 end Initialize_Option_Scan
;
851 -------------------------------------
852 -- Internal_Initialize_Option_Scan --
853 -------------------------------------
855 procedure Internal_Initialize_Option_Scan
856 (Parser
: Opt_Parser
;
857 Switch_Char
: Character;
858 Stop_At_First_Non_Switch
: Boolean;
859 Section_Delimiters
: String)
861 Section_Num
: Section_Number
;
862 Section_Index
: Integer;
864 Delimiter_Found
: Boolean;
867 pragma Warnings
(Off
, Discard
);
870 Parser
.Current_Argument
:= 0;
871 Parser
.Current_Index
:= 0;
872 Parser
.In_Expansion
:= False;
873 Parser
.Switch_Character
:= Switch_Char
;
874 Parser
.Stop_At_First
:= Stop_At_First_Non_Switch
;
876 -- If we are using sections, we have to preprocess the command line
877 -- to delimit them. A section can be repeated, so we just give each
878 -- item on the command line a section number
881 Section_Index
:= Section_Delimiters
'First;
882 while Section_Index
<= Section_Delimiters
'Last loop
883 Last
:= Section_Index
;
884 while Last
<= Section_Delimiters
'Last
885 and then Section_Delimiters
(Last
) /= ' '
890 Delimiter_Found
:= False;
891 Section_Num
:= Section_Num
+ 1;
893 for Index
in 1 .. Parser
.Arg_Count
loop
894 if Argument
(Parser
, Index
)(1) = Parser
.Switch_Character
896 Argument
(Parser
, Index
) = Parser
.Switch_Character
&
898 (Section_Index
.. Last
- 1)
900 Parser
.Section
(Index
) := 0;
901 Delimiter_Found
:= True;
903 elsif Parser
.Section
(Index
) = 0 then
904 Delimiter_Found
:= False;
906 elsif Delimiter_Found
then
907 Parser
.Section
(Index
) := Section_Num
;
911 Section_Index
:= Last
+ 1;
912 while Section_Index
<= Section_Delimiters
'Last
913 and then Section_Delimiters
(Section_Index
) = ' '
915 Section_Index
:= Section_Index
+ 1;
919 Discard
:= Goto_Next_Argument_In_Section
(Parser
);
920 end Internal_Initialize_Option_Scan
;
927 (Parser
: Opt_Parser
:= Command_Line_Parser
) return String
930 if Parser
.The_Parameter
.First
> Parser
.The_Parameter
.Last
then
931 return String'(1 .. 0 => ' ');
933 return Argument (Parser, Parser.The_Parameter.Arg_Num)
934 (Parser.The_Parameter.First .. Parser.The_Parameter.Last);
943 (Parser : Opt_Parser := Command_Line_Parser) return Character
946 return Parser.The_Separator;
953 procedure Set_Parameter
954 (Variable : out Parameter_Type;
958 Extra : Character := ASCII.NUL)
961 Variable.Arg_Num := Arg_Num;
962 Variable.First := First;
963 Variable.Last := Last;
964 Variable.Extra := Extra;
967 ---------------------
968 -- Start_Expansion --
969 ---------------------
971 procedure Start_Expansion
972 (Iterator : out Expansion_Iterator;
974 Directory : String := "";
975 Basic_Regexp : Boolean := True)
977 Directory_Separator : Character;
978 pragma Import (C, Directory_Separator, "__gnat_dir_separator");
980 First : Positive := Pattern'First;
981 Pat : String := Pattern;
984 Canonical_Case_File_Name (Pat);
985 Iterator.Current_Depth := 1;
987 -- If Directory is unspecified, use the current directory ("./" or ".\")
989 if Directory = "" then
990 Iterator.Dir_Name (1 .. 2) := "." & Directory_Separator;
994 Iterator.Dir_Name (1 .. Directory'Length) := Directory;
995 Iterator.Start := Directory'Length + 1;
996 Canonical_Case_File_Name (Iterator.Dir_Name (1 .. Directory'Length));
998 -- Make sure that the last character is a directory separator
1000 if Directory (Directory'Last) /= Directory_Separator then
1001 Iterator.Dir_Name (Iterator.Start) := Directory_Separator;
1002 Iterator.Start := Iterator.Start + 1;
1006 Iterator.Levels (1).Name_Last := Iterator.Start - 1;
1008 -- Open the initial Directory, at depth 1
1010 GNAT.Directory_Operations.Open
1011 (Iterator.Levels (1).Dir, Iterator.Dir_Name (1 .. Iterator.Start - 1));
1013 -- If in the current directory and the pattern starts with "./" or ".\",
1014 -- drop the "./" or ".\" from the pattern.
1016 if Directory = "" and then Pat'Length > 2
1017 and then Pat (Pat'First) = '.'
1018 and then Pat (Pat'First + 1) = Directory_Separator
1020 First := Pat'First + 2;
1024 GNAT.Regexp.Compile (Pat (First .. Pat'Last), Basic_Regexp, True);
1026 Iterator.Maximum_Depth := 1;
1028 -- Maximum_Depth is equal to 1 plus the number of directory separators
1031 for Index in First .. Pat'Last loop
1032 if Pat (Index) = Directory_Separator then
1033 Iterator.Maximum_Depth := Iterator.Maximum_Depth + 1;
1034 exit when Iterator.Maximum_Depth = Max_Depth;
1037 end Start_Expansion;
1043 procedure Free (Parser : in out Opt_Parser) is
1044 procedure Unchecked_Free is new Ada.Unchecked_Deallocation
1045 (Opt_Parser_Data, Opt_Parser);
1048 and then Parser /= Command_Line_Parser
1050 Free (Parser.Arguments);
1051 Unchecked_Free (Parser);
1055 ------------------------
1056 -- Args_From_Expanded --
1057 ------------------------
1059 function Args_From_Expanded (Args : Boolean_Chars) return String is
1060 Result : String (1 .. Args'Length);
1061 Index : Natural := Result'First;
1064 for A in Args'Range loop
1066 Result (Index) := A;
1071 return Result (1 .. Index - 1);
1072 end Args_From_Expanded;
1078 procedure Define_Alias
1079 (Config : in out Command_Line_Configuration;
1084 if Config = null then
1085 Config := new Command_Line_Configuration_Record;
1088 Append (Config.Aliases, new String'(Switch
));
1089 Append
(Config
.Expansions
, new String'(Expanded));
1096 procedure Define_Prefix
1097 (Config : in out Command_Line_Configuration;
1101 if Config = null then
1102 Config := new Command_Line_Configuration_Record;
1105 Append (Config.Prefixes, new String'(Prefix
));
1108 -----------------------
1109 -- Set_Configuration --
1110 -----------------------
1112 procedure Set_Configuration
1113 (Cmd
: in out Command_Line
;
1114 Config
: Command_Line_Configuration
)
1117 Cmd
.Config
:= Config
;
1118 end Set_Configuration
;
1120 ----------------------
1121 -- Set_Command_Line --
1122 ----------------------
1124 procedure Set_Command_Line
1125 (Cmd
: in out Command_Line
;
1127 Getopt_Description
: String := "";
1128 Switch_Char
: Character := '-')
1130 Tmp
: Argument_List_Access
;
1131 Parser
: Opt_Parser
;
1135 Free
(Cmd
.Expanded
);
1138 if Switches
/= "" then
1139 Tmp
:= Argument_String_To_List
(Switches
);
1140 Initialize_Option_Scan
(Parser
, Tmp
, Switch_Char
);
1144 S
:= Getopt
(Switches
=> "* " & Getopt_Description
,
1145 Concatenate
=> False,
1147 exit when S
= ASCII
.NUL
;
1150 Add_Switch
(Cmd
, Full_Switch
(Parser
), Parameter
(Parser
),
1151 Separator
(Parser
));
1154 (Cmd
, Switch_Char
& Full_Switch
(Parser
),
1155 Parameter
(Parser
), Separator
(Parser
));
1159 when Invalid_Parameter
=>
1160 -- Add it with no parameter, if that's the way the user
1162 Add_Switch
(Cmd
, Switch_Char
& Full_Switch
(Parser
));
1168 end Set_Command_Line
;
1177 Substring
: String) return Boolean is
1179 return Index
+ Substring
'Length - 1 <= Type_Str
'Last
1180 and then Type_Str
(Index
.. Index
+ Substring
'Length - 1) = Substring
;
1183 ----------------------------
1184 -- For_Each_Simple_Switch --
1185 ----------------------------
1187 procedure For_Each_Simple_Switch
1188 (Cmd
: Command_Line
;
1190 Callback
: Callback_Procedure
)
1193 -- Are we adding a switch that can in fact be expanded through aliases ?
1194 -- If yes, we add separately each of its expansion.
1196 -- This takes care of expansions like "-T" -> "-gnatwrs", where the
1197 -- alias and its expansion do not have the same prefix. Given the order
1198 -- in which we do things here, the expansion of the alias will itself
1199 -- be checked for a common prefix and further split into simple switches
1201 if Cmd
.Config
/= null
1202 and then Cmd
.Config
.Aliases
/= null
1204 for A
in Cmd
.Config
.Aliases
'Range loop
1205 if Cmd
.Config
.Aliases
(A
).all = Switch
then
1206 For_Each_Simple_Switch
1207 (Cmd
, Cmd
.Config
.Expansions
(A
).all, Callback
);
1213 -- Are we adding a switch grouping several switches ? If yes, add each
1214 -- of the simple switches instead.
1216 if Cmd
.Config
/= null
1217 and then Cmd
.Config
.Prefixes
/= null
1219 for P
in Cmd
.Config
.Prefixes
'Range loop
1220 if Switch
'Length > Cmd
.Config
.Prefixes
(P
)'Length + 1
1222 (Switch
, Switch
'First, Cmd
.Config
.Prefixes
(P
).all)
1224 -- Alias expansion will be done recursively
1226 for S
in Switch
'First + Cmd
.Config
.Prefixes
(P
)'Length
1229 For_Each_Simple_Switch
1230 (Cmd
, Cmd
.Config
.Prefixes
(P
).all & Switch
(S
), Callback
);
1238 end For_Each_Simple_Switch
;
1244 procedure Add_Switch
1245 (Cmd
: in out Command_Line
;
1247 Parameter
: String := "";
1248 Separator
: Character := ' ')
1250 procedure Add_Simple_Switch
(Simple
: String);
1251 -- Add a new switch that has had all its aliases expanded, and switches
1252 -- ungrouped. We know there is no more aliases in Switches
1254 -----------------------
1255 -- Add_Simple_Switch --
1256 -----------------------
1258 procedure Add_Simple_Switch
(Simple
: String) is
1260 if Cmd
.Expanded
= null then
1261 Cmd
.Expanded
:= new Argument_List
'(1 .. 1 => new String'(Simple
));
1262 if Parameter
= "" then
1263 Cmd
.Params
:= new Argument_List
'(1 .. 1 => null);
1265 Cmd.Params := new Argument_List'
1266 (1 .. 1 => new String'(Separator & Parameter));
1270 -- Do we already have this switch ?
1272 for C in Cmd.Expanded'Range loop
1273 if Cmd.Expanded (C).all = Simple
1275 ((Cmd.Params (C) = null and then Parameter = "")
1277 (Cmd.Params (C) /= null
1278 and then Cmd.Params (C).all = Separator & Parameter))
1284 Append (Cmd.Expanded, new String'(Simple
));
1286 if Parameter
= "" then
1287 Append
(Cmd
.Params
, null);
1289 Append
(Cmd
.Params
, new String'(Separator & Parameter));
1292 end Add_Simple_Switch;
1294 -- Start of processing for Add_Switch
1297 For_Each_Simple_Switch
1298 (Cmd, Switch, Add_Simple_Switch'Unrestricted_Access);
1299 Free (Cmd.Coalesce);
1306 procedure Remove (Line : in out Argument_List_Access; Index : Integer) is
1307 Tmp : Argument_List_Access := Line;
1310 Line := new Argument_List (Tmp'First .. Tmp'Last - 1);
1312 if Index /= Tmp'First then
1313 Line (Tmp'First .. Index - 1) := Tmp (Tmp'First .. Index - 1);
1318 if Index /= Tmp'Last then
1319 Line (Index .. Tmp'Last - 1) := Tmp (Index + 1 .. Tmp'Last);
1322 Unchecked_Free (Tmp);
1330 (Line : in out Argument_List_Access;
1331 Str : String_Access)
1333 Tmp : Argument_List_Access := Line;
1336 Line := new Argument_List (Tmp'First .. Tmp'Last + 1);
1337 Line (Tmp'Range) := Tmp.all;
1338 Unchecked_Free (Tmp);
1340 Line := new Argument_List (1 .. 1);
1343 Line (Line'Last) := Str;
1350 procedure Remove_Switch
1351 (Cmd : in out Command_Line;
1353 Remove_All : Boolean := False)
1355 procedure Remove_Simple_Switch (Simple : String);
1356 -- Removes a simple switch, with no aliasing or grouping
1358 --------------------------
1359 -- Remove_Simple_Switch --
1360 --------------------------
1362 procedure Remove_Simple_Switch (Simple : String) is
1366 if Cmd.Expanded /= null then
1367 C := Cmd.Expanded'First;
1368 while C <= Cmd.Expanded'Last loop
1369 if Cmd.Expanded (C).all = Simple then
1370 Remove (Cmd.Expanded, C);
1371 Remove (Cmd.Params, C);
1373 if not Remove_All then
1382 end Remove_Simple_Switch;
1384 -- Start of processing for Remove_Switch
1387 For_Each_Simple_Switch
1388 (Cmd, Switch, Remove_Simple_Switch'Unrestricted_Access);
1389 Free (Cmd.Coalesce);
1396 procedure Remove_Switch
1397 (Cmd : in out Command_Line;
1401 procedure Remove_Simple_Switch (Simple : String);
1402 -- Removes a simple switch, with no aliasing or grouping
1404 --------------------------
1405 -- Remove_Simple_Switch --
1406 --------------------------
1408 procedure Remove_Simple_Switch (Simple : String) is
1412 if Cmd.Expanded /= null then
1413 C := Cmd.Expanded'First;
1414 while C <= Cmd.Expanded'Last loop
1415 if Cmd.Expanded (C).all = Simple
1417 ((Cmd.Params (C) = null and then Parameter = "")
1419 (Cmd.Params (C) /= null
1422 -- Ignore the separator stored in Parameter
1424 Cmd.Params (C) (Cmd.Params (C)'First + 1
1425 .. Cmd.Params (C)'Last) =
1428 Remove (Cmd.Expanded, C);
1429 Remove (Cmd.Params, C);
1431 -- The switch is necessarily unique by construction of
1441 end Remove_Simple_Switch;
1443 -- Start of processing for Remove_Switch
1446 For_Each_Simple_Switch
1447 (Cmd, Switch, Remove_Simple_Switch'Unrestricted_Access);
1448 Free (Cmd.Coalesce);
1451 --------------------
1452 -- Group_Switches --
1453 --------------------
1455 procedure Group_Switches
1456 (Cmd : Command_Line;
1457 Result : Argument_List_Access;
1458 Params : Argument_List_Access)
1460 type Boolean_Array is array (Result'Range) of Boolean;
1462 Matched : Boolean_Array;
1465 From_Args : Boolean_Chars;
1468 if Cmd.Config = null
1469 or else Cmd.Config.Prefixes = null
1474 for P in Cmd.Config.Prefixes'Range loop
1475 Matched := (others => False);
1478 for C in Result'Range loop
1479 if Result (C) /= null
1480 and then Params (C) = null -- ignored if has a parameter
1482 (Result (C).all, Result (C)'First, Cmd.Config.Prefixes (P).all)
1484 Matched (C) := True;
1490 From_Args := (others => False);
1493 for M in Matched'Range loop
1499 for A in Result (M)'First + Cmd.Config.Prefixes (P)'Length
1502 From_Args (Result (M)(A)) := True;
1508 Result (First) := new String'
1509 (Cmd
.Config
.Prefixes
(P
).all & Args_From_Expanded
(From_Args
));
1514 --------------------
1515 -- Alias_Switches --
1516 --------------------
1518 procedure Alias_Switches
1519 (Cmd
: Command_Line
;
1520 Result
: Argument_List_Access
;
1521 Params
: Argument_List_Access
)
1526 procedure Check_Cb
(Switch
: String);
1527 -- Comment required ???
1529 procedure Remove_Cb
(Switch
: String);
1530 -- Comment required ???
1536 procedure Check_Cb
(Switch
: String) is
1539 for E
in Result
'Range loop
1540 if Result
(E
) /= null
1541 and then Params
(E
) = null -- Ignore if has a param
1542 and then Result
(E
).all = Switch
1556 procedure Remove_Cb
(Switch
: String) is
1558 for E
in Result
'Range loop
1559 if Result
(E
) /= null and then Result
(E
).all = Switch
then
1569 -- Start of processing for Alias_Switches
1572 if Cmd
.Config
= null
1573 or else Cmd
.Config
.Aliases
= null
1578 for A
in Cmd
.Config
.Aliases
'Range loop
1580 -- Compute the various simple switches that make up the alias. We
1581 -- split the expansion into as many simple switches as possible, and
1582 -- then check whether the expanded command line has all of them.
1585 For_Each_Simple_Switch
1586 (Cmd
, Cmd
.Config
.Expansions
(A
).all,
1587 Check_Cb
'Unrestricted_Access);
1590 First
:= Integer'Last;
1591 For_Each_Simple_Switch
1592 (Cmd
, Cmd
.Config
.Expansions
(A
).all,
1593 Remove_Cb
'Unrestricted_Access);
1594 Result
(First
) := new String'(Cmd.Config.Aliases (A).all);
1604 (Cmd : in out Command_Line;
1605 Iter : in out Command_Line_Iterator;
1609 if Cmd.Expanded = null then
1614 -- Coalesce the switches as much as possible
1617 and then Cmd.Coalesce = null
1619 Cmd.Coalesce := new Argument_List (Cmd.Expanded'Range);
1620 for E in Cmd.Expanded'Range loop
1621 Cmd.Coalesce (E) := new String'(Cmd
.Expanded
(E
).all);
1624 -- Not a clone, since we will not modify the parameters anyway
1626 Cmd
.Coalesce_Params
:= Cmd
.Params
;
1627 Alias_Switches
(Cmd
, Cmd
.Coalesce
, Cmd
.Params
);
1628 Group_Switches
(Cmd
, Cmd
.Coalesce
, Cmd
.Params
);
1632 Iter
.List
:= Cmd
.Expanded
;
1633 Iter
.Params
:= Cmd
.Params
;
1635 Iter
.List
:= Cmd
.Coalesce
;
1636 Iter
.Params
:= Cmd
.Coalesce_Params
;
1639 if Iter
.List
= null then
1640 Iter
.Current
:= Integer'Last;
1642 Iter
.Current
:= Iter
.List
'First;
1643 while Iter
.Current
<= Iter
.List
'Last
1644 and then Iter
.List
(Iter
.Current
) = null
1646 Iter
.Current
:= Iter
.Current
+ 1;
1651 --------------------
1652 -- Current_Switch --
1653 --------------------
1655 function Current_Switch
(Iter
: Command_Line_Iterator
) return String is
1657 return Iter
.List
(Iter
.Current
).all;
1660 -----------------------
1661 -- Current_Separator --
1662 -----------------------
1664 function Current_Separator
(Iter
: Command_Line_Iterator
) return String is
1666 if Iter
.Params
= null
1667 or else Iter
.Current
> Iter
.Params
'Last
1668 or else Iter
.Params
(Iter
.Current
) = null
1674 Sep
: constant Character :=
1675 Iter
.Params
(Iter
.Current
) (Iter
.Params
(Iter
.Current
)'First);
1677 if Sep
= ASCII
.NUL
then
1684 end Current_Separator
;
1686 -----------------------
1687 -- Current_Parameter --
1688 -----------------------
1690 function Current_Parameter
(Iter
: Command_Line_Iterator
) return String is
1692 if Iter
.Params
= null
1693 or else Iter
.Current
> Iter
.Params
'Last
1694 or else Iter
.Params
(Iter
.Current
) = null
1700 P
: constant String := Iter
.Params
(Iter
.Current
).all;
1705 return P
(P
'First + 1 .. P
'Last);
1708 end Current_Parameter
;
1714 function Has_More
(Iter
: Command_Line_Iterator
) return Boolean is
1716 return Iter
.List
/= null and then Iter
.Current
<= Iter
.List
'Last;
1723 procedure Next
(Iter
: in out Command_Line_Iterator
) is
1725 Iter
.Current
:= Iter
.Current
+ 1;
1726 while Iter
.Current
<= Iter
.List
'Last
1727 and then Iter
.List
(Iter
.Current
) = null
1729 Iter
.Current
:= Iter
.Current
+ 1;
1737 procedure Free
(Config
: in out Command_Line_Configuration
) is
1739 if Config
/= null then
1740 Free
(Config
.Aliases
);
1741 Free
(Config
.Expansions
);
1742 Free
(Config
.Prefixes
);
1743 Unchecked_Free
(Config
);
1751 procedure Free
(Cmd
: in out Command_Line
) is
1753 Free
(Cmd
.Expanded
);
1754 Free
(Cmd
.Coalesce
);
1758 end GNAT
.Command_Line
;