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-2008, 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 Ada
.Strings
.Unbounded
;
37 with GNAT
.OS_Lib
; use GNAT
.OS_Lib
;
39 package body GNAT
.Command_Line
is
41 package CL
renames Ada
.Command_Line
;
43 type Switch_Parameter_Type
is
45 Parameter_With_Optional_Space
, -- ':' in getopt
46 Parameter_With_Space_Or_Equal
, -- '=' in getopt
47 Parameter_No_Space
, -- '!' in getopt
48 Parameter_Optional
); -- '?' in getopt
50 procedure Set_Parameter
51 (Variable
: out Parameter_Type
;
55 Extra
: Character := ASCII
.NUL
);
56 pragma Inline
(Set_Parameter
);
57 -- Set the parameter that will be returned by Parameter below
58 -- Parameters need to be defined ???
60 function Goto_Next_Argument_In_Section
(Parser
: Opt_Parser
) return Boolean;
61 -- Go to the next argument on the command line. If we are at the end of
62 -- the current section, we want to make sure there is no other identical
63 -- section on the command line (there might be multiple instances of
64 -- -largs). Returns True iff there is another argument.
66 function Get_File_Names_Case_Sensitive
return Integer;
67 pragma Import
(C
, Get_File_Names_Case_Sensitive
,
68 "__gnat_get_file_names_case_sensitive");
70 File_Names_Case_Sensitive
: constant Boolean :=
71 Get_File_Names_Case_Sensitive
/= 0;
73 procedure Canonical_Case_File_Name
(S
: in out String);
74 -- Given a file name, converts it to canonical case form. For systems where
75 -- file names are case sensitive, this procedure has no effect. If file
76 -- names are not case sensitive (i.e. for example if you have the file
77 -- "xyz.adb", you can refer to it as XYZ.adb or XyZ.AdB), then this call
78 -- converts the given string to canonical all lower case form, so that two
79 -- file names compare equal if they refer to the same file.
81 procedure Internal_Initialize_Option_Scan
83 Switch_Char
: Character;
84 Stop_At_First_Non_Switch
: Boolean;
85 Section_Delimiters
: String);
86 -- Initialize Parser, which must have been allocated already
88 function Argument
(Parser
: Opt_Parser
; Index
: Integer) return String;
89 -- Return the index-th command line argument
91 procedure Find_Longest_Matching_Switch
94 Index_In_Switches
: out Integer;
95 Switch_Length
: out Integer;
96 Param
: out Switch_Parameter_Type
);
97 -- return the Longest switch from Switches that matches at least
98 -- partially Arg. Index_In_Switches is set to 0 if none matches
100 procedure Unchecked_Free
is new Ada
.Unchecked_Deallocation
101 (Argument_List
, Argument_List_Access
);
103 procedure Unchecked_Free
is new Ada
.Unchecked_Deallocation
104 (Command_Line_Configuration_Record
, Command_Line_Configuration
);
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
;
112 Before
: Boolean := False);
113 -- Add a new element to Line. If Before is True, the item is inserted at
114 -- the beginning, else it is appended.
116 function Can_Have_Parameter
(S
: String) return Boolean;
117 -- True if S can have a parameter.
119 function Require_Parameter
(S
: String) return Boolean;
120 -- True if S requires a parameter.
122 function Actual_Switch
(S
: String) return String;
123 -- Remove any possible trailing '!', ':', '?' and '='
126 with procedure Callback
(Simple_Switch
: String; Parameter
: String);
127 procedure For_Each_Simple_Switch
130 Parameter
: String := "";
131 Unalias
: Boolean := True);
132 -- Breaks Switch into as simple switches as possible (expanding aliases and
133 -- ungrouping common prefixes when possible), and call Callback for each of
136 procedure Sort_Sections
137 (Line
: GNAT
.OS_Lib
.Argument_List_Access
;
138 Sections
: GNAT
.OS_Lib
.Argument_List_Access
;
139 Params
: GNAT
.OS_Lib
.Argument_List_Access
);
140 -- Reorder the command line switches so that the switches belonging to a
141 -- section are grouped together.
143 procedure Group_Switches
145 Result
: Argument_List_Access
;
146 Sections
: Argument_List_Access
;
147 Params
: Argument_List_Access
);
148 -- Group switches with common prefixes whenever possible. Once they have
149 -- been grouped, we also check items for possible aliasing.
151 procedure Alias_Switches
153 Result
: Argument_List_Access
;
154 Params
: Argument_List_Access
);
155 -- When possible, replace one or more switches by an alias, i.e. a shorter
161 Substring
: String) return Boolean;
162 -- Return True if the characters starting at Index in Type_Str are
163 -- equivalent to Substring.
169 function Argument
(Parser
: Opt_Parser
; Index
: Integer) return String is
171 if Parser
.Arguments
/= null then
172 return Parser
.Arguments
(Index
+ Parser
.Arguments
'First - 1).all;
174 return CL
.Argument
(Index
);
178 ------------------------------
179 -- Canonical_Case_File_Name --
180 ------------------------------
182 procedure Canonical_Case_File_Name
(S
: in out String) is
184 if not File_Names_Case_Sensitive
then
185 for J
in S
'Range loop
186 if S
(J
) in 'A' .. 'Z' then
187 S
(J
) := Character'Val
188 (Character'Pos (S
(J
)) +
189 Character'Pos ('a') -
190 Character'Pos ('A'));
194 end Canonical_Case_File_Name
;
200 function Expansion
(Iterator
: Expansion_Iterator
) return String is
201 use GNAT
.Directory_Operations
;
202 type Pointer
is access all Expansion_Iterator
;
204 It
: constant Pointer
:= Iterator
'Unrestricted_Access;
205 S
: String (1 .. 1024);
208 Current
: Depth
:= It
.Current_Depth
;
212 -- It is assumed that a directory is opened at the current level.
213 -- Otherwise GNAT.Directory_Operations.Directory_Error will be raised
214 -- at the first call to Read.
217 Read
(It
.Levels
(Current
).Dir
, S
, Last
);
219 -- If we have exhausted the directory, close it and go back one level
222 Close
(It
.Levels
(Current
).Dir
);
224 -- If we are at level 1, we are finished; return an empty string
227 return String'(1 .. 0 => ' ');
229 -- Otherwise continue with the directory at the previous level
231 Current := Current - 1;
232 It.Current_Depth := Current;
235 -- If this is a directory, that is neither "." or "..", attempt to
236 -- go to the next level.
239 (It.Dir_Name (1 .. It.Levels (Current).Name_Last) & S (1 .. Last))
240 and then S (1 .. Last) /= "."
241 and then S (1 .. Last) /= ".."
243 -- We can go to the next level only if we have not reached the
246 if Current < It.Maximum_Depth then
247 NL := It.Levels (Current).Name_Last;
249 -- And if relative path of this new directory is not too long
251 if NL + Last + 1 < Max_Path_Length then
252 Current := Current + 1;
253 It.Current_Depth := Current;
254 It.Dir_Name (NL + 1 .. NL + Last) := S (1 .. Last);
256 It.Dir_Name (NL) := Directory_Separator;
257 It.Levels (Current).Name_Last := NL;
258 Canonical_Case_File_Name (It.Dir_Name (1 .. NL));
260 -- Open the new directory, and read from it
262 GNAT.Directory_Operations.Open
263 (It.Levels (Current).Dir, It.Dir_Name (1 .. NL));
267 -- If not a directory, check the relative path against the pattern
272 It.Dir_Name (It.Start .. It.Levels (Current).Name_Last)
275 Canonical_Case_File_Name (Name);
277 -- If it matches return the relative path
279 if GNAT.Regexp.Match (Name, Iterator.Regexp) then
292 (Parser : Opt_Parser := Command_Line_Parser) return String
295 if Parser.The_Switch.Extra = ASCII.NUL then
296 return Argument (Parser, Parser.The_Switch.Arg_Num)
297 (Parser.The_Switch.First .. Parser.The_Switch.Last);
299 return Parser.The_Switch.Extra
300 & Argument (Parser, Parser.The_Switch.Arg_Num)
301 (Parser.The_Switch.First .. Parser.The_Switch.Last);
309 function Get_Argument
310 (Do_Expansion : Boolean := False;
311 Parser : Opt_Parser := Command_Line_Parser) return String
314 if Parser.In_Expansion then
316 S : constant String := Expansion (Parser.Expansion_It);
318 if S'Length /= 0 then
321 Parser.In_Expansion := False;
326 if Parser.Current_Argument > Parser.Arg_Count then
328 -- If this is the first time this function is called
330 if Parser.Current_Index = 1 then
331 Parser.Current_Argument := 1;
332 while Parser.Current_Argument <= Parser.Arg_Count
333 and then Parser.Section (Parser.Current_Argument) /=
334 Parser.Current_Section
336 Parser.Current_Argument := Parser.Current_Argument + 1;
339 return String'(1 .. 0 => ' ');
342 elsif Parser
.Section
(Parser
.Current_Argument
) = 0 then
343 while Parser
.Current_Argument
<= Parser
.Arg_Count
344 and then Parser
.Section
(Parser
.Current_Argument
) /=
345 Parser
.Current_Section
347 Parser
.Current_Argument
:= Parser
.Current_Argument
+ 1;
351 Parser
.Current_Index
:= Integer'Last;
353 while Parser
.Current_Argument
<= Parser
.Arg_Count
354 and then Parser
.Is_Switch
(Parser
.Current_Argument
)
356 Parser
.Current_Argument
:= Parser
.Current_Argument
+ 1;
359 if Parser
.Current_Argument
> Parser
.Arg_Count
then
360 return String'(1 .. 0 => ' ');
361 elsif Parser.Section (Parser.Current_Argument) = 0 then
362 return Get_Argument (Do_Expansion);
365 Parser.Current_Argument := Parser.Current_Argument + 1;
367 -- Could it be a file name with wild cards to expand?
371 Arg : constant String :=
372 Argument (Parser, Parser.Current_Argument - 1);
377 while Index <= Arg'Last loop
379 or else Arg (Index) = '?
'
380 or else Arg (Index) = '['
382 Parser.In_Expansion := True;
383 Start_Expansion (Parser.Expansion_It, Arg);
384 return Get_Argument (Do_Expansion);
392 return Argument (Parser, Parser.Current_Argument - 1);
395 ----------------------------------
396 -- Find_Longest_Matching_Switch --
397 ----------------------------------
399 procedure Find_Longest_Matching_Switch
402 Index_In_Switches : out Integer;
403 Switch_Length : out Integer;
404 Param : out Switch_Parameter_Type)
407 Length : Natural := 1;
408 P : Switch_Parameter_Type;
411 Index_In_Switches := 0;
414 -- Remove all leading spaces first to make sure that Index points
415 -- at the start of the first switch.
417 Index := Switches'First;
418 while Index <= Switches'Last and then Switches (Index) = ' ' loop
422 while Index <= Switches'Last loop
424 -- Search the length of the parameter at this position in Switches
427 while Length <= Switches'Last
428 and then Switches (Length) /= ' '
430 Length := Length + 1;
433 if Length = Index + 1 then
436 case Switches (Length - 1) is
438 P := Parameter_With_Optional_Space;
439 Length := Length - 1;
441 P := Parameter_With_Space_Or_Equal;
442 Length := Length - 1;
444 P := Parameter_No_Space;
445 Length := Length - 1;
447 P := Parameter_Optional;
448 Length := Length - 1;
454 -- If it is the one we searched, it may be a candidate
456 if Arg'First + Length - 1 - Index <= Arg'Last
457 and then Switches (Index .. Length - 1) =
458 Arg (Arg'First .. Arg'First + Length - 1 - Index)
459 and then Length - Index > Switch_Length
462 Index_In_Switches := Index;
463 Switch_Length := Length - Index;
466 -- Look for the next switch in Switches
468 while Index <= Switches'Last
469 and then Switches (Index) /= ' '
476 end Find_Longest_Matching_Switch;
484 Concatenate : Boolean := True;
485 Parser : Opt_Parser := Command_Line_Parser) return Character
488 pragma Unreferenced (Dummy);
493 -- If we have finished parsing the current command line item (there
494 -- might be multiple switches in a single item), then go to the next
497 if Parser.Current_Argument > Parser.Arg_Count
498 or else (Parser.Current_Index >
499 Argument (Parser, Parser.Current_Argument)'Last
500 and then not Goto_Next_Argument_In_Section (Parser))
505 -- By default, the switch will not have a parameter
507 Parser.The_Parameter :=
508 (Integer'Last, Integer'Last, Integer'Last - 1, ASCII.NUL);
509 Parser.The_Separator := ASCII.NUL;
512 Arg : constant String :=
513 Argument (Parser, Parser.Current_Argument);
514 Index_Switches : Natural := 0;
515 Max_Length : Natural := 0;
517 Param : Switch_Parameter_Type;
519 -- If we are on a new item, test if this might be a switch
521 if Parser.Current_Index = Arg'First then
522 if Arg (Arg'First) /= Parser.Switch_Character then
524 -- If it isn't a switch, return it immediately. We also know it
525 -- isn't the parameter to a previous switch, since that has
526 -- already been handled
528 if Switches (Switches'First) = '*' then
531 Arg_Num => Parser.Current_Argument,
534 Parser.Is_Switch (Parser.Current_Argument) := True;
535 Dummy := Goto_Next_Argument_In_Section (Parser);
539 if Parser.Stop_At_First then
540 Parser.Current_Argument := Positive'Last;
543 elsif not Goto_Next_Argument_In_Section (Parser) then
547 -- Recurse to get the next switch on the command line
553 -- We are on the first character of a new command line argument,
554 -- which starts with Switch_Character. Further analysis is needed.
556 Parser.Current_Index := Parser.Current_Index + 1;
557 Parser.Is_Switch (Parser.Current_Argument) := True;
560 Find_Longest_Matching_Switch
561 (Switches => Switches,
562 Arg => Arg (Parser.Current_Index .. Arg'Last),
563 Index_In_Switches => Index_Switches,
564 Switch_Length => Max_Length,
567 -- If switch is not accepted, it is either invalid or is returned
568 -- in the context of '*'.
570 if Index_Switches = 0 then
572 -- Depending on the value of Concatenate, the full switch is
573 -- a single character or the rest of the argument.
576 End_Index := Parser.Current_Index;
578 End_Index := Arg'Last;
581 if Switches (Switches'First) = '*' then
583 -- Always prepend the switch character, so that users know that
584 -- this comes from a switch on the command line. This is
585 -- especially important when Concatenate is False, since
586 -- otherwise the current argument first character is lost.
590 Arg_Num => Parser.Current_Argument,
591 First => Parser.Current_Index,
593 Extra => Parser.Switch_Character);
594 Parser.Is_Switch (Parser.Current_Argument) := True;
595 Dummy := Goto_Next_Argument_In_Section (Parser);
601 Arg_Num => Parser.Current_Argument,
602 First => Parser.Current_Index,
604 Parser.Current_Index := End_Index + 1;
605 raise Invalid_Switch;
608 End_Index := Parser.Current_Index + Max_Length - 1;
611 Arg_Num => Parser.Current_Argument,
612 First => Parser.Current_Index,
616 when Parameter_With_Optional_Space =>
617 if End_Index < Arg'Last then
619 (Parser.The_Parameter,
620 Arg_Num => Parser.Current_Argument,
621 First => End_Index + 1,
623 Dummy := Goto_Next_Argument_In_Section (Parser);
625 elsif Parser.Current_Argument < Parser.Arg_Count
626 and then Parser.Section (Parser.Current_Argument + 1) /= 0
628 Parser.Current_Argument := Parser.Current_Argument + 1;
629 Parser.The_Separator := ' ';
631 (Parser.The_Parameter,
632 Arg_Num => Parser.Current_Argument,
633 First => Argument (Parser, Parser.Current_Argument)'First,
634 Last => Argument (Parser, Parser.Current_Argument)'Last);
635 Parser.Is_Switch (Parser.Current_Argument) := True;
636 Dummy := Goto_Next_Argument_In_Section (Parser);
639 Parser.Current_Index := End_Index + 1;
640 raise Invalid_Parameter;
643 when Parameter_With_Space_Or_Equal =>
645 -- If the switch is of the form <switch>=xxx
647 if End_Index < Arg'Last then
649 if Arg (End_Index + 1) = '='
650 and then End_Index + 1 < Arg'Last
652 Parser.The_Separator := '=';
654 (Parser.The_Parameter,
655 Arg_Num => Parser.Current_Argument,
656 First => End_Index + 2,
658 Dummy := Goto_Next_Argument_In_Section (Parser);
660 Parser.Current_Index := End_Index + 1;
661 raise Invalid_Parameter;
664 -- If the switch is of the form <switch> xxx
666 elsif Parser.Current_Argument < Parser.Arg_Count
667 and then Parser.Section (Parser.Current_Argument + 1) /= 0
669 Parser.Current_Argument := Parser.Current_Argument + 1;
670 Parser.The_Separator := ' ';
672 (Parser.The_Parameter,
673 Arg_Num => Parser.Current_Argument,
674 First => Argument (Parser, Parser.Current_Argument)'First,
675 Last => Argument (Parser, Parser.Current_Argument)'Last);
676 Parser.Is_Switch (Parser.Current_Argument) := True;
677 Dummy := Goto_Next_Argument_In_Section (Parser);
680 Parser.Current_Index := End_Index + 1;
681 raise Invalid_Parameter;
684 when Parameter_No_Space =>
686 if End_Index < Arg'Last then
688 (Parser.The_Parameter,
689 Arg_Num => Parser.Current_Argument,
690 First => End_Index + 1,
692 Dummy := Goto_Next_Argument_In_Section (Parser);
695 Parser.Current_Index := End_Index + 1;
696 raise Invalid_Parameter;
699 when Parameter_Optional =>
701 if End_Index < Arg'Last then
703 (Parser.The_Parameter,
704 Arg_Num => Parser.Current_Argument,
705 First => End_Index + 1,
709 Dummy := Goto_Next_Argument_In_Section (Parser);
711 when Parameter_None =>
713 if Concatenate or else End_Index = Arg'Last then
714 Parser.Current_Index := End_Index + 1;
717 -- If Concatenate is False and the full argument is not
718 -- recognized as a switch, this is an invalid switch.
720 if Switches (Switches'First) = '*' then
723 Arg_Num => Parser.Current_Argument,
726 Parser.Is_Switch (Parser.Current_Argument) := True;
727 Dummy := Goto_Next_Argument_In_Section (Parser);
733 Arg_Num => Parser.Current_Argument,
734 First => Parser.Current_Index,
736 Parser.Current_Index := Arg'Last + 1;
737 raise Invalid_Switch;
741 return Switches (Index_Switches);
745 -----------------------------------
746 -- Goto_Next_Argument_In_Section --
747 -----------------------------------
749 function Goto_Next_Argument_In_Section
750 (Parser : Opt_Parser) return Boolean
753 Parser.Current_Argument := Parser.Current_Argument + 1;
755 if Parser.Current_Argument > Parser.Arg_Count
756 or else Parser.Section (Parser.Current_Argument) = 0
759 Parser.Current_Argument := Parser.Current_Argument + 1;
761 if Parser.Current_Argument > Parser.Arg_Count then
762 Parser.Current_Index := 1;
766 exit when Parser.Section (Parser.Current_Argument) =
767 Parser.Current_Section;
771 Parser.Current_Index :=
772 Argument (Parser, Parser.Current_Argument)'First;
775 end Goto_Next_Argument_In_Section;
781 procedure Goto_Section
782 (Name : String := "";
783 Parser : Opt_Parser := Command_Line_Parser)
788 Parser.In_Expansion := False;
791 Parser.Current_Argument := 1;
792 Parser.Current_Index := 1;
793 Parser.Current_Section := 1;
798 while Index <= Parser.Arg_Count loop
799 if Parser.Section (Index) = 0
800 and then Argument (Parser, Index) = Parser.Switch_Character & Name
802 Parser.Current_Argument := Index + 1;
803 Parser.Current_Index := 1;
805 if Parser.Current_Argument <= Parser.Arg_Count then
806 Parser.Current_Section :=
807 Parser.Section (Parser.Current_Argument);
815 Parser.Current_Argument := Positive'Last;
816 Parser.Current_Index := 2; -- so that Get_Argument returns nothing
819 ----------------------------
820 -- Initialize_Option_Scan --
821 ----------------------------
823 procedure Initialize_Option_Scan
824 (Switch_Char : Character := '-';
825 Stop_At_First_Non_Switch : Boolean := False;
826 Section_Delimiters : String := "")
829 Internal_Initialize_Option_Scan
830 (Parser => Command_Line_Parser,
831 Switch_Char => Switch_Char,
832 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
833 Section_Delimiters => Section_Delimiters);
834 end Initialize_Option_Scan;
836 ----------------------------
837 -- Initialize_Option_Scan --
838 ----------------------------
840 procedure Initialize_Option_Scan
841 (Parser : out Opt_Parser;
842 Command_Line : GNAT.OS_Lib.Argument_List_Access;
843 Switch_Char : Character := '-';
844 Stop_At_First_Non_Switch : Boolean := False;
845 Section_Delimiters : String := "")
850 if Command_Line = null then
851 Parser := new Opt_Parser_Data (CL.Argument_Count);
852 Initialize_Option_Scan
853 (Switch_Char => Switch_Char,
854 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
855 Section_Delimiters => Section_Delimiters);
857 Parser := new Opt_Parser_Data (Command_Line'Length);
858 Parser.Arguments := Command_Line;
859 Internal_Initialize_Option_Scan
861 Switch_Char => Switch_Char,
862 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
863 Section_Delimiters => Section_Delimiters);
865 end Initialize_Option_Scan;
867 -------------------------------------
868 -- Internal_Initialize_Option_Scan --
869 -------------------------------------
871 procedure Internal_Initialize_Option_Scan
872 (Parser : Opt_Parser;
873 Switch_Char : Character;
874 Stop_At_First_Non_Switch : Boolean;
875 Section_Delimiters : String)
877 Section_Num : Section_Number;
878 Section_Index : Integer;
880 Delimiter_Found : Boolean;
883 pragma Warnings (Off, Discard);
886 Parser.Current_Argument := 0;
887 Parser.Current_Index := 0;
888 Parser.In_Expansion := False;
889 Parser.Switch_Character := Switch_Char;
890 Parser.Stop_At_First := Stop_At_First_Non_Switch;
892 -- If we are using sections, we have to preprocess the command line
893 -- to delimit them. A section can be repeated, so we just give each
894 -- item on the command line a section number
897 Section_Index := Section_Delimiters'First;
898 while Section_Index <= Section_Delimiters'Last loop
899 Last := Section_Index;
900 while Last <= Section_Delimiters'Last
901 and then Section_Delimiters (Last) /= ' '
906 Delimiter_Found := False;
907 Section_Num := Section_Num + 1;
909 for Index in 1 .. Parser.Arg_Count loop
910 if Argument (Parser, Index)(1) = Parser.Switch_Character
912 Argument (Parser, Index) = Parser.Switch_Character &
914 (Section_Index .. Last - 1)
916 Parser.Section (Index) := 0;
917 Delimiter_Found := True;
919 elsif Parser.Section (Index) = 0 then
920 Delimiter_Found := False;
922 elsif Delimiter_Found then
923 Parser.Section (Index) := Section_Num;
927 Section_Index := Last + 1;
928 while Section_Index <= Section_Delimiters'Last
929 and then Section_Delimiters (Section_Index) = ' '
931 Section_Index := Section_Index + 1;
935 Discard := Goto_Next_Argument_In_Section (Parser);
936 end Internal_Initialize_Option_Scan;
943 (Parser : Opt_Parser := Command_Line_Parser) return String
946 if Parser.The_Parameter.First > Parser.The_Parameter.Last then
947 return String'(1 .. 0 => ' ');
949 return Argument
(Parser
, Parser
.The_Parameter
.Arg_Num
)
950 (Parser
.The_Parameter
.First
.. Parser
.The_Parameter
.Last
);
959 (Parser
: Opt_Parser
:= Command_Line_Parser
) return Character
962 return Parser
.The_Separator
;
969 procedure Set_Parameter
970 (Variable
: out Parameter_Type
;
974 Extra
: Character := ASCII
.NUL
)
977 Variable
.Arg_Num
:= Arg_Num
;
978 Variable
.First
:= First
;
979 Variable
.Last
:= Last
;
980 Variable
.Extra
:= Extra
;
983 ---------------------
984 -- Start_Expansion --
985 ---------------------
987 procedure Start_Expansion
988 (Iterator
: out Expansion_Iterator
;
990 Directory
: String := "";
991 Basic_Regexp
: Boolean := True)
993 Directory_Separator
: Character;
994 pragma Import
(C
, Directory_Separator
, "__gnat_dir_separator");
996 First
: Positive := Pattern
'First;
997 Pat
: String := Pattern
;
1000 Canonical_Case_File_Name
(Pat
);
1001 Iterator
.Current_Depth
:= 1;
1003 -- If Directory is unspecified, use the current directory ("./" or ".\")
1005 if Directory
= "" then
1006 Iterator
.Dir_Name
(1 .. 2) := "." & Directory_Separator
;
1007 Iterator
.Start
:= 3;
1010 Iterator
.Dir_Name
(1 .. Directory
'Length) := Directory
;
1011 Iterator
.Start
:= Directory
'Length + 1;
1012 Canonical_Case_File_Name
(Iterator
.Dir_Name
(1 .. Directory
'Length));
1014 -- Make sure that the last character is a directory separator
1016 if Directory
(Directory
'Last) /= Directory_Separator
then
1017 Iterator
.Dir_Name
(Iterator
.Start
) := Directory_Separator
;
1018 Iterator
.Start
:= Iterator
.Start
+ 1;
1022 Iterator
.Levels
(1).Name_Last
:= Iterator
.Start
- 1;
1024 -- Open the initial Directory, at depth 1
1026 GNAT
.Directory_Operations
.Open
1027 (Iterator
.Levels
(1).Dir
, Iterator
.Dir_Name
(1 .. Iterator
.Start
- 1));
1029 -- If in the current directory and the pattern starts with "./" or ".\",
1030 -- drop the "./" or ".\" from the pattern.
1032 if Directory
= "" and then Pat
'Length > 2
1033 and then Pat
(Pat
'First) = '.'
1034 and then Pat
(Pat
'First + 1) = Directory_Separator
1036 First
:= Pat
'First + 2;
1040 GNAT
.Regexp
.Compile
(Pat
(First
.. Pat
'Last), Basic_Regexp
, True);
1042 Iterator
.Maximum_Depth
:= 1;
1044 -- Maximum_Depth is equal to 1 plus the number of directory separators
1047 for Index
in First
.. Pat
'Last loop
1048 if Pat
(Index
) = Directory_Separator
then
1049 Iterator
.Maximum_Depth
:= Iterator
.Maximum_Depth
+ 1;
1050 exit when Iterator
.Maximum_Depth
= Max_Depth
;
1053 end Start_Expansion
;
1059 procedure Free
(Parser
: in out Opt_Parser
) is
1060 procedure Unchecked_Free
is new Ada
.Unchecked_Deallocation
1061 (Opt_Parser_Data
, Opt_Parser
);
1064 and then Parser
/= Command_Line_Parser
1066 Free
(Parser
.Arguments
);
1067 Unchecked_Free
(Parser
);
1075 procedure Define_Alias
1076 (Config
: in out Command_Line_Configuration
;
1081 if Config
= null then
1082 Config
:= new Command_Line_Configuration_Record
;
1085 Add
(Config
.Aliases
, new String'(Switch));
1086 Add (Config.Expansions, new String'(Expanded
));
1093 procedure Define_Prefix
1094 (Config
: in out Command_Line_Configuration
;
1098 if Config
= null then
1099 Config
:= new Command_Line_Configuration_Record
;
1102 Add
(Config
.Prefixes
, new String'(Prefix));
1109 procedure Define_Switch
1110 (Config : in out Command_Line_Configuration;
1114 if Config = null then
1115 Config := new Command_Line_Configuration_Record;
1118 Add (Config.Switches, new String'(Switch
));
1121 --------------------
1122 -- Define_Section --
1123 --------------------
1125 procedure Define_Section
1126 (Config
: in out Command_Line_Configuration
;
1130 if Config
= null then
1131 Config
:= new Command_Line_Configuration_Record
;
1134 Add
(Config
.Sections
, new String'(Section));
1141 function Get_Switches
1142 (Config : Command_Line_Configuration;
1143 Switch_Char : Character)
1146 Ret : Ada.Strings.Unbounded.Unbounded_String;
1147 use type Ada.Strings.Unbounded.Unbounded_String;
1150 if Config = null or else Config.Switches = null then
1154 for J in Config.Switches'Range loop
1155 if Config.Switches (J) (Config.Switches (J)'First) = Switch_Char then
1159 (Config.Switches (J)'First + 1 .. Config.Switches (J)'Last);
1161 Ret := Ret & " " & Config.Switches (J).all;
1165 return Ada.Strings.Unbounded.To_String (Ret);
1168 -----------------------
1169 -- Set_Configuration --
1170 -----------------------
1172 procedure Set_Configuration
1173 (Cmd : in out Command_Line;
1174 Config : Command_Line_Configuration)
1177 Cmd.Config := Config;
1178 end Set_Configuration;
1180 -----------------------
1181 -- Get_Configuration --
1182 -----------------------
1184 function Get_Configuration
1185 (Cmd : Command_Line) return Command_Line_Configuration is
1188 end Get_Configuration;
1190 ----------------------
1191 -- Set_Command_Line --
1192 ----------------------
1194 procedure Set_Command_Line
1195 (Cmd : in out Command_Line;
1197 Getopt_Description : String := "";
1198 Switch_Char : Character := '-')
1200 Tmp : Argument_List_Access;
1201 Parser : Opt_Parser;
1203 Section : String_Access := null;
1205 function Real_Full_Switch
1207 Parser : Opt_Parser) return String;
1208 -- Ensure that the returned switch value contains the
1209 -- Switch_Char prefix if needed.
1211 ----------------------
1212 -- Real_Full_Switch --
1213 ----------------------
1215 function Real_Full_Switch
1217 Parser : Opt_Parser) return String
1221 return Full_Switch (Parser);
1223 return Switch_Char & Full_Switch (Parser);
1225 end Real_Full_Switch;
1227 -- Start of processing for Set_Command_Line
1230 Free (Cmd.Expanded);
1233 if Switches /= "" then
1234 Tmp := Argument_String_To_List (Switches);
1235 Initialize_Option_Scan (Parser, Tmp, Switch_Char);
1239 S := Getopt (Switches => "* " & Getopt_Description,
1240 Concatenate => False,
1242 exit when S = ASCII.NUL;
1245 Sw : constant String :=
1246 Real_Full_Switch (S, Parser);
1247 Is_Section : Boolean := False;
1250 if Cmd.Config /= null
1251 and then Cmd.Config.Sections /= null
1254 for S in Cmd.Config.Sections'Range loop
1255 if Sw = Cmd.Config.Sections (S).all then
1256 Section := Cmd.Config.Sections (S);
1259 exit Section_Search;
1261 end loop Section_Search;
1264 if not Is_Section then
1265 if Section = null then
1267 -- Work around some weird cases: some switches may
1268 -- expect parameters, but have the same value as
1269 -- longer switches: -gnaty3 (-gnaty, parameter=3) and
1270 -- -gnatya (-gnatya, no parameter).
1272 -- So we are calling add_switch here with parameter
1273 -- attached. This will be anyway correctly handled by
1274 -- Add_Switch if -gnaty3 is actually provided.
1276 if Separator (Parser) = ASCII.NUL then
1278 (Cmd, Sw & Parameter (Parser), "");
1281 (Cmd, Sw, Parameter (Parser), Separator (Parser));
1284 if Separator (Parser) = ASCII.NUL then
1286 (Cmd, Sw & Parameter (Parser), "",
1301 when Invalid_Parameter =>
1303 -- Add it with no parameter, if that's the way the user
1306 -- Specify the separator in all cases, as the switch might
1307 -- need to be unaliased, and the alias might contain
1308 -- switches with parameters.
1310 if Section = null then
1312 (Cmd, Switch_Char & Full_Switch (Parser),
1313 Separator => Separator (Parser));
1316 (Cmd, Switch_Char & Full_Switch (Parser),
1317 Separator => Separator (Parser),
1318 Section => Section.all);
1325 end Set_Command_Line;
1334 Substring : String) return Boolean is
1336 return Index + Substring'Length - 1 <= Type_Str'Last
1337 and then Type_Str (Index .. Index + Substring'Length - 1) = Substring;
1340 ------------------------
1341 -- Can_Have_Parameter --
1342 ------------------------
1344 function Can_Have_Parameter (S : String) return Boolean is
1346 if S'Length <= 1 then
1351 when '!' | ':' | '?
' | '=' =>
1356 end Can_Have_Parameter;
1358 -----------------------
1359 -- Require_Parameter --
1360 -----------------------
1362 function Require_Parameter (S : String) return Boolean is
1364 if S'Length <= 1 then
1369 when '!' | ':' | '=' =>
1374 end Require_Parameter;
1380 function Actual_Switch (S : String) return String is
1382 if S'Length <= 1 then
1387 when '!' | ':' | '?
' | '=' =>
1388 return S (S'First .. S'Last - 1);
1394 ----------------------------
1395 -- For_Each_Simple_Switch --
1396 ----------------------------
1398 procedure For_Each_Simple_Switch
1399 (Cmd : Command_Line;
1401 Parameter : String := "";
1402 Unalias : Boolean := True)
1404 function Group_Analysis
1406 Group : String) return Boolean;
1407 -- Perform the analysis of a group of switches.
1409 --------------------
1410 -- Group_Analysis --
1411 --------------------
1413 function Group_Analysis
1415 Group : String) return Boolean
1422 while Idx <= Group'Last loop
1425 for S in Cmd.Config.Switches'Range loop
1427 Sw : constant String :=
1429 (Cmd.Config.Switches (S).all);
1430 Full : constant String :=
1431 Prefix & Group (Idx .. Group'Last);
1436 if Sw'Length >= Prefix'Length
1438 -- Verify that sw starts with Prefix
1440 and then Looking_At (Sw, Sw'First, Prefix)
1442 -- Verify that the group starts with sw
1444 and then Looking_At (Full, Full'First, Sw)
1446 Last := Idx + Sw'Length - Prefix'Length - 1;
1449 if Can_Have_Parameter (Cmd.Config.Switches (S).all) then
1451 -- Include potential parameter to the recursive call.
1452 -- Only numbers are allowed.
1454 while Last < Group'Last
1455 and then Group (Last + 1) in '0' .. '9'
1461 if not Require_Parameter (Cmd.Config.Switches (S).all)
1462 or else Last >= Param
1464 if Idx = Group'First
1465 and then Last = Group'Last
1466 and then Last < Param
1468 -- The group only concerns a single switch. Do not
1469 -- perform recursive call.
1471 -- Note that we still perform a recursive call if
1472 -- a parameter is detected in the switch, as this
1473 -- is a way to correctly identify such a parameter
1481 -- Recursive call, using the detected parameter if any
1483 if Last >= Param then
1484 For_Each_Simple_Switch
1486 Prefix & Group (Idx .. Param - 1),
1487 Group (Param .. Last));
1489 For_Each_Simple_Switch
1490 (Cmd, Prefix & Group (Idx .. Last), "");
1501 For_Each_Simple_Switch (Cmd, Prefix & Group (Idx), "");
1510 -- Are we adding a switch that can in fact be expanded through aliases ?
1511 -- If yes, we add separately each of its expansion.
1513 -- This takes care of expansions like "-T" -> "-gnatwrs", where the
1514 -- alias and its expansion do not have the same prefix. Given the order
1515 -- in which we do things here, the expansion of the alias will itself
1516 -- be checked for a common prefix and further split into simple switches
1519 and then Cmd.Config /= null
1520 and then Cmd.Config.Aliases /= null
1522 for A in Cmd.Config.Aliases'Range loop
1523 if Cmd.Config.Aliases (A).all = Switch
1524 and then Parameter = ""
1526 For_Each_Simple_Switch
1527 (Cmd, Cmd.Config.Expansions (A).all, "");
1533 -- Are we adding a switch grouping several switches ? If yes, add each
1534 -- of the simple switches instead.
1536 if Cmd.Config /= null
1537 and then Cmd.Config.Prefixes /= null
1539 for P in Cmd.Config.Prefixes'Range loop
1540 if Switch'Length > Cmd.Config.Prefixes (P)'Length + 1
1542 (Switch, Switch'First, Cmd.Config.Prefixes (P).all)
1544 -- Alias expansion will be done recursively
1545 if Cmd.Config.Switches = null then
1546 for S in Switch'First + Cmd.Config.Prefixes (P)'Length
1549 For_Each_Simple_Switch
1550 (Cmd, Cmd.Config.Prefixes (P).all & Switch (S), "");
1555 elsif Group_Analysis
1556 (Cmd.Config.Prefixes (P).all,
1558 (Switch'First + Cmd.Config.Prefixes (P)'Length
1561 -- Recursive calls already done on each switch of the
1562 -- group. Let's return to not call Callback.
1569 -- Test if added switch is a known switch with parameter attached
1572 and then Cmd.Config /= null
1573 and then Cmd.Config.Switches /= null
1575 for S in Cmd.Config.Switches'Range loop
1577 Sw : constant String :=
1578 Actual_Switch (Cmd.Config.Switches (S).all);
1583 -- Verify that switch starts with Sw
1584 -- What if the "verification" fails???
1586 if Switch'Length >= Sw'Length
1587 and then Looking_At (Switch, Switch'First, Sw)
1589 Param := Switch'First + Sw'Length - 1;
1592 if Can_Have_Parameter (Cmd.Config.Switches (S).all) then
1593 while Last < Switch'Last
1594 and then Switch (Last + 1) in '0' .. '9'
1600 -- If full Switch is a known switch with attached parameter
1601 -- then we use this parameter in the callback.
1603 if Last = Switch'Last then
1605 (Switch (Switch'First .. Param),
1606 Switch (Param + 1 .. Last));
1615 Callback (Switch, Parameter);
1616 end For_Each_Simple_Switch;
1622 procedure Add_Switch
1623 (Cmd : in out Command_Line;
1625 Parameter : String := "";
1626 Separator : Character := ' ';
1627 Section : String := "";
1628 Add_Before : Boolean := False)
1631 pragma Unreferenced (Success);
1634 (Cmd, Switch, Parameter, Separator, Section, Add_Before, Success);
1641 procedure Add_Switch
1642 (Cmd : in out Command_Line;
1644 Parameter : String := "";
1645 Separator : Character := ' ';
1646 Section : String := "";
1647 Add_Before : Boolean := False;
1648 Success : out Boolean)
1650 procedure Add_Simple_Switch (Simple : String; Param : String);
1651 -- Add a new switch that has had all its aliases expanded, and switches
1652 -- ungrouped. We know there are no more aliases in Switches.
1654 -----------------------
1655 -- Add_Simple_Switch --
1656 -----------------------
1658 procedure Add_Simple_Switch (Simple : String; Param : String) is
1660 if Cmd.Expanded = null then
1661 Cmd.Expanded := new Argument_List'(1 .. 1 => new String'(Simple));
1664 Cmd.Params := new Argument_List'
1665 (1 .. 1 => new String'(Separator & Param));
1668 Cmd.Params := new Argument_List'(1 .. 1 => null);
1671 if Section
= "" then
1672 Cmd
.Sections
:= new Argument_List
'(1 .. 1 => null);
1675 Cmd.Sections := new Argument_List'
1676 (1 .. 1 => new String'(Section));
1680 -- Do we already have this switch?
1682 for C in Cmd.Expanded'Range loop
1683 if Cmd.Expanded (C).all = Simple
1685 ((Cmd.Params (C) = null and then Param = "")
1687 (Cmd.Params (C) /= null
1688 and then Cmd.Params (C).all = Separator & Param))
1690 ((Cmd.Sections (C) = null and then Section = "")
1692 (Cmd.Sections (C) /= null
1693 and then Cmd.Sections (C).all = Section))
1699 -- Inserting at least one switch
1702 Add (Cmd.Expanded, new String'(Simple
), Add_Before
);
1707 new String'(Separator & Param),
1717 if Section = "" then
1725 new String'(Section
),
1729 end Add_Simple_Switch
;
1731 procedure Add_Simple_Switches
is
1732 new For_Each_Simple_Switch
(Add_Simple_Switch
);
1734 -- Start of processing for Add_Switch
1738 Add_Simple_Switches
(Cmd
, Switch
, Parameter
);
1739 Free
(Cmd
.Coalesce
);
1746 procedure Remove
(Line
: in out Argument_List_Access
; Index
: Integer) is
1747 Tmp
: Argument_List_Access
:= Line
;
1750 Line
:= new Argument_List
(Tmp
'First .. Tmp
'Last - 1);
1752 if Index
/= Tmp
'First then
1753 Line
(Tmp
'First .. Index
- 1) := Tmp
(Tmp
'First .. Index
- 1);
1758 if Index
/= Tmp
'Last then
1759 Line
(Index
.. Tmp
'Last - 1) := Tmp
(Index
+ 1 .. Tmp
'Last);
1762 Unchecked_Free
(Tmp
);
1770 (Line
: in out Argument_List_Access
;
1771 Str
: String_Access
;
1772 Before
: Boolean := False)
1774 Tmp
: Argument_List_Access
:= Line
;
1778 Line
:= new Argument_List
(Tmp
'First .. Tmp
'Last + 1);
1781 Line
(Tmp
'First) := Str
;
1782 Line
(Tmp
'First + 1 .. Tmp
'Last + 1) := Tmp
.all;
1784 Line
(Tmp
'Range) := Tmp
.all;
1785 Line
(Tmp
'Last + 1) := Str
;
1788 Unchecked_Free
(Tmp
);
1791 Line
:= new Argument_List
'(1 .. 1 => Str);
1799 procedure Remove_Switch
1800 (Cmd : in out Command_Line;
1802 Remove_All : Boolean := False;
1803 Has_Parameter : Boolean := False;
1804 Section : String := "")
1807 pragma Unreferenced (Success);
1809 Remove_Switch (Cmd, Switch, Remove_All, Has_Parameter, Section, Success);
1816 procedure Remove_Switch
1817 (Cmd : in out Command_Line;
1819 Remove_All : Boolean := False;
1820 Has_Parameter : Boolean := False;
1821 Section : String := "";
1822 Success : out Boolean)
1824 procedure Remove_Simple_Switch (Simple : String; Param : String);
1825 -- Removes a simple switch, with no aliasing or grouping
1827 --------------------------
1828 -- Remove_Simple_Switch --
1829 --------------------------
1831 procedure Remove_Simple_Switch (Simple : String; Param : String) is
1833 pragma Unreferenced (Param);
1836 if Cmd.Expanded /= null then
1837 C := Cmd.Expanded'First;
1838 while C <= Cmd.Expanded'Last loop
1839 if Cmd.Expanded (C).all = Simple
1842 or else (Cmd.Sections (C) = null
1843 and then Section = "")
1844 or else (Cmd.Sections (C) /= null
1845 and then Section = Cmd.Sections (C).all))
1846 and then (not Has_Parameter or else Cmd.Params (C) /= null)
1848 Remove (Cmd.Expanded, C);
1849 Remove (Cmd.Params, C);
1850 Remove (Cmd.Sections, C);
1853 if not Remove_All then
1862 end Remove_Simple_Switch;
1864 procedure Remove_Simple_Switches is
1865 new For_Each_Simple_Switch (Remove_Simple_Switch);
1867 -- Start of processing for Remove_Switch
1871 Remove_Simple_Switches (Cmd, Switch, "", Unalias => not Has_Parameter);
1872 Free (Cmd.Coalesce);
1879 procedure Remove_Switch
1880 (Cmd : in out Command_Line;
1883 Section : String := "")
1885 procedure Remove_Simple_Switch (Simple : String; Param : String);
1886 -- Removes a simple switch, with no aliasing or grouping
1888 --------------------------
1889 -- Remove_Simple_Switch --
1890 --------------------------
1892 procedure Remove_Simple_Switch (Simple : String; Param : String) is
1896 if Cmd.Expanded /= null then
1897 C := Cmd.Expanded'First;
1898 while C <= Cmd.Expanded'Last loop
1899 if Cmd.Expanded (C).all = Simple
1901 ((Cmd.Sections (C) = null
1902 and then Section = "")
1904 (Cmd.Sections (C) /= null
1905 and then Section = Cmd.Sections (C).all))
1907 ((Cmd.Params (C) = null and then Param = "")
1909 (Cmd.Params (C) /= null
1912 -- Ignore the separator stored in Parameter
1914 Cmd.Params (C) (Cmd.Params (C)'First + 1
1915 .. Cmd.Params (C)'Last) =
1918 Remove (Cmd.Expanded, C);
1919 Remove (Cmd.Params, C);
1920 Remove (Cmd.Sections, C);
1922 -- The switch is necessarily unique by construction of
1932 end Remove_Simple_Switch;
1934 procedure Remove_Simple_Switches is
1935 new For_Each_Simple_Switch (Remove_Simple_Switch);
1937 -- Start of processing for Remove_Switch
1940 Remove_Simple_Switches (Cmd, Switch, Parameter);
1941 Free (Cmd.Coalesce);
1944 --------------------
1945 -- Group_Switches --
1946 --------------------
1948 procedure Group_Switches
1949 (Cmd : Command_Line;
1950 Result : Argument_List_Access;
1951 Sections : Argument_List_Access;
1952 Params : Argument_List_Access)
1954 function Compatible_Parameter (Param : String_Access) return Boolean;
1955 -- True when the parameter can be part of a group
1957 --------------------------
1958 -- Compatible_Parameter --
1959 --------------------------
1961 function Compatible_Parameter (Param : String_Access) return Boolean is
1965 if Param = null then
1968 -- We need parameters without separators
1970 elsif Param (Param'First) /= ASCII.NUL then
1973 -- Parameters must be all digits
1976 for J in Param'First + 1 .. Param'Last loop
1977 if Param (J) not in '0' .. '9' then
1984 end Compatible_Parameter;
1986 -- Local declarations
1988 Group : Ada.Strings.Unbounded.Unbounded_String;
1990 use type Ada.Strings.Unbounded.Unbounded_String;
1992 -- Start of processing for Group_Switches
1995 if Cmd.Config = null
1996 or else Cmd.Config.Prefixes = null
2001 for P in Cmd.Config.Prefixes'Range loop
2002 Group := Ada.Strings.Unbounded.Null_Unbounded_String;
2005 for C in Result'Range loop
2006 if Result (C) /= null
2007 and then Compatible_Parameter (Params (C))
2009 (Result (C).all, Result (C)'First, Cmd.Config.Prefixes (P).all)
2011 -- If we are still in the same section, group the switches
2015 (Sections (C) = null
2016 and then Sections (First) = null)
2018 (Sections (C) /= null
2019 and then Sections (First) /= null
2020 and then Sections (C).all = Sections (First).all)
2025 (Result (C)'First + Cmd.Config.Prefixes (P)'Length ..
2028 if Params (C) /= null then
2031 Params (C) (Params (C)'First + 1 .. Params (C)'Last);
2042 -- We changed section: we put the grouped switches to the
2043 -- first place, on continue with the new section.
2047 (Cmd
.Config
.Prefixes
(P
).all &
2048 Ada
.Strings
.Unbounded
.To_String
(Group
));
2050 Ada
.Strings
.Unbounded
.To_Unbounded_String
2052 (Result
(C
)'First + Cmd
.Config
.Prefixes
(P
)'Length ..
2062 (Cmd.Config.Prefixes (P).all &
2063 Ada.Strings.Unbounded.To_String (Group));
2068 --------------------
2069 -- Alias_Switches --
2070 --------------------
2072 procedure Alias_Switches
2073 (Cmd : Command_Line;
2074 Result : Argument_List_Access;
2075 Params : Argument_List_Access)
2080 procedure Check_Cb (Switch : String; Param : String);
2081 -- Comment required ???
2083 procedure Remove_Cb (Switch : String; Param : String);
2084 -- Comment required ???
2090 procedure Check_Cb (Switch : String; Param : String) is
2093 for E in Result'Range loop
2094 if Result (E) /= null
2097 or else Params (E) (Params (E)'First + 1
2098 .. Params (E)'Last) = Param)
2099 and then Result (E).all = Switch
2113 procedure Remove_Cb (Switch : String; Param : String) is
2115 for E in Result'Range loop
2116 if Result (E) /= null
2119 or else Params (E) (Params (E)'First + 1
2120 .. Params (E)'Last) = Param)
2121 and then Result (E).all = Switch
2133 procedure Check_All is new For_Each_Simple_Switch (Check_Cb);
2134 procedure Remove_All is new For_Each_Simple_Switch (Remove_Cb);
2136 -- Start of processing for Alias_Switches
2139 if Cmd.Config = null
2140 or else Cmd.Config.Aliases = null
2145 for A in Cmd.Config.Aliases'Range loop
2147 -- Compute the various simple switches that make up the alias. We
2148 -- split the expansion into as many simple switches as possible, and
2149 -- then check whether the expanded command line has all of them.
2152 Check_All (Cmd, Cmd.Config.Expansions (A).all);
2155 First := Integer'Last;
2156 Remove_All (Cmd, Cmd.Config.Expansions (A).all);
2157 Result (First) := new String'(Cmd
.Config
.Aliases
(A
).all);
2166 procedure Sort_Sections
2167 (Line
: GNAT
.OS_Lib
.Argument_List_Access
;
2168 Sections
: GNAT
.OS_Lib
.Argument_List_Access
;
2169 Params
: GNAT
.OS_Lib
.Argument_List_Access
)
2171 Sections_List
: Argument_List_Access
:=
2172 new Argument_List
'(1 .. 1 => null);
2174 Old_Line : constant Argument_List := Line.all;
2175 Old_Sections : constant Argument_List := Sections.all;
2176 Old_Params : constant Argument_List := Params.all;
2184 -- First construct a list of all sections
2186 for E in Line'Range loop
2187 if Sections (E) /= null then
2189 for S in Sections_List'Range loop
2190 if (Sections_List (S) = null and then Sections (E) = null)
2192 (Sections_List (S) /= null
2193 and then Sections (E) /= null
2194 and then Sections_List (S).all = Sections (E).all)
2202 Add (Sections_List, Sections (E));
2207 Index := Line'First;
2209 for S in Sections_List'Range loop
2210 for E in Old_Line'Range loop
2211 if (Sections_List (S) = null and then Old_Sections (E) = null)
2213 (Sections_List (S) /= null
2214 and then Old_Sections (E) /= null
2215 and then Sections_List (S).all = Old_Sections (E).all)
2217 Line (Index) := Old_Line (E);
2218 Sections (Index) := Old_Sections (E);
2219 Params (Index) := Old_Params (E);
2231 (Cmd : in out Command_Line;
2232 Iter : in out Command_Line_Iterator;
2236 if Cmd.Expanded = null then
2241 -- Reorder the expanded line so that sections are grouped
2243 Sort_Sections (Cmd.Expanded, Cmd.Sections, Cmd.Params);
2245 -- Coalesce the switches as much as possible
2248 and then Cmd.Coalesce = null
2250 Cmd.Coalesce := new Argument_List (Cmd.Expanded'Range);
2251 for E in Cmd.Expanded'Range loop
2252 Cmd.Coalesce (E) := new String'(Cmd
.Expanded
(E
).all);
2255 Cmd
.Coalesce_Sections
:= new Argument_List
(Cmd
.Sections
'Range);
2256 for E
in Cmd
.Sections
'Range loop
2257 if Cmd
.Sections
(E
) = null then
2258 Cmd
.Coalesce_Sections
(E
) := null;
2260 Cmd
.Coalesce_Sections
(E
) := new String'(Cmd.Sections (E).all);
2264 Cmd.Coalesce_Params := new Argument_List (Cmd.Params'Range);
2265 for E in Cmd.Params'Range loop
2266 if Cmd.Params (E) = null then
2267 Cmd.Coalesce_Params (E) := null;
2269 Cmd.Coalesce_Params (E) := new String'(Cmd
.Params
(E
).all);
2273 -- Not a clone, since we will not modify the parameters anyway
2275 Alias_Switches
(Cmd
, Cmd
.Coalesce
, Cmd
.Coalesce_Params
);
2277 (Cmd
, Cmd
.Coalesce
, Cmd
.Coalesce_Sections
, Cmd
.Coalesce_Params
);
2281 Iter
.List
:= Cmd
.Expanded
;
2282 Iter
.Params
:= Cmd
.Params
;
2283 Iter
.Sections
:= Cmd
.Sections
;
2285 Iter
.List
:= Cmd
.Coalesce
;
2286 Iter
.Params
:= Cmd
.Coalesce_Params
;
2287 Iter
.Sections
:= Cmd
.Coalesce_Sections
;
2290 if Iter
.List
= null then
2291 Iter
.Current
:= Integer'Last;
2293 Iter
.Current
:= Iter
.List
'First;
2295 while Iter
.Current
<= Iter
.List
'Last
2296 and then Iter
.List
(Iter
.Current
) = null
2298 Iter
.Current
:= Iter
.Current
+ 1;
2303 --------------------
2304 -- Current_Switch --
2305 --------------------
2307 function Current_Switch
(Iter
: Command_Line_Iterator
) return String is
2309 return Iter
.List
(Iter
.Current
).all;
2312 --------------------
2313 -- Is_New_Section --
2314 --------------------
2316 function Is_New_Section
(Iter
: Command_Line_Iterator
) return Boolean is
2317 Section
: constant String := Current_Section
(Iter
);
2319 if Iter
.Sections
= null then
2321 elsif Iter
.Current
= Iter
.Sections
'First
2322 or else Iter
.Sections
(Iter
.Current
- 1) = null
2324 return Section
/= "";
2327 return Section
/= Iter
.Sections
(Iter
.Current
- 1).all;
2330 ---------------------
2331 -- Current_Section --
2332 ---------------------
2334 function Current_Section
(Iter
: Command_Line_Iterator
) return String is
2336 if Iter
.Sections
= null
2337 or else Iter
.Current
> Iter
.Sections
'Last
2338 or else Iter
.Sections
(Iter
.Current
) = null
2343 return Iter
.Sections
(Iter
.Current
).all;
2344 end Current_Section
;
2346 -----------------------
2347 -- Current_Separator --
2348 -----------------------
2350 function Current_Separator
(Iter
: Command_Line_Iterator
) return String is
2352 if Iter
.Params
= null
2353 or else Iter
.Current
> Iter
.Params
'Last
2354 or else Iter
.Params
(Iter
.Current
) = null
2360 Sep
: constant Character :=
2361 Iter
.Params
(Iter
.Current
) (Iter
.Params
(Iter
.Current
)'First);
2363 if Sep
= ASCII
.NUL
then
2370 end Current_Separator
;
2372 -----------------------
2373 -- Current_Parameter --
2374 -----------------------
2376 function Current_Parameter
(Iter
: Command_Line_Iterator
) return String is
2378 if Iter
.Params
= null
2379 or else Iter
.Current
> Iter
.Params
'Last
2380 or else Iter
.Params
(Iter
.Current
) = null
2386 P
: constant String := Iter
.Params
(Iter
.Current
).all;
2391 return P
(P
'First + 1 .. P
'Last);
2394 end Current_Parameter
;
2400 function Has_More
(Iter
: Command_Line_Iterator
) return Boolean is
2402 return Iter
.List
/= null and then Iter
.Current
<= Iter
.List
'Last;
2409 procedure Next
(Iter
: in out Command_Line_Iterator
) is
2411 Iter
.Current
:= Iter
.Current
+ 1;
2412 while Iter
.Current
<= Iter
.List
'Last
2413 and then Iter
.List
(Iter
.Current
) = null
2415 Iter
.Current
:= Iter
.Current
+ 1;
2423 procedure Free
(Config
: in out Command_Line_Configuration
) is
2425 if Config
/= null then
2426 Free
(Config
.Aliases
);
2427 Free
(Config
.Expansions
);
2428 Free
(Config
.Prefixes
);
2429 Unchecked_Free
(Config
);
2437 procedure Free
(Cmd
: in out Command_Line
) is
2439 Free
(Cmd
.Expanded
);
2440 Free
(Cmd
.Coalesce
);
2444 end GNAT
.Command_Line
;