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-2011, 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 3, 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. --
18 -- As a special exception under Section 7 of GPL version 3, you are granted --
19 -- additional permissions described in the GCC Runtime Library Exception, --
20 -- version 3.1, as published by the Free Software Foundation. --
22 -- In particular, you can freely distribute your programs built with the --
23 -- GNAT Pro compiler, including any required library run-time units, using --
24 -- any licensing terms of your choosing. See the AdaCore Software License --
25 -- for full details. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 with Ada
.Characters
.Handling
; use Ada
.Characters
.Handling
;
33 with Ada
.Strings
.Unbounded
;
34 with Ada
.Text_IO
; use Ada
.Text_IO
;
35 with Ada
.Unchecked_Deallocation
;
37 with GNAT
.Directory_Operations
; use GNAT
.Directory_Operations
;
38 with GNAT
.OS_Lib
; use GNAT
.OS_Lib
;
40 package body GNAT
.Command_Line
is
42 package CL
renames Ada
.Command_Line
;
44 type Switch_Parameter_Type
is
46 Parameter_With_Optional_Space
, -- ':' in getopt
47 Parameter_With_Space_Or_Equal
, -- '=' in getopt
48 Parameter_No_Space
, -- '!' in getopt
49 Parameter_Optional
); -- '?' in getopt
51 procedure Set_Parameter
52 (Variable
: out Parameter_Type
;
56 Extra
: Character := ASCII
.NUL
);
57 pragma Inline
(Set_Parameter
);
58 -- Set the parameter that will be returned by Parameter below
59 -- Parameters need to be defined ???
61 function Goto_Next_Argument_In_Section
(Parser
: Opt_Parser
) return Boolean;
62 -- Go to the next argument on the command line. If we are at the end of
63 -- the current section, we want to make sure there is no other identical
64 -- section on the command line (there might be multiple instances of
65 -- -largs). Returns True iff there is another argument.
67 function Get_File_Names_Case_Sensitive
return Integer;
68 pragma Import
(C
, Get_File_Names_Case_Sensitive
,
69 "__gnat_get_file_names_case_sensitive");
71 File_Names_Case_Sensitive
: constant Boolean :=
72 Get_File_Names_Case_Sensitive
/= 0;
74 procedure Canonical_Case_File_Name
(S
: in out String);
75 -- Given a file name, converts it to canonical case form. For systems where
76 -- file names are case sensitive, this procedure has no effect. If file
77 -- names are not case sensitive (i.e. for example if you have the file
78 -- "xyz.adb", you can refer to it as XYZ.adb or XyZ.AdB), then this call
79 -- converts the given string to canonical all lower case form, so that two
80 -- file names compare equal if they refer to the same file.
82 procedure Internal_Initialize_Option_Scan
84 Switch_Char
: Character;
85 Stop_At_First_Non_Switch
: Boolean;
86 Section_Delimiters
: String);
87 -- Initialize Parser, which must have been allocated already
89 function Argument
(Parser
: Opt_Parser
; Index
: Integer) return String;
90 -- Return the index-th command line argument
92 procedure Find_Longest_Matching_Switch
95 Index_In_Switches
: out Integer;
96 Switch_Length
: out Integer;
97 Param
: out Switch_Parameter_Type
);
98 -- Return the Longest switch from Switches that at least partially
99 -- partially Arg. Index_In_Switches is set to 0 if none matches.
100 -- What are other parameters??? in particular Param is not always set???
102 procedure Unchecked_Free
is new Ada
.Unchecked_Deallocation
103 (Argument_List
, Argument_List_Access
);
105 procedure Unchecked_Free
is new Ada
.Unchecked_Deallocation
106 (Command_Line_Configuration_Record
, Command_Line_Configuration
);
108 procedure Remove
(Line
: in out Argument_List_Access
; Index
: Integer);
109 -- Remove a specific element from Line
112 (Line
: in out Argument_List_Access
;
114 Before
: Boolean := False);
115 -- Add a new element to Line. If Before is True, the item is inserted at
116 -- the beginning, else it is appended.
119 (Config
: in out Command_Line_Configuration
;
120 Switch
: Switch_Definition
);
122 (Def
: in out Alias_Definitions_List
;
123 Alias
: Alias_Definition
);
124 -- Add a new element to Def
126 procedure Initialize_Switch_Def
127 (Def
: out Switch_Definition
;
128 Switch
: String := "";
129 Long_Switch
: String := "";
131 Section
: String := "");
132 -- Initialize [Def] with the contents of the other parameters.
133 -- This also checks consistency of the switch parameters, and will raise
134 -- Invalid_Switch if they do not match.
136 procedure Decompose_Switch
138 Parameter_Type
: out Switch_Parameter_Type
;
139 Switch_Last
: out Integer);
140 -- Given a switch definition ("name:" for instance), extracts the type of
141 -- parameter that is expected, and the name of the switch
143 function Can_Have_Parameter
(S
: String) return Boolean;
144 -- True if S can have a parameter
146 function Require_Parameter
(S
: String) return Boolean;
147 -- True if S requires a parameter
149 function Actual_Switch
(S
: String) return String;
150 -- Remove any possible trailing '!', ':', '?' and '='
153 with procedure Callback
154 (Simple_Switch
: String;
157 Index
: Integer); -- Index in Config.Switches, or -1
158 procedure For_Each_Simple_Switch
159 (Config
: Command_Line_Configuration
;
162 Parameter
: String := "";
163 Unalias
: Boolean := True);
164 -- Breaks Switch into as simple switches as possible (expanding aliases and
165 -- ungrouping common prefixes when possible), and call Callback for each of
168 procedure Sort_Sections
169 (Line
: GNAT
.OS_Lib
.Argument_List_Access
;
170 Sections
: GNAT
.OS_Lib
.Argument_List_Access
;
171 Params
: GNAT
.OS_Lib
.Argument_List_Access
);
172 -- Reorder the command line switches so that the switches belonging to a
173 -- section are grouped together.
175 procedure Group_Switches
177 Result
: Argument_List_Access
;
178 Sections
: Argument_List_Access
;
179 Params
: Argument_List_Access
);
180 -- Group switches with common prefixes whenever possible. Once they have
181 -- been grouped, we also check items for possible aliasing.
183 procedure Alias_Switches
185 Result
: Argument_List_Access
;
186 Params
: Argument_List_Access
);
187 -- When possible, replace one or more switches by an alias, i.e. a shorter
193 Substring
: String) return Boolean;
194 -- Return True if the characters starting at Index in Type_Str are
195 -- equivalent to Substring.
198 with function Callback
(S
: String; Index
: Integer) return Boolean;
199 procedure Foreach_Switch
200 (Config
: Command_Line_Configuration
;
202 -- Iterate over all switches defined in Config, for a specific section.
203 -- Index is set to the index in Config.Switches. Stop iterating when
204 -- Callback returns False.
210 function Argument
(Parser
: Opt_Parser
; Index
: Integer) return String is
212 if Parser
.Arguments
/= null then
213 return Parser
.Arguments
(Index
+ Parser
.Arguments
'First - 1).all;
215 return CL
.Argument
(Index
);
219 ------------------------------
220 -- Canonical_Case_File_Name --
221 ------------------------------
223 procedure Canonical_Case_File_Name
(S
: in out String) is
225 if not File_Names_Case_Sensitive
then
226 for J
in S
'Range loop
227 if S
(J
) in 'A' .. 'Z' then
228 S
(J
) := Character'Val
229 (Character'Pos (S
(J
)) +
230 (Character'Pos ('a') - Character'Pos ('A')));
234 end Canonical_Case_File_Name
;
240 function Expansion
(Iterator
: Expansion_Iterator
) return String is
241 type Pointer
is access all Expansion_Iterator
;
243 It
: constant Pointer
:= Iterator
'Unrestricted_Access;
244 S
: String (1 .. 1024);
247 Current
: Depth
:= It
.Current_Depth
;
251 -- It is assumed that a directory is opened at the current level.
252 -- Otherwise GNAT.Directory_Operations.Directory_Error will be raised
253 -- at the first call to Read.
256 Read
(It
.Levels
(Current
).Dir
, S
, Last
);
258 -- If we have exhausted the directory, close it and go back one level
261 Close
(It
.Levels
(Current
).Dir
);
263 -- If we are at level 1, we are finished; return an empty string
266 return String'(1 .. 0 => ' ');
268 -- Otherwise continue with the directory at the previous level
271 Current := Current - 1;
272 It.Current_Depth := Current;
275 -- If this is a directory, that is neither "." or "..", attempt to
276 -- go to the next level.
279 (It.Dir_Name (1 .. It.Levels (Current).Name_Last) &
281 and then S (1 .. Last) /= "."
282 and then S (1 .. Last) /= ".."
284 -- We can go to the next level only if we have not reached the
287 if Current < It.Maximum_Depth then
288 NL := It.Levels (Current).Name_Last;
290 -- And if relative path of this new directory is not too long
292 if NL + Last + 1 < Max_Path_Length then
293 Current := Current + 1;
294 It.Current_Depth := Current;
295 It.Dir_Name (NL + 1 .. NL + Last) := S (1 .. Last);
297 It.Dir_Name (NL) := Directory_Separator;
298 It.Levels (Current).Name_Last := NL;
299 Canonical_Case_File_Name (It.Dir_Name (1 .. NL));
301 -- Open the new directory, and read from it
303 GNAT.Directory_Operations.Open
304 (It.Levels (Current).Dir, It.Dir_Name (1 .. NL));
309 -- Check the relative path against the pattern
311 -- Note that we try to match also against directory names, since
312 -- clients of this function may expect to retrieve directories.
316 It.Dir_Name (It.Start .. It.Levels (Current).Name_Last)
320 Canonical_Case_File_Name (Name);
322 -- If it matches return the relative path
324 if GNAT.Regexp.Match (Name, Iterator.Regexp) then
331 ---------------------
332 -- Current_Section --
333 ---------------------
335 function Current_Section
336 (Parser : Opt_Parser := Command_Line_Parser) return String
339 if Parser.Current_Section = 1 then
343 for Index in reverse 1 .. Integer'Min (Parser.Current_Argument - 1,
346 if Parser.Section (Index) = 0 then
347 return Argument (Parser, Index);
359 (Parser : Opt_Parser := Command_Line_Parser) return String
362 if Parser.The_Switch.Extra = ASCII.NUL then
363 return Argument (Parser, Parser.The_Switch.Arg_Num)
364 (Parser.The_Switch.First .. Parser.The_Switch.Last);
366 return Parser.The_Switch.Extra
367 & Argument (Parser, Parser.The_Switch.Arg_Num)
368 (Parser.The_Switch.First .. Parser.The_Switch.Last);
376 function Get_Argument
377 (Do_Expansion : Boolean := False;
378 Parser : Opt_Parser := Command_Line_Parser) return String
381 if Parser.In_Expansion then
383 S : constant String := Expansion (Parser.Expansion_It);
385 if S'Length /= 0 then
388 Parser.In_Expansion := False;
393 if Parser.Current_Argument > Parser.Arg_Count then
395 -- If this is the first time this function is called
397 if Parser.Current_Index = 1 then
398 Parser.Current_Argument := 1;
399 while Parser.Current_Argument <= Parser.Arg_Count
400 and then Parser.Section (Parser.Current_Argument) /=
401 Parser.Current_Section
403 Parser.Current_Argument := Parser.Current_Argument + 1;
407 return String'(1 .. 0 => ' ');
410 elsif Parser
.Section
(Parser
.Current_Argument
) = 0 then
411 while Parser
.Current_Argument
<= Parser
.Arg_Count
412 and then Parser
.Section
(Parser
.Current_Argument
) /=
413 Parser
.Current_Section
415 Parser
.Current_Argument
:= Parser
.Current_Argument
+ 1;
419 Parser
.Current_Index
:= Integer'Last;
421 while Parser
.Current_Argument
<= Parser
.Arg_Count
422 and then Parser
.Is_Switch
(Parser
.Current_Argument
)
424 Parser
.Current_Argument
:= Parser
.Current_Argument
+ 1;
427 if Parser
.Current_Argument
> Parser
.Arg_Count
then
428 return String'(1 .. 0 => ' ');
429 elsif Parser.Section (Parser.Current_Argument) = 0 then
430 return Get_Argument (Do_Expansion);
433 Parser.Current_Argument := Parser.Current_Argument + 1;
435 -- Could it be a file name with wild cards to expand?
439 Arg : constant String :=
440 Argument (Parser, Parser.Current_Argument - 1);
445 while Index <= Arg'Last loop
447 or else Arg (Index) = '?
'
448 or else Arg (Index) = '['
450 Parser.In_Expansion := True;
451 Start_Expansion (Parser.Expansion_It, Arg);
452 return Get_Argument (Do_Expansion);
460 return Argument (Parser, Parser.Current_Argument - 1);
463 ----------------------
464 -- Decompose_Switch --
465 ----------------------
467 procedure Decompose_Switch
469 Parameter_Type : out Switch_Parameter_Type;
470 Switch_Last : out Integer)
474 Parameter_Type := Parameter_None;
475 Switch_Last := Switch'Last;
479 case Switch (Switch'Last) is
481 Parameter_Type := Parameter_With_Optional_Space;
482 Switch_Last := Switch'Last - 1;
484 Parameter_Type := Parameter_With_Space_Or_Equal;
485 Switch_Last := Switch'Last - 1;
487 Parameter_Type := Parameter_No_Space;
488 Switch_Last := Switch'Last - 1;
490 Parameter_Type := Parameter_Optional;
491 Switch_Last := Switch'Last - 1;
493 Parameter_Type := Parameter_None;
494 Switch_Last := Switch'Last;
496 end Decompose_Switch;
498 ----------------------------------
499 -- Find_Longest_Matching_Switch --
500 ----------------------------------
502 procedure Find_Longest_Matching_Switch
505 Index_In_Switches : out Integer;
506 Switch_Length : out Integer;
507 Param : out Switch_Parameter_Type)
510 Length : Natural := 1;
512 P : Switch_Parameter_Type;
515 Index_In_Switches := 0;
518 -- Remove all leading spaces first to make sure that Index points
519 -- at the start of the first switch.
521 Index := Switches'First;
522 while Index <= Switches'Last and then Switches (Index) = ' ' loop
526 while Index <= Switches'Last loop
528 -- Search the length of the parameter at this position in Switches
531 while Length <= Switches'Last
532 and then Switches (Length) /= ' '
534 Length := Length + 1;
537 -- Length now marks the separator after the current switch. Last will
538 -- mark the last character of the name of the switch.
540 if Length = Index + 1 then
544 Decompose_Switch (Switches (Index .. Length - 1), P, Last);
547 -- If it is the one we searched, it may be a candidate
549 if Arg'First + Last - Index <= Arg'Last
550 and then Switches (Index .. Last) =
551 Arg (Arg'First .. Arg'First + Last - Index)
552 and then Last - Index + 1 > Switch_Length
555 Index_In_Switches := Index;
556 Switch_Length := Last - Index + 1;
559 -- Look for the next switch in Switches
561 while Index <= Switches'Last
562 and then Switches (Index) /= ' '
569 end Find_Longest_Matching_Switch;
577 Concatenate : Boolean := True;
578 Parser : Opt_Parser := Command_Line_Parser) return Character
581 pragma Unreferenced (Dummy);
586 -- If we have finished parsing the current command line item (there
587 -- might be multiple switches in a single item), then go to the next
590 if Parser.Current_Argument > Parser.Arg_Count
591 or else (Parser.Current_Index >
592 Argument (Parser, Parser.Current_Argument)'Last
593 and then not Goto_Next_Argument_In_Section (Parser))
598 -- By default, the switch will not have a parameter
600 Parser.The_Parameter :=
601 (Integer'Last, Integer'Last, Integer'Last - 1, ASCII.NUL);
602 Parser.The_Separator := ASCII.NUL;
605 Arg : constant String :=
606 Argument (Parser, Parser.Current_Argument);
607 Index_Switches : Natural := 0;
608 Max_Length : Natural := 0;
610 Param : Switch_Parameter_Type;
612 -- If we are on a new item, test if this might be a switch
614 if Parser.Current_Index = Arg'First then
615 if Arg (Arg'First) /= Parser.Switch_Character then
617 -- If it isn't a switch, return it immediately. We also know it
618 -- isn't the parameter to a previous switch, since that has
619 -- already been handled.
621 if Switches (Switches'First) = '*' then
624 Arg_Num => Parser.Current_Argument,
627 Parser.Is_Switch (Parser.Current_Argument) := True;
628 Dummy := Goto_Next_Argument_In_Section (Parser);
632 if Parser.Stop_At_First then
633 Parser.Current_Argument := Positive'Last;
636 elsif not Goto_Next_Argument_In_Section (Parser) then
640 -- Recurse to get the next switch on the command line
646 -- We are on the first character of a new command line argument,
647 -- which starts with Switch_Character. Further analysis is needed.
649 Parser.Current_Index := Parser.Current_Index + 1;
650 Parser.Is_Switch (Parser.Current_Argument) := True;
653 Find_Longest_Matching_Switch
654 (Switches => Switches,
655 Arg => Arg (Parser.Current_Index .. Arg'Last),
656 Index_In_Switches => Index_Switches,
657 Switch_Length => Max_Length,
660 -- If switch is not accepted, it is either invalid or is returned
661 -- in the context of '*'.
663 if Index_Switches = 0 then
665 -- Depending on the value of Concatenate, the full switch is
666 -- a single character or the rest of the argument.
669 (if Concatenate then Parser.Current_Index else Arg'Last);
671 if Switches (Switches'First) = '*' then
673 -- Always prepend the switch character, so that users know that
674 -- this comes from a switch on the command line. This is
675 -- especially important when Concatenate is False, since
676 -- otherwise the current argument first character is lost.
678 if Parser.Section (Parser.Current_Argument) = 0 then
680 -- A section transition should not be returned to the user
682 Dummy := Goto_Next_Argument_In_Section (Parser);
688 Arg_Num => Parser.Current_Argument,
689 First => Parser.Current_Index,
691 Extra => Parser.Switch_Character);
692 Parser.Is_Switch (Parser.Current_Argument) := True;
693 Dummy := Goto_Next_Argument_In_Section (Parser);
700 Arg_Num => Parser.Current_Argument,
701 First => Parser.Current_Index,
703 Parser.Current_Index := End_Index + 1;
705 raise Invalid_Switch;
708 End_Index := Parser.Current_Index + Max_Length - 1;
711 Arg_Num => Parser.Current_Argument,
712 First => Parser.Current_Index,
716 when Parameter_With_Optional_Space =>
717 if End_Index < Arg'Last then
719 (Parser.The_Parameter,
720 Arg_Num => Parser.Current_Argument,
721 First => End_Index + 1,
723 Dummy := Goto_Next_Argument_In_Section (Parser);
725 elsif Parser.Current_Argument < Parser.Arg_Count
726 and then Parser.Section (Parser.Current_Argument + 1) /= 0
728 Parser.Current_Argument := Parser.Current_Argument + 1;
729 Parser.The_Separator := ' ';
731 (Parser.The_Parameter,
732 Arg_Num => Parser.Current_Argument,
733 First => Argument (Parser, Parser.Current_Argument)'First,
734 Last => Argument (Parser, Parser.Current_Argument)'Last);
735 Parser.Is_Switch (Parser.Current_Argument) := True;
736 Dummy := Goto_Next_Argument_In_Section (Parser);
739 Parser.Current_Index := End_Index + 1;
740 raise Invalid_Parameter;
743 when Parameter_With_Space_Or_Equal =>
745 -- If the switch is of the form <switch>=xxx
747 if End_Index < Arg'Last then
748 if Arg (End_Index + 1) = '='
749 and then End_Index + 1 < Arg'Last
751 Parser.The_Separator := '=';
753 (Parser.The_Parameter,
754 Arg_Num => Parser.Current_Argument,
755 First => End_Index + 2,
757 Dummy := Goto_Next_Argument_In_Section (Parser);
760 Parser.Current_Index := End_Index + 1;
761 raise Invalid_Parameter;
764 -- If the switch is of the form <switch> xxx
766 elsif Parser.Current_Argument < Parser.Arg_Count
767 and then Parser.Section (Parser.Current_Argument + 1) /= 0
769 Parser.Current_Argument := Parser.Current_Argument + 1;
770 Parser.The_Separator := ' ';
772 (Parser.The_Parameter,
773 Arg_Num => Parser.Current_Argument,
774 First => Argument (Parser, Parser.Current_Argument)'First,
775 Last => Argument (Parser, Parser.Current_Argument)'Last);
776 Parser.Is_Switch (Parser.Current_Argument) := True;
777 Dummy := Goto_Next_Argument_In_Section (Parser);
780 Parser.Current_Index := End_Index + 1;
781 raise Invalid_Parameter;
784 when Parameter_No_Space =>
785 if End_Index < Arg'Last then
787 (Parser.The_Parameter,
788 Arg_Num => Parser.Current_Argument,
789 First => End_Index + 1,
791 Dummy := Goto_Next_Argument_In_Section (Parser);
794 Parser.Current_Index := End_Index + 1;
795 raise Invalid_Parameter;
798 when Parameter_Optional =>
799 if End_Index < Arg'Last then
801 (Parser.The_Parameter,
802 Arg_Num => Parser.Current_Argument,
803 First => End_Index + 1,
807 Dummy := Goto_Next_Argument_In_Section (Parser);
809 when Parameter_None =>
810 if Concatenate or else End_Index = Arg'Last then
811 Parser.Current_Index := End_Index + 1;
814 -- If Concatenate is False and the full argument is not
815 -- recognized as a switch, this is an invalid switch.
817 if Switches (Switches'First) = '*' then
820 Arg_Num => Parser.Current_Argument,
823 Parser.Is_Switch (Parser.Current_Argument) := True;
824 Dummy := Goto_Next_Argument_In_Section (Parser);
830 Arg_Num => Parser.Current_Argument,
831 First => Parser.Current_Index,
833 Parser.Current_Index := Arg'Last + 1;
834 raise Invalid_Switch;
838 return Switches (Index_Switches);
842 -----------------------------------
843 -- Goto_Next_Argument_In_Section --
844 -----------------------------------
846 function Goto_Next_Argument_In_Section
847 (Parser : Opt_Parser) return Boolean
850 Parser.Current_Argument := Parser.Current_Argument + 1;
852 if Parser.Current_Argument > Parser.Arg_Count
853 or else Parser.Section (Parser.Current_Argument) = 0
856 Parser.Current_Argument := Parser.Current_Argument + 1;
858 if Parser.Current_Argument > Parser.Arg_Count then
859 Parser.Current_Index := 1;
863 exit when Parser.Section (Parser.Current_Argument) =
864 Parser.Current_Section;
868 Parser.Current_Index :=
869 Argument (Parser, Parser.Current_Argument)'First;
872 end Goto_Next_Argument_In_Section;
878 procedure Goto_Section
879 (Name : String := "";
880 Parser : Opt_Parser := Command_Line_Parser)
885 Parser.In_Expansion := False;
888 Parser.Current_Argument := 1;
889 Parser.Current_Index := 1;
890 Parser.Current_Section := 1;
895 while Index <= Parser.Arg_Count loop
896 if Parser.Section (Index) = 0
897 and then Argument (Parser, Index) = Parser.Switch_Character & Name
899 Parser.Current_Argument := Index + 1;
900 Parser.Current_Index := 1;
902 if Parser.Current_Argument <= Parser.Arg_Count then
903 Parser.Current_Section :=
904 Parser.Section (Parser.Current_Argument);
907 -- Exit from loop if we have the start of another section
909 if Index = Parser.Section'Last
910 or else Parser.Section (Index + 1) /= 0
919 Parser.Current_Argument := Positive'Last;
920 Parser.Current_Index := 2; -- so that Get_Argument returns nothing
923 ----------------------------
924 -- Initialize_Option_Scan --
925 ----------------------------
927 procedure Initialize_Option_Scan
928 (Switch_Char : Character := '-';
929 Stop_At_First_Non_Switch : Boolean := False;
930 Section_Delimiters : String := "")
933 Internal_Initialize_Option_Scan
934 (Parser => Command_Line_Parser,
935 Switch_Char => Switch_Char,
936 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
937 Section_Delimiters => Section_Delimiters);
938 end Initialize_Option_Scan;
940 ----------------------------
941 -- Initialize_Option_Scan --
942 ----------------------------
944 procedure Initialize_Option_Scan
945 (Parser : out Opt_Parser;
946 Command_Line : GNAT.OS_Lib.Argument_List_Access;
947 Switch_Char : Character := '-';
948 Stop_At_First_Non_Switch : Boolean := False;
949 Section_Delimiters : String := "")
954 if Command_Line = null then
955 Parser := new Opt_Parser_Data (CL.Argument_Count);
956 Internal_Initialize_Option_Scan
958 Switch_Char => Switch_Char,
959 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
960 Section_Delimiters => Section_Delimiters);
962 Parser := new Opt_Parser_Data (Command_Line'Length);
963 Parser.Arguments := Command_Line;
964 Internal_Initialize_Option_Scan
966 Switch_Char => Switch_Char,
967 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
968 Section_Delimiters => Section_Delimiters);
970 end Initialize_Option_Scan;
972 -------------------------------------
973 -- Internal_Initialize_Option_Scan --
974 -------------------------------------
976 procedure Internal_Initialize_Option_Scan
977 (Parser : Opt_Parser;
978 Switch_Char : Character;
979 Stop_At_First_Non_Switch : Boolean;
980 Section_Delimiters : String)
982 Section_Num : Section_Number;
983 Section_Index : Integer;
985 Delimiter_Found : Boolean;
988 pragma Warnings (Off, Discard);
991 Parser.Current_Argument := 0;
992 Parser.Current_Index := 0;
993 Parser.In_Expansion := False;
994 Parser.Switch_Character := Switch_Char;
995 Parser.Stop_At_First := Stop_At_First_Non_Switch;
996 Parser.Section := (others => 1);
998 -- If we are using sections, we have to preprocess the command line to
999 -- delimit them. A section can be repeated, so we just give each item
1000 -- on the command line a section number
1003 Section_Index := Section_Delimiters'First;
1004 while Section_Index <= Section_Delimiters'Last loop
1005 Last := Section_Index;
1006 while Last <= Section_Delimiters'Last
1007 and then Section_Delimiters (Last) /= ' '
1012 Delimiter_Found := False;
1013 Section_Num := Section_Num + 1;
1015 for Index in 1 .. Parser.Arg_Count loop
1016 if Argument (Parser, Index)(1) = Parser.Switch_Character
1018 Argument (Parser, Index) = Parser.Switch_Character &
1020 (Section_Index .. Last - 1)
1022 Parser.Section (Index) := 0;
1023 Delimiter_Found := True;
1025 elsif Parser.Section (Index) = 0 then
1027 -- A previous section delimiter
1029 Delimiter_Found := False;
1031 elsif Delimiter_Found then
1032 Parser.Section (Index) := Section_Num;
1036 Section_Index := Last + 1;
1037 while Section_Index <= Section_Delimiters'Last
1038 and then Section_Delimiters (Section_Index) = ' '
1040 Section_Index := Section_Index + 1;
1044 Discard := Goto_Next_Argument_In_Section (Parser);
1045 end Internal_Initialize_Option_Scan;
1052 (Parser : Opt_Parser := Command_Line_Parser) return String
1055 if Parser.The_Parameter.First > Parser.The_Parameter.Last then
1056 return String'(1 .. 0 => ' ');
1058 return Argument
(Parser
, Parser
.The_Parameter
.Arg_Num
)
1059 (Parser
.The_Parameter
.First
.. Parser
.The_Parameter
.Last
);
1068 (Parser
: Opt_Parser
:= Command_Line_Parser
) return Character
1071 return Parser
.The_Separator
;
1078 procedure Set_Parameter
1079 (Variable
: out Parameter_Type
;
1083 Extra
: Character := ASCII
.NUL
)
1086 Variable
.Arg_Num
:= Arg_Num
;
1087 Variable
.First
:= First
;
1088 Variable
.Last
:= Last
;
1089 Variable
.Extra
:= Extra
;
1092 ---------------------
1093 -- Start_Expansion --
1094 ---------------------
1096 procedure Start_Expansion
1097 (Iterator
: out Expansion_Iterator
;
1099 Directory
: String := "";
1100 Basic_Regexp
: Boolean := True)
1102 Directory_Separator
: Character;
1103 pragma Import
(C
, Directory_Separator
, "__gnat_dir_separator");
1105 First
: Positive := Pattern
'First;
1106 Pat
: String := Pattern
;
1109 Canonical_Case_File_Name
(Pat
);
1110 Iterator
.Current_Depth
:= 1;
1112 -- If Directory is unspecified, use the current directory ("./" or ".\")
1114 if Directory
= "" then
1115 Iterator
.Dir_Name
(1 .. 2) := "." & Directory_Separator
;
1116 Iterator
.Start
:= 3;
1119 Iterator
.Dir_Name
(1 .. Directory
'Length) := Directory
;
1120 Iterator
.Start
:= Directory
'Length + 1;
1121 Canonical_Case_File_Name
(Iterator
.Dir_Name
(1 .. Directory
'Length));
1123 -- Make sure that the last character is a directory separator
1125 if Directory
(Directory
'Last) /= Directory_Separator
then
1126 Iterator
.Dir_Name
(Iterator
.Start
) := Directory_Separator
;
1127 Iterator
.Start
:= Iterator
.Start
+ 1;
1131 Iterator
.Levels
(1).Name_Last
:= Iterator
.Start
- 1;
1133 -- Open the initial Directory, at depth 1
1135 GNAT
.Directory_Operations
.Open
1136 (Iterator
.Levels
(1).Dir
, Iterator
.Dir_Name
(1 .. Iterator
.Start
- 1));
1138 -- If in the current directory and the pattern starts with "./" or ".\",
1139 -- drop the "./" or ".\" from the pattern.
1141 if Directory
= "" and then Pat
'Length > 2
1142 and then Pat
(Pat
'First) = '.'
1143 and then Pat
(Pat
'First + 1) = Directory_Separator
1145 First
:= Pat
'First + 2;
1149 GNAT
.Regexp
.Compile
(Pat
(First
.. Pat
'Last), Basic_Regexp
, True);
1151 Iterator
.Maximum_Depth
:= 1;
1153 -- Maximum_Depth is equal to 1 plus the number of directory separators
1156 for Index
in First
.. Pat
'Last loop
1157 if Pat
(Index
) = Directory_Separator
then
1158 Iterator
.Maximum_Depth
:= Iterator
.Maximum_Depth
+ 1;
1159 exit when Iterator
.Maximum_Depth
= Max_Depth
;
1162 end Start_Expansion
;
1168 procedure Free
(Parser
: in out Opt_Parser
) is
1169 procedure Unchecked_Free
is new
1170 Ada
.Unchecked_Deallocation
(Opt_Parser_Data
, Opt_Parser
);
1173 and then Parser
/= Command_Line_Parser
1175 Free
(Parser
.Arguments
);
1176 Unchecked_Free
(Parser
);
1184 procedure Define_Alias
1185 (Config
: in out Command_Line_Configuration
;
1188 Section
: String := "")
1190 Def
: Alias_Definition
;
1192 if Config
= null then
1193 Config
:= new Command_Line_Configuration_Record
;
1196 Def
.Alias
:= new String'(Switch);
1197 Def.Expansion := new String'(Expanded
);
1198 Def
.Section
:= new String'(Section);
1199 Add (Config.Aliases, Def);
1206 procedure Define_Prefix
1207 (Config : in out Command_Line_Configuration;
1211 if Config = null then
1212 Config := new Command_Line_Configuration_Record;
1215 Add (Config.Prefixes, new String'(Prefix
));
1223 (Config
: in out Command_Line_Configuration
;
1224 Switch
: Switch_Definition
)
1226 procedure Unchecked_Free
is new Ada
.Unchecked_Deallocation
1227 (Switch_Definitions
, Switch_Definitions_List
);
1229 Tmp
: Switch_Definitions_List
;
1232 if Config
= null then
1233 Config
:= new Command_Line_Configuration_Record
;
1236 Tmp
:= Config
.Switches
;
1239 Config
.Switches
:= new Switch_Definitions
(1 .. 1);
1241 Config
.Switches
:= new Switch_Definitions
(1 .. Tmp
'Length + 1);
1242 Config
.Switches
(1 .. Tmp
'Length) := Tmp
.all;
1243 Unchecked_Free
(Tmp
);
1246 if Switch
.Switch
/= null and then Switch
.Switch
.all = "*" then
1247 Config
.Star_Switch
:= True;
1250 Config
.Switches
(Config
.Switches
'Last) := Switch
;
1257 procedure Add
(Def
: in out Alias_Definitions_List
;
1258 Alias
: Alias_Definition
)
1260 procedure Unchecked_Free
is new
1261 Ada
.Unchecked_Deallocation
1262 (Alias_Definitions
, Alias_Definitions_List
);
1264 Tmp
: Alias_Definitions_List
:= Def
;
1268 Def
:= new Alias_Definitions
(1 .. 1);
1270 Def
:= new Alias_Definitions
(1 .. Tmp
'Length + 1);
1271 Def
(1 .. Tmp
'Length) := Tmp
.all;
1272 Unchecked_Free
(Tmp
);
1275 Def
(Def
'Last) := Alias
;
1278 ---------------------------
1279 -- Initialize_Switch_Def --
1280 ---------------------------
1282 procedure Initialize_Switch_Def
1283 (Def
: out Switch_Definition
;
1284 Switch
: String := "";
1285 Long_Switch
: String := "";
1286 Help
: String := "";
1287 Section
: String := "")
1289 P1
, P2
: Switch_Parameter_Type
:= Parameter_None
;
1290 Last1
, Last2
: Integer;
1293 if Switch
/= "" then
1294 Def
.Switch
:= new String'(Switch);
1295 Decompose_Switch (Switch, P1, Last1);
1298 if Long_Switch /= "" then
1299 Def.Long_Switch := new String'(Long_Switch
);
1300 Decompose_Switch
(Long_Switch
, P2
, Last2
);
1303 if Switch
/= "" and then Long_Switch
/= "" then
1304 if (P1
= Parameter_None
and then P2
/= P1
)
1305 or else (P2
= Parameter_None
and then P1
/= P2
)
1306 or else (P1
= Parameter_Optional
and then P2
/= P1
)
1307 or else (P2
= Parameter_Optional
and then P2
/= P1
)
1309 raise Invalid_Switch
1310 with "Inconsistent parameter types for "
1311 & Switch
& " and " & Long_Switch
;
1315 if Section
/= "" then
1316 Def
.Section
:= new String'(Section);
1320 Def.Help := new String'(Help
);
1322 end Initialize_Switch_Def
;
1328 procedure Define_Switch
1329 (Config
: in out Command_Line_Configuration
;
1330 Switch
: String := "";
1331 Long_Switch
: String := "";
1332 Help
: String := "";
1333 Section
: String := "")
1335 Def
: Switch_Definition
;
1337 if Switch
/= "" or else Long_Switch
/= "" then
1338 Initialize_Switch_Def
(Def
, Switch
, Long_Switch
, Help
, Section
);
1347 procedure Define_Switch
1348 (Config
: in out Command_Line_Configuration
;
1349 Output
: access Boolean;
1350 Switch
: String := "";
1351 Long_Switch
: String := "";
1352 Help
: String := "";
1353 Section
: String := "";
1354 Value
: Boolean := True)
1356 Def
: Switch_Definition
(Switch_Boolean
);
1358 if Switch
/= "" or else Long_Switch
/= "" then
1359 Initialize_Switch_Def
(Def
, Switch
, Long_Switch
, Help
, Section
);
1360 Def
.Boolean_Output
:= Output
.all'Unchecked_Access;
1361 Def
.Boolean_Value
:= Value
;
1370 procedure Define_Switch
1371 (Config
: in out Command_Line_Configuration
;
1372 Output
: access Integer;
1373 Switch
: String := "";
1374 Long_Switch
: String := "";
1375 Help
: String := "";
1376 Section
: String := "";
1377 Initial
: Integer := 0;
1378 Default
: Integer := 1)
1380 Def
: Switch_Definition
(Switch_Integer
);
1382 if Switch
/= "" or else Long_Switch
/= "" then
1383 Initialize_Switch_Def
(Def
, Switch
, Long_Switch
, Help
, Section
);
1384 Def
.Integer_Output
:= Output
.all'Unchecked_Access;
1385 Def
.Integer_Default
:= Default
;
1386 Def
.Integer_Initial
:= Initial
;
1395 procedure Define_Switch
1396 (Config
: in out Command_Line_Configuration
;
1397 Output
: access GNAT
.Strings
.String_Access
;
1398 Switch
: String := "";
1399 Long_Switch
: String := "";
1400 Help
: String := "";
1401 Section
: String := "")
1403 Def
: Switch_Definition
(Switch_String
);
1405 if Switch
/= "" or else Long_Switch
/= "" then
1406 Initialize_Switch_Def
(Def
, Switch
, Long_Switch
, Help
, Section
);
1407 Def
.String_Output
:= Output
.all'Unchecked_Access;
1412 --------------------
1413 -- Define_Section --
1414 --------------------
1416 procedure Define_Section
1417 (Config
: in out Command_Line_Configuration
;
1421 if Config
= null then
1422 Config
:= new Command_Line_Configuration_Record
;
1425 Add
(Config
.Sections
, new String'(Section));
1428 --------------------
1429 -- Foreach_Switch --
1430 --------------------
1432 procedure Foreach_Switch
1433 (Config : Command_Line_Configuration;
1437 if Config /= null and then Config.Switches /= null then
1438 for J in Config.Switches'Range loop
1439 if (Section = "" and then Config.Switches (J).Section = null)
1441 (Config.Switches (J).Section /= null
1442 and then Config.Switches (J).Section.all = Section)
1444 exit when Config.Switches (J).Switch /= null
1445 and then not Callback (Config.Switches (J).Switch.all, J);
1447 exit when Config.Switches (J).Long_Switch /= null
1449 not Callback (Config.Switches (J).Long_Switch.all, J);
1459 function Get_Switches
1460 (Config : Command_Line_Configuration;
1461 Switch_Char : Character := '-';
1462 Section : String := "") return String
1464 Ret : Ada.Strings.Unbounded.Unbounded_String;
1465 use Ada.Strings.Unbounded;
1467 function Add_Switch (S : String; Index : Integer) return Boolean;
1468 -- Add a switch to Ret
1474 function Add_Switch (S : String; Index : Integer) return Boolean is
1475 pragma Unreferenced (Index);
1478 Ret := "*" & Ret; -- Always first
1479 elsif S (S'First) = Switch_Char then
1480 Append (Ret, " " & S (S'First + 1 .. S'Last));
1482 Append (Ret, " " & S);
1489 pragma Unreferenced (Tmp);
1491 procedure Foreach is new Foreach_Switch (Add_Switch);
1493 -- Start of processing for Get_Switches
1496 if Config = null then
1500 Foreach (Config, Section => Section);
1502 -- Adding relevant aliases
1504 if Config.Aliases /= null then
1505 for A in Config.Aliases'Range loop
1506 if Config.Aliases (A).Section.all = Section then
1507 Tmp := Add_Switch (Config.Aliases (A).Alias.all, -1);
1512 return To_String (Ret);
1515 ------------------------
1516 -- Section_Delimiters --
1517 ------------------------
1519 function Section_Delimiters
1520 (Config : Command_Line_Configuration) return String
1522 use Ada.Strings.Unbounded;
1523 Result : Unbounded_String;
1526 if Config /= null and then Config.Sections /= null then
1527 for S in Config.Sections'Range loop
1528 Append (Result, " " & Config.Sections (S).all);
1532 return To_String (Result);
1533 end Section_Delimiters;
1535 -----------------------
1536 -- Set_Configuration --
1537 -----------------------
1539 procedure Set_Configuration
1540 (Cmd : in out Command_Line;
1541 Config : Command_Line_Configuration)
1544 Cmd.Config := Config;
1545 end Set_Configuration;
1547 -----------------------
1548 -- Get_Configuration --
1549 -----------------------
1551 function Get_Configuration
1552 (Cmd : Command_Line) return Command_Line_Configuration
1556 end Get_Configuration;
1558 ----------------------
1559 -- Set_Command_Line --
1560 ----------------------
1562 procedure Set_Command_Line
1563 (Cmd : in out Command_Line;
1565 Getopt_Description : String := "";
1566 Switch_Char : Character := '-')
1568 Tmp : Argument_List_Access;
1569 Parser : Opt_Parser;
1571 Section : String_Access := null;
1573 function Real_Full_Switch
1575 Parser : Opt_Parser) return String;
1576 -- Ensure that the returned switch value contains the
1577 -- Switch_Char prefix if needed.
1579 ----------------------
1580 -- Real_Full_Switch --
1581 ----------------------
1583 function Real_Full_Switch
1585 Parser : Opt_Parser) return String
1589 return Full_Switch (Parser);
1591 return Switch_Char & Full_Switch (Parser);
1593 end Real_Full_Switch;
1595 -- Start of processing for Set_Command_Line
1598 Free (Cmd.Expanded);
1601 if Switches /= "" then
1602 Tmp := Argument_String_To_List (Switches);
1603 Initialize_Option_Scan (Parser, Tmp, Switch_Char);
1607 if Cmd.Config /= null then
1609 -- Do not use Getopt_Description in this case. Otherwise,
1610 -- if we have defined a prefix -gnaty, and two switches
1611 -- -gnatya and -gnatyL!, we would have a different behavior
1612 -- depending on the order of switches:
1614 -- -gnatyL1a => -gnatyL with argument "1a"
1615 -- -gnatyaL1 => -gnatya and -gnatyL with argument "1"
1617 -- This is because the call to Getopt below knows nothing
1618 -- about prefixes, and in the first case finds a valid
1619 -- switch with arguments, so returns it without analyzing
1620 -- the argument. In the second case, the switch matches "*",
1621 -- and is then decomposed below.
1623 S := Getopt (Switches => "*",
1624 Concatenate => False,
1628 S := Getopt (Switches => "* " & Getopt_Description,
1629 Concatenate => False,
1633 exit when S = ASCII.NUL;
1636 Sw : constant String := Real_Full_Switch (S, Parser);
1637 Is_Section : Boolean := False;
1640 if Cmd.Config /= null
1641 and then Cmd.Config.Sections /= null
1644 for S in Cmd.Config.Sections'Range loop
1645 if Sw = Cmd.Config.Sections (S).all then
1646 Section := Cmd.Config.Sections (S);
1649 exit Section_Search;
1651 end loop Section_Search;
1654 if not Is_Section then
1655 if Section = null then
1656 Add_Switch (Cmd, Sw, Parameter (Parser));
1659 (Cmd, Sw, Parameter (Parser),
1660 Section => Section.all);
1666 when Invalid_Parameter =>
1668 -- Add it with no parameter, if that's the way the user
1671 -- Specify the separator in all cases, as the switch might
1672 -- need to be unaliased, and the alias might contain
1673 -- switches with parameters.
1675 if Section = null then
1677 (Cmd, Switch_Char & Full_Switch (Parser));
1680 (Cmd, Switch_Char & Full_Switch (Parser),
1681 Section => Section.all);
1688 end Set_Command_Line;
1697 Substring : String) return Boolean
1700 return Index + Substring'Length - 1 <= Type_Str'Last
1701 and then Type_Str (Index .. Index + Substring'Length - 1) = Substring;
1704 ------------------------
1705 -- Can_Have_Parameter --
1706 ------------------------
1708 function Can_Have_Parameter (S : String) return Boolean is
1710 if S'Length <= 1 then
1715 when '!' | ':' | '?
' | '=' =>
1720 end Can_Have_Parameter;
1722 -----------------------
1723 -- Require_Parameter --
1724 -----------------------
1726 function Require_Parameter (S : String) return Boolean is
1728 if S'Length <= 1 then
1733 when '!' | ':' | '=' =>
1738 end Require_Parameter;
1744 function Actual_Switch (S : String) return String is
1746 if S'Length <= 1 then
1751 when '!' | ':' | '?
' | '=' =>
1752 return S (S'First .. S'Last - 1);
1758 ----------------------------
1759 -- For_Each_Simple_Switch --
1760 ----------------------------
1762 procedure For_Each_Simple_Switch
1763 (Config : Command_Line_Configuration;
1766 Parameter : String := "";
1767 Unalias : Boolean := True)
1769 function Group_Analysis
1771 Group : String) return Boolean;
1772 -- Perform the analysis of a group of switches
1774 Found_In_Config : Boolean := False;
1775 function Is_In_Config
1776 (Config_Switch : String; Index : Integer) return Boolean;
1777 -- If Switch is the same as Config_Switch, run the callback and sets
1778 -- Found_In_Config to True.
1780 function Starts_With
1781 (Config_Switch : String; Index : Integer) return Boolean;
1782 -- if Switch starts with Config_Switch, sets Found_In_Config to True.
1783 -- The return value is for the Foreach_Switch iterator.
1785 --------------------
1786 -- Group_Analysis --
1787 --------------------
1789 function Group_Analysis
1791 Group : String) return Boolean
1796 function Analyze_Simple_Switch
1797 (Switch : String; Index : Integer) return Boolean;
1798 -- "Switches" is one of the switch definitions passed to the
1799 -- configuration, not one of the switches found on the command line.
1801 ---------------------------
1802 -- Analyze_Simple_Switch --
1803 ---------------------------
1805 function Analyze_Simple_Switch
1806 (Switch : String; Index : Integer) return Boolean
1808 pragma Unreferenced (Index);
1810 Full : constant String := Prefix & Group (Idx .. Group'Last);
1812 Sw : constant String := Actual_Switch (Switch);
1813 -- Switches definition minus argument definition
1819 -- Verify that sw starts with Prefix
1821 if Looking_At (Sw, Sw'First, Prefix)
1823 -- Verify that the group starts with sw
1825 and then Looking_At (Full, Full'First, Sw)
1827 Last := Idx + Sw'Length - Prefix'Length - 1;
1830 if Can_Have_Parameter (Switch) then
1832 -- Include potential parameter to the recursive call. Only
1833 -- numbers are allowed.
1835 while Last < Group'Last
1836 and then Group (Last + 1) in '0' .. '9'
1842 if not Require_Parameter (Switch) or else Last >= Param then
1843 if Idx = Group'First
1844 and then Last = Group'Last
1845 and then Last < Param
1847 -- The group only concerns a single switch. Do not
1848 -- perform recursive call.
1850 -- Note that we still perform a recursive call if
1851 -- a parameter is detected in the switch, as this
1852 -- is a way to correctly identify such a parameter
1860 -- Recursive call, using the detected parameter if any
1862 if Last >= Param then
1863 For_Each_Simple_Switch
1866 Prefix & Group (Idx .. Param - 1),
1867 Group (Param .. Last));
1870 For_Each_Simple_Switch
1871 (Config, Section, Prefix & Group (Idx .. Last), "");
1880 end Analyze_Simple_Switch;
1882 procedure Foreach is new Foreach_Switch (Analyze_Simple_Switch);
1884 -- Start of processing for Group_Analysis
1888 while Idx <= Group'Last loop
1890 Foreach (Config, Section);
1893 For_Each_Simple_Switch
1894 (Config, Section, Prefix & Group (Idx), "");
1906 function Is_In_Config
1907 (Config_Switch : String; Index : Integer) return Boolean
1910 P : Switch_Parameter_Type;
1913 Decompose_Switch (Config_Switch, P, Last);
1915 if Config_Switch (Config_Switch'First .. Last) = Switch then
1917 when Parameter_None =>
1918 if Parameter = "" then
1919 Callback (Switch, "", "", Index => Index);
1920 Found_In_Config := True;
1924 when Parameter_With_Optional_Space =>
1925 Callback (Switch, " ", Parameter, Index => Index);
1926 Found_In_Config := True;
1929 when Parameter_With_Space_Or_Equal =>
1930 Callback (Switch, "=", Parameter, Index => Index);
1931 Found_In_Config := True;
1934 when Parameter_No_Space =>
1935 Callback (Switch, "", Parameter, Index);
1936 Found_In_Config := True;
1939 when Parameter_Optional =>
1940 Callback (Switch, "", Parameter, Index);
1941 Found_In_Config := True;
1953 function Starts_With
1954 (Config_Switch : String; Index : Integer) return Boolean
1958 P : Switch_Parameter_Type;
1961 -- This function is called when we believe the parameter was
1962 -- specified as part of the switch, instead of separately. Thus we
1963 -- look in the config to find all possible switches.
1965 Decompose_Switch (Config_Switch, P, Last);
1968 (Switch, Switch'First,
1969 Config_Switch (Config_Switch'First .. Last))
1971 -- Set first char of Param, and last char of Switch
1973 Param := Switch'First + Last;
1974 Last := Switch'First + Last - Config_Switch'First;
1978 -- None is already handled in Is_In_Config
1980 when Parameter_None =>
1983 when Parameter_With_Space_Or_Equal =>
1984 if Param <= Switch'Last
1986 (Switch (Param) = ' ' or else Switch (Param) = '=')
1988 Callback (Switch (Switch'First .. Last),
1989 "=", Switch (Param + 1 .. Switch'Last), Index);
1990 Found_In_Config := True;
1994 when Parameter_With_Optional_Space =>
1995 if Param <= Switch'Last and then Switch (Param) = ' ' then
1999 Callback (Switch (Switch'First .. Last),
2000 " ", Switch (Param .. Switch'Last), Index);
2001 Found_In_Config := True;
2004 when Parameter_No_Space | Parameter_Optional =>
2005 Callback (Switch (Switch'First .. Last),
2006 "", Switch (Param .. Switch'Last), Index);
2007 Found_In_Config := True;
2014 procedure Foreach_In_Config is new Foreach_Switch (Is_In_Config);
2015 procedure Foreach_Starts_With is new Foreach_Switch (Starts_With);
2017 -- Start of processing for For_Each_Simple_Switch
2020 -- First determine if the switch corresponds to one belonging to the
2021 -- configuration. If so, run callback and exit.
2023 -- ??? Is this necessary. On simple tests, we seem to have the same
2024 -- results with or without this call.
2026 Foreach_In_Config (Config, Section);
2028 if Found_In_Config then
2032 -- If adding a switch that can in fact be expanded through aliases,
2033 -- add separately each of its expansions.
2035 -- This takes care of expansions like "-T" -> "-gnatwrs", where the
2036 -- alias and its expansion do not have the same prefix. Given the order
2037 -- in which we do things here, the expansion of the alias will itself
2038 -- be checked for a common prefix and split into simple switches.
2041 and then Config /= null
2042 and then Config.Aliases /= null
2044 for A in Config.Aliases'Range loop
2045 if Config.Aliases (A).Section.all = Section
2046 and then Config.Aliases (A).Alias.all = Switch
2047 and then Parameter = ""
2049 For_Each_Simple_Switch
2050 (Config, Section, Config.Aliases (A).Expansion.all, "");
2056 -- If adding a switch grouping several switches, add each of the simple
2057 -- switches instead.
2059 if Config /= null and then Config.Prefixes /= null then
2060 for P in Config.Prefixes'Range loop
2061 if Switch'Length > Config.Prefixes (P)'Length + 1
2063 Looking_At (Switch, Switch'First, Config.Prefixes (P).all)
2065 -- Alias expansion will be done recursively
2067 if Config.Switches = null then
2068 for S in Switch'First + Config.Prefixes (P)'Length
2071 For_Each_Simple_Switch
2073 Config.Prefixes (P).all & Switch (S), "");
2078 elsif Group_Analysis
2079 (Config.Prefixes (P).all,
2081 (Switch'First + Config.Prefixes (P)'Length .. Switch'Last))
2083 -- Recursive calls already done on each switch of the group:
2084 -- Return without executing Callback.
2092 -- Test if added switch is a known switch with parameter attached
2093 -- instead of being specified separately
2096 and then Config /= null
2097 and then Config.Switches /= null
2099 Found_In_Config := False;
2100 Foreach_Starts_With (Config, Section);
2102 if Found_In_Config then
2107 -- The switch is invalid in the config, but we still want to report it.
2108 -- The config could, for instance, include "*" to specify it accepts
2111 Callback (Switch, " ", Parameter, Index => -1);
2112 end For_Each_Simple_Switch;
2118 procedure Add_Switch
2119 (Cmd : in out Command_Line;
2121 Parameter : String := "";
2122 Separator : Character := ASCII.NUL;
2123 Section : String := "";
2124 Add_Before : Boolean := False)
2127 pragma Unreferenced (Success);
2129 Add_Switch (Cmd, Switch, Parameter, Separator,
2130 Section, Add_Before, Success);
2137 procedure Add_Switch
2138 (Cmd : in out Command_Line;
2140 Parameter : String := "";
2141 Separator : Character := ASCII.NUL;
2142 Section : String := "";
2143 Add_Before : Boolean := False;
2144 Success : out Boolean)
2146 procedure Add_Simple_Switch
2151 -- Add a new switch that has had all its aliases expanded, and switches
2152 -- ungrouped. We know there are no more aliases in Switches.
2154 -----------------------
2155 -- Add_Simple_Switch --
2156 -----------------------
2158 procedure Add_Simple_Switch
2168 and then Cmd.Config /= null
2169 and then not Cmd.Config.Star_Switch
2171 raise Invalid_Switch
2172 with "Invalid switch " & Simple;
2175 if Separator /= ASCII.NUL then
2178 elsif Sepa = "" then
2181 Sep := Sepa (Sepa'First);
2184 if Cmd.Expanded = null then
2185 Cmd.Expanded := new Argument_List'(1 .. 1 => new String'(Simple));
2189 new Argument_List'(1 .. 1 => new String'(Sep & Param));
2191 Cmd.Params := new Argument_List'(1 .. 1 => null);
2194 if Section
= "" then
2195 Cmd
.Sections
:= new Argument_List
'(1 .. 1 => null);
2198 new Argument_List'(1 .. 1 => new String'(Section));
2202 -- Do we already have this switch?
2204 for C in Cmd.Expanded'Range loop
2205 if Cmd.Expanded (C).all = Simple
2207 ((Cmd.Params (C) = null and then Param = "")
2209 (Cmd.Params (C) /= null
2210 and then Cmd.Params (C).all = Sep & Param))
2212 ((Cmd.Sections (C) = null and then Section = "")
2214 (Cmd.Sections (C) /= null
2215 and then Cmd.Sections (C).all = Section))
2221 -- Inserting at least one switch
2224 Add (Cmd.Expanded, new String'(Simple
), Add_Before
);
2229 new String'(Sep & Param),
2238 if Section = "" then
2246 new String'(Section
),
2250 end Add_Simple_Switch
;
2252 procedure Add_Simple_Switches
is
2253 new For_Each_Simple_Switch
(Add_Simple_Switch
);
2257 Section_Valid
: Boolean := False;
2259 -- Start of processing for Add_Switch
2262 if Section
/= "" and then Cmd
.Config
/= null then
2263 for S
in Cmd
.Config
.Sections
'Range loop
2264 if Section
= Cmd
.Config
.Sections
(S
).all then
2265 Section_Valid
:= True;
2270 if not Section_Valid
then
2271 raise Invalid_Section
;
2276 Add_Simple_Switches
(Cmd
.Config
, Section
, Switch
, Parameter
);
2277 Free
(Cmd
.Coalesce
);
2284 procedure Remove
(Line
: in out Argument_List_Access
; Index
: Integer) is
2285 Tmp
: Argument_List_Access
:= Line
;
2288 Line
:= new Argument_List
(Tmp
'First .. Tmp
'Last - 1);
2290 if Index
/= Tmp
'First then
2291 Line
(Tmp
'First .. Index
- 1) := Tmp
(Tmp
'First .. Index
- 1);
2296 if Index
/= Tmp
'Last then
2297 Line
(Index
.. Tmp
'Last - 1) := Tmp
(Index
+ 1 .. Tmp
'Last);
2300 Unchecked_Free
(Tmp
);
2308 (Line
: in out Argument_List_Access
;
2309 Str
: String_Access
;
2310 Before
: Boolean := False)
2312 Tmp
: Argument_List_Access
:= Line
;
2316 Line
:= new Argument_List
(Tmp
'First .. Tmp
'Last + 1);
2319 Line
(Tmp
'First) := Str
;
2320 Line
(Tmp
'First + 1 .. Tmp
'Last + 1) := Tmp
.all;
2322 Line
(Tmp
'Range) := Tmp
.all;
2323 Line
(Tmp
'Last + 1) := Str
;
2326 Unchecked_Free
(Tmp
);
2329 Line
:= new Argument_List
'(1 .. 1 => Str);
2337 procedure Remove_Switch
2338 (Cmd : in out Command_Line;
2340 Remove_All : Boolean := False;
2341 Has_Parameter : Boolean := False;
2342 Section : String := "")
2345 pragma Unreferenced (Success);
2347 Remove_Switch (Cmd, Switch, Remove_All, Has_Parameter, Section, Success);
2354 procedure Remove_Switch
2355 (Cmd : in out Command_Line;
2357 Remove_All : Boolean := False;
2358 Has_Parameter : Boolean := False;
2359 Section : String := "";
2360 Success : out Boolean)
2362 procedure Remove_Simple_Switch
2363 (Simple, Separator, Param : String; Index : Integer);
2364 -- Removes a simple switch, with no aliasing or grouping
2366 --------------------------
2367 -- Remove_Simple_Switch --
2368 --------------------------
2370 procedure Remove_Simple_Switch
2371 (Simple, Separator, Param : String; Index : Integer)
2374 pragma Unreferenced (Param, Separator, Index);
2377 if Cmd.Expanded /= null then
2378 C := Cmd.Expanded'First;
2379 while C <= Cmd.Expanded'Last loop
2380 if Cmd.Expanded (C).all = Simple
2383 or else (Cmd.Sections (C) = null
2384 and then Section = "")
2385 or else (Cmd.Sections (C) /= null
2386 and then Section = Cmd.Sections (C).all))
2387 and then (not Has_Parameter or else Cmd.Params (C) /= null)
2389 Remove (Cmd.Expanded, C);
2390 Remove (Cmd.Params, C);
2391 Remove (Cmd.Sections, C);
2394 if not Remove_All then
2403 end Remove_Simple_Switch;
2405 procedure Remove_Simple_Switches is
2406 new For_Each_Simple_Switch (Remove_Simple_Switch);
2408 -- Start of processing for Remove_Switch
2412 Remove_Simple_Switches
2413 (Cmd.Config, Section, Switch, "", Unalias => not Has_Parameter);
2414 Free (Cmd.Coalesce);
2421 procedure Remove_Switch
2422 (Cmd : in out Command_Line;
2425 Section : String := "")
2427 procedure Remove_Simple_Switch
2428 (Simple, Separator, Param : String; Index : Integer);
2429 -- Removes a simple switch, with no aliasing or grouping
2431 --------------------------
2432 -- Remove_Simple_Switch --
2433 --------------------------
2435 procedure Remove_Simple_Switch
2436 (Simple, Separator, Param : String; Index : Integer)
2438 pragma Unreferenced (Separator, Index);
2442 if Cmd.Expanded /= null then
2443 C := Cmd.Expanded'First;
2444 while C <= Cmd.Expanded'Last loop
2445 if Cmd.Expanded (C).all = Simple
2447 ((Cmd.Sections (C) = null
2448 and then Section = "")
2450 (Cmd.Sections (C) /= null
2451 and then Section = Cmd.Sections (C).all))
2453 ((Cmd.Params (C) = null and then Param = "")
2455 (Cmd.Params (C) /= null
2458 -- Ignore the separator stored in Parameter
2460 Cmd.Params (C) (Cmd.Params (C)'First + 1
2461 .. Cmd.Params (C)'Last) =
2464 Remove (Cmd.Expanded, C);
2465 Remove (Cmd.Params, C);
2466 Remove (Cmd.Sections, C);
2468 -- The switch is necessarily unique by construction of
2478 end Remove_Simple_Switch;
2480 procedure Remove_Simple_Switches is
2481 new For_Each_Simple_Switch (Remove_Simple_Switch);
2483 -- Start of processing for Remove_Switch
2486 Remove_Simple_Switches (Cmd.Config, Section, Switch, Parameter);
2487 Free (Cmd.Coalesce);
2490 --------------------
2491 -- Group_Switches --
2492 --------------------
2494 procedure Group_Switches
2495 (Cmd : Command_Line;
2496 Result : Argument_List_Access;
2497 Sections : Argument_List_Access;
2498 Params : Argument_List_Access)
2500 function Compatible_Parameter (Param : String_Access) return Boolean;
2501 -- True when the parameter can be part of a group
2503 --------------------------
2504 -- Compatible_Parameter --
2505 --------------------------
2507 function Compatible_Parameter (Param : String_Access) return Boolean is
2511 if Param = null then
2514 -- We need parameters without separators
2516 elsif Param (Param'First) /= ASCII.NUL then
2519 -- Parameters must be all digits
2522 for J in Param'First + 1 .. Param'Last loop
2523 if Param (J) not in '0' .. '9' then
2530 end Compatible_Parameter;
2532 -- Local declarations
2534 Group : Ada.Strings.Unbounded.Unbounded_String;
2536 use type Ada.Strings.Unbounded.Unbounded_String;
2538 -- Start of processing for Group_Switches
2541 if Cmd.Config = null
2542 or else Cmd.Config.Prefixes = null
2547 for P in Cmd.Config.Prefixes'Range loop
2548 Group := Ada.Strings.Unbounded.Null_Unbounded_String;
2551 for C in Result'Range loop
2552 if Result (C) /= null
2553 and then Compatible_Parameter (Params (C))
2557 Cmd.Config.Prefixes (P).all)
2559 -- If we are still in the same section, group the switches
2563 (Sections (C) = null
2564 and then Sections (First) = null)
2566 (Sections (C) /= null
2567 and then Sections (First) /= null
2568 and then Sections (C).all = Sections (First).all)
2573 (Result (C)'First + Cmd.Config.Prefixes (P)'Length ..
2576 if Params (C) /= null then
2579 Params (C) (Params (C)'First + 1 .. Params (C)'Last);
2589 -- We changed section: we put the grouped switches to the first
2590 -- place, on continue with the new section.
2595 (Cmd
.Config
.Prefixes
(P
).all &
2596 Ada
.Strings
.Unbounded
.To_String
(Group
));
2598 Ada
.Strings
.Unbounded
.To_Unbounded_String
2600 (Result
(C
)'First + Cmd
.Config
.Prefixes
(P
)'Length ..
2610 (Cmd.Config.Prefixes (P).all &
2611 Ada.Strings.Unbounded.To_String (Group));
2616 --------------------
2617 -- Alias_Switches --
2618 --------------------
2620 procedure Alias_Switches
2621 (Cmd : Command_Line;
2622 Result : Argument_List_Access;
2623 Params : Argument_List_Access)
2628 procedure Check_Cb (Switch, Separator, Param : String; Index : Integer);
2629 -- Checks whether the command line contains [Switch].
2630 -- Sets the global variable [Found] appropriately.
2631 -- This will be called for each simple switch that make up an alias, to
2632 -- know whether the alias should be applied.
2634 procedure Remove_Cb (Switch, Separator, Param : String; Index : Integer);
2635 -- Remove the simple switch [Switch] from the command line, since it is
2636 -- part of a simpler alias
2643 (Switch, Separator, Param : String; Index : Integer)
2645 pragma Unreferenced (Separator, Index);
2649 for E in Result'Range loop
2650 if Result (E) /= null
2653 or else Params (E) (Params (E)'First + 1 ..
2654 Params (E)'Last) = Param)
2655 and then Result (E).all = Switch
2669 procedure Remove_Cb (Switch, Separator, Param : String; Index : Integer)
2671 pragma Unreferenced (Separator, Index);
2674 for E in Result'Range loop
2675 if Result (E) /= null
2678 or else Params (E) (Params (E)'First + 1
2679 .. Params (E)'Last) = Param)
2680 and then Result (E).all = Switch
2693 procedure Check_All is new For_Each_Simple_Switch (Check_Cb);
2694 procedure Remove_All is new For_Each_Simple_Switch (Remove_Cb);
2696 -- Start of processing for Alias_Switches
2699 if Cmd.Config = null
2700 or else Cmd.Config.Aliases = null
2705 for A in Cmd.Config.Aliases'Range loop
2707 -- Compute the various simple switches that make up the alias. We
2708 -- split the expansion into as many simple switches as possible, and
2709 -- then check whether the expanded command line has all of them.
2712 Check_All (Cmd.Config,
2713 Switch => Cmd.Config.Aliases (A).Expansion.all,
2714 Section => Cmd.Config.Aliases (A).Section.all);
2717 First := Integer'Last;
2718 Remove_All (Cmd.Config,
2719 Switch => Cmd.Config.Aliases (A).Expansion.all,
2720 Section => Cmd.Config.Aliases (A).Section.all);
2721 Result (First) := new String'(Cmd
.Config
.Aliases
(A
).Alias
.all);
2730 procedure Sort_Sections
2731 (Line
: GNAT
.OS_Lib
.Argument_List_Access
;
2732 Sections
: GNAT
.OS_Lib
.Argument_List_Access
;
2733 Params
: GNAT
.OS_Lib
.Argument_List_Access
)
2735 Sections_List
: Argument_List_Access
:=
2736 new Argument_List
'(1 .. 1 => null);
2738 Old_Line : constant Argument_List := Line.all;
2739 Old_Sections : constant Argument_List := Sections.all;
2740 Old_Params : constant Argument_List := Params.all;
2748 -- First construct a list of all sections
2750 for E in Line'Range loop
2751 if Sections (E) /= null then
2753 for S in Sections_List'Range loop
2754 if (Sections_List (S) = null and then Sections (E) = null)
2756 (Sections_List (S) /= null
2757 and then Sections (E) /= null
2758 and then Sections_List (S).all = Sections (E).all)
2766 Add (Sections_List, Sections (E));
2771 Index := Line'First;
2773 for S in Sections_List'Range loop
2774 for E in Old_Line'Range loop
2775 if (Sections_List (S) = null and then Old_Sections (E) = null)
2777 (Sections_List (S) /= null
2778 and then Old_Sections (E) /= null
2779 and then Sections_List (S).all = Old_Sections (E).all)
2781 Line (Index) := Old_Line (E);
2782 Sections (Index) := Old_Sections (E);
2783 Params (Index) := Old_Params (E);
2789 Unchecked_Free (Sections_List);
2797 (Cmd : in out Command_Line;
2798 Iter : in out Command_Line_Iterator;
2799 Expanded : Boolean := False)
2802 if Cmd.Expanded = null then
2807 -- Reorder the expanded line so that sections are grouped
2809 Sort_Sections (Cmd.Expanded, Cmd.Sections, Cmd.Params);
2811 -- Coalesce the switches as much as possible
2814 and then Cmd.Coalesce = null
2816 Cmd.Coalesce := new Argument_List (Cmd.Expanded'Range);
2817 for E in Cmd.Expanded'Range loop
2818 Cmd.Coalesce (E) := new String'(Cmd
.Expanded
(E
).all);
2821 Free
(Cmd
.Coalesce_Sections
);
2822 Cmd
.Coalesce_Sections
:= new Argument_List
(Cmd
.Sections
'Range);
2823 for E
in Cmd
.Sections
'Range loop
2824 Cmd
.Coalesce_Sections
(E
) :=
2825 (if Cmd
.Sections
(E
) = null then null
2826 else new String'(Cmd.Sections (E).all));
2829 Free (Cmd.Coalesce_Params);
2830 Cmd.Coalesce_Params := new Argument_List (Cmd.Params'Range);
2831 for E in Cmd.Params'Range loop
2832 Cmd.Coalesce_Params (E) :=
2833 (if Cmd.Params (E) = null then null
2834 else new String'(Cmd
.Params
(E
).all));
2837 -- Not a clone, since we will not modify the parameters anyway
2839 Alias_Switches
(Cmd
, Cmd
.Coalesce
, Cmd
.Coalesce_Params
);
2841 (Cmd
, Cmd
.Coalesce
, Cmd
.Coalesce_Sections
, Cmd
.Coalesce_Params
);
2845 Iter
.List
:= Cmd
.Expanded
;
2846 Iter
.Params
:= Cmd
.Params
;
2847 Iter
.Sections
:= Cmd
.Sections
;
2849 Iter
.List
:= Cmd
.Coalesce
;
2850 Iter
.Params
:= Cmd
.Coalesce_Params
;
2851 Iter
.Sections
:= Cmd
.Coalesce_Sections
;
2854 if Iter
.List
= null then
2855 Iter
.Current
:= Integer'Last;
2857 Iter
.Current
:= Iter
.List
'First - 1;
2862 --------------------
2863 -- Current_Switch --
2864 --------------------
2866 function Current_Switch
(Iter
: Command_Line_Iterator
) return String is
2868 return Iter
.List
(Iter
.Current
).all;
2871 --------------------
2872 -- Is_New_Section --
2873 --------------------
2875 function Is_New_Section
(Iter
: Command_Line_Iterator
) return Boolean is
2876 Section
: constant String := Current_Section
(Iter
);
2879 if Iter
.Sections
= null then
2882 elsif Iter
.Current
= Iter
.Sections
'First
2883 or else Iter
.Sections
(Iter
.Current
- 1) = null
2885 return Section
/= "";
2888 return Section
/= Iter
.Sections
(Iter
.Current
- 1).all;
2892 ---------------------
2893 -- Current_Section --
2894 ---------------------
2896 function Current_Section
(Iter
: Command_Line_Iterator
) return String is
2898 if Iter
.Sections
= null
2899 or else Iter
.Current
> Iter
.Sections
'Last
2900 or else Iter
.Sections
(Iter
.Current
) = null
2905 return Iter
.Sections
(Iter
.Current
).all;
2906 end Current_Section
;
2908 -----------------------
2909 -- Current_Separator --
2910 -----------------------
2912 function Current_Separator
(Iter
: Command_Line_Iterator
) return String is
2914 if Iter
.Params
= null
2915 or else Iter
.Current
> Iter
.Params
'Last
2916 or else Iter
.Params
(Iter
.Current
) = null
2922 Sep
: constant Character :=
2923 Iter
.Params
(Iter
.Current
) (Iter
.Params
(Iter
.Current
)'First);
2925 if Sep
= ASCII
.NUL
then
2932 end Current_Separator
;
2934 -----------------------
2935 -- Current_Parameter --
2936 -----------------------
2938 function Current_Parameter
(Iter
: Command_Line_Iterator
) return String is
2940 if Iter
.Params
= null
2941 or else Iter
.Current
> Iter
.Params
'Last
2942 or else Iter
.Params
(Iter
.Current
) = null
2947 -- Return result, skipping separator
2950 P
: constant String := Iter
.Params
(Iter
.Current
).all;
2952 return P
(P
'First + 1 .. P
'Last);
2955 end Current_Parameter
;
2961 function Has_More
(Iter
: Command_Line_Iterator
) return Boolean is
2963 return Iter
.List
/= null and then Iter
.Current
<= Iter
.List
'Last;
2970 procedure Next
(Iter
: in out Command_Line_Iterator
) is
2972 Iter
.Current
:= Iter
.Current
+ 1;
2973 while Iter
.Current
<= Iter
.List
'Last
2974 and then Iter
.List
(Iter
.Current
) = null
2976 Iter
.Current
:= Iter
.Current
+ 1;
2984 procedure Free
(Config
: in out Command_Line_Configuration
) is
2985 procedure Unchecked_Free
is new
2986 Ada
.Unchecked_Deallocation
2987 (Switch_Definitions
, Switch_Definitions_List
);
2989 procedure Unchecked_Free
is new
2990 Ada
.Unchecked_Deallocation
2991 (Alias_Definitions
, Alias_Definitions_List
);
2994 if Config
/= null then
2995 Free
(Config
.Prefixes
);
2996 Free
(Config
.Sections
);
2997 Free
(Config
.Usage
);
2999 Free
(Config
.Help_Msg
);
3001 if Config
.Aliases
/= null then
3002 for A
in Config
.Aliases
'Range loop
3003 Free
(Config
.Aliases
(A
).Alias
);
3004 Free
(Config
.Aliases
(A
).Expansion
);
3005 Free
(Config
.Aliases
(A
).Section
);
3008 Unchecked_Free
(Config
.Aliases
);
3011 if Config
.Switches
/= null then
3012 for S
in Config
.Switches
'Range loop
3013 Free
(Config
.Switches
(S
).Switch
);
3014 Free
(Config
.Switches
(S
).Long_Switch
);
3015 Free
(Config
.Switches
(S
).Help
);
3016 Free
(Config
.Switches
(S
).Section
);
3019 Unchecked_Free
(Config
.Switches
);
3022 Unchecked_Free
(Config
);
3030 procedure Free
(Cmd
: in out Command_Line
) is
3032 Free
(Cmd
.Expanded
);
3033 Free
(Cmd
.Coalesce
);
3034 Free
(Cmd
.Coalesce_Sections
);
3035 Free
(Cmd
.Coalesce_Params
);
3037 Free
(Cmd
.Sections
);
3045 (Config
: in out Command_Line_Configuration
;
3046 Usage
: String := "[switches] [arguments]";
3047 Help
: String := "";
3048 Help_Msg
: String := "")
3051 if Config
= null then
3052 Config
:= new Command_Line_Configuration_Record
;
3055 Free
(Config
.Usage
);
3057 Free
(Config
.Help_Msg
);
3059 Config
.Usage
:= new String'(Usage);
3060 Config.Help := new String'(Help
);
3061 Config
.Help_Msg
:= new String'(Help_Msg);
3068 procedure Display_Help (Config : Command_Line_Configuration) is
3069 function Switch_Name
3070 (Def : Switch_Definition;
3071 Section : String) return String;
3072 -- Return the "-short, --long=ARG" string for Def.
3073 -- Returns "" if the switch is not in the section.
3076 (P : Switch_Parameter_Type;
3077 Name : String := "ARG") return String;
3078 -- Return the display for a switch parameter
3080 procedure Display_Section_Help (Section : String);
3081 -- Display the help for a specific section ("" is the default section)
3083 --------------------------
3084 -- Display_Section_Help --
3085 --------------------------
3087 procedure Display_Section_Help (Section : String) is
3088 Max_Len : Natural := 0;
3091 -- ??? Special display for "*"
3095 if Section /= "" then
3096 Put_Line ("Switches after " & Section);
3099 -- Compute size of the switches column
3101 for S in Config.Switches'Range loop
3102 Max_Len := Natural'Max
3103 (Max_Len, Switch_Name (Config.Switches (S), Section)'Length);
3106 if Config.Aliases /= null then
3107 for A in Config.Aliases'Range loop
3108 if Config.Aliases (A).Section.all = Section then
3109 Max_Len := Natural'Max
3110 (Max_Len, Config.Aliases (A).Alias'Length);
3115 -- Display the switches
3117 for S in Config.Switches'Range loop
3119 N : constant String :=
3120 Switch_Name (Config.Switches (S), Section);
3126 Put ((1 .. Max_Len - N'Length + 1 => ' '));
3128 if Config.Switches (S).Help /= null then
3129 Put (Config.Switches (S).Help.all);
3137 -- Display the aliases
3139 if Config.Aliases /= null then
3140 for A in Config.Aliases'Range loop
3141 if Config.Aliases (A).Section.all = Section then
3143 Put (Config.Aliases (A).Alias.all);
3144 Put ((1 .. Max_Len - Config.Aliases (A).Alias'Length + 1
3146 Put ("Equivalent to " & Config.Aliases (A).Expansion.all);
3151 end Display_Section_Help;
3158 (P : Switch_Parameter_Type;
3159 Name : String := "ARG") return String
3163 when Parameter_None =>
3166 when Parameter_With_Optional_Space =>
3167 return " " & To_Upper (Name);
3169 when Parameter_With_Space_Or_Equal =>
3170 return "=" & To_Upper (Name);
3172 when Parameter_No_Space =>
3173 return To_Upper (Name);
3175 when Parameter_Optional =>
3176 return '[' & To_Upper (Name) & ']';
3184 function Switch_Name
3185 (Def : Switch_Definition;
3186 Section : String) return String
3188 use Ada.Strings.Unbounded;
3189 Result : Unbounded_String;
3190 P1, P2 : Switch_Parameter_Type;
3191 Last1, Last2 : Integer := 0;
3194 if (Section = "" and then Def.Section = null)
3195 or else (Def.Section /= null and then Def.Section.all = Section)
3197 if Def.Switch /= null and then Def.Switch.all = "*" then
3198 return "[any switch]";
3201 if Def.Switch /= null then
3202 Decompose_Switch (Def.Switch.all, P1, Last1);
3203 Append (Result, Def.Switch (Def.Switch'First .. Last1));
3205 if Def.Long_Switch /= null then
3206 Decompose_Switch (Def.Long_Switch.all, P2, Last2);
3207 Append (Result, ", "
3208 & Def.Long_Switch (Def.Long_Switch'First .. Last2));
3209 Append (Result, Param_Name (P2, "ARG"));
3212 Append (Result, Param_Name (P1, "ARG"));
3215 else -- Long_Switch necessarily not null
3216 Decompose_Switch (Def.Long_Switch.all, P2, Last2);
3218 Def.Long_Switch (Def.Long_Switch'First .. Last2));
3219 Append (Result, Param_Name (P2, "ARG"));
3223 return To_String (Result);
3226 -- Start of processing for Display_Help
3229 if Config = null then
3233 if Config.Help /= null and then Config.Help.all /= "" then
3234 Put_Line (Config.Help.all);
3237 if Config.Usage /= null then
3240 (Ada.Command_Line.Command_Name) & " " & Config.Usage.all);
3242 Put_Line ("Usage: " & Base_Name (Ada.Command_Line.Command_Name)
3243 & " [switches] [arguments]");
3246 if Config.Help_Msg /= null and then Config.Help_Msg.all /= "" then
3247 Put_Line (Config.Help_Msg.all);
3250 Display_Section_Help ("");
3252 if Config.Sections /= null and then Config.Switches /= null then
3253 for S in Config.Sections'Range loop
3254 Display_Section_Help (Config.Sections (S).all);
3265 (Config : Command_Line_Configuration;
3266 Callback : Switch_Handler := null;
3267 Parser : Opt_Parser := Command_Line_Parser;
3268 Concatenate : Boolean := True)
3270 Getopt_Switches : String_Access;
3271 C : Character := ASCII.NUL;
3273 Empty_Name : aliased constant String := "";
3274 Current_Section : Integer := -1;
3275 Section_Name : not null access constant String := Empty_Name'Access;
3277 procedure Simple_Callback
3278 (Simple_Switch : String;
3282 -- Needs comments ???
3284 procedure Do_Callback (Switch, Parameter : String; Index : Integer);
3290 procedure Do_Callback (Switch, Parameter : String; Index : Integer) is
3292 -- Do automatic handling when possible
3295 case Config.Switches (Index).Typ is
3296 when Switch_Untyped =>
3297 null; -- no automatic handling
3299 when Switch_Boolean =>
3300 Config.Switches (Index).Boolean_Output.all :=
3301 Config.Switches (Index).Boolean_Value;
3304 when Switch_Integer =>
3306 if Parameter = "" then
3307 Config.Switches (Index).Integer_Output.all :=
3308 Config.Switches (Index).Integer_Default;
3310 Config.Switches (Index).Integer_Output.all :=
3311 Integer'Value (Parameter);
3315 when Constraint_Error =>
3316 raise Invalid_Parameter
3317 with "Expected integer parameter for '"
3323 when Switch_String =>
3324 Free (Config.Switches (Index).String_Output.all);
3325 Config.Switches (Index).String_Output.all :=
3326 new String'(Parameter
);
3332 -- Otherwise calls the user callback if one was defined
3334 if Callback
/= null then
3335 Callback
(Switch
=> Switch
,
3336 Parameter
=> Parameter
,
3337 Section
=> Section_Name
.all);
3341 procedure For_Each_Simple
3342 is new For_Each_Simple_Switch
(Simple_Callback
);
3344 ---------------------
3345 -- Simple_Callback --
3346 ---------------------
3348 procedure Simple_Callback
3349 (Simple_Switch
: String;
3354 pragma Unreferenced
(Separator
);
3356 Do_Callback
(Switch
=> Simple_Switch
,
3357 Parameter
=> Parameter
,
3359 end Simple_Callback
;
3361 -- Start of processing for Getopt
3364 -- Initialize sections
3366 if Config
.Sections
= null then
3367 Config
.Sections
:= new Argument_List
'(1 .. 0 => null);
3370 Internal_Initialize_Option_Scan
3372 Switch_Char => Parser.Switch_Character,
3373 Stop_At_First_Non_Switch => Parser.Stop_At_First,
3374 Section_Delimiters => Section_Delimiters (Config));
3376 Getopt_Switches := new String'
3377 (Get_Switches
(Config
, Parser
.Switch_Character
, Section_Name
.all)
3380 -- Initialize output values for automatically handled switches
3382 for S
in Config
.Switches
'Range loop
3383 case Config
.Switches
(S
).Typ
is
3384 when Switch_Untyped
=>
3385 null; -- Nothing to do
3387 when Switch_Boolean
=>
3388 Config
.Switches
(S
).Boolean_Output
.all :=
3389 not Config
.Switches
(S
).Boolean_Value
;
3391 when Switch_Integer
=>
3392 Config
.Switches
(S
).Integer_Output
.all :=
3393 Config
.Switches
(S
).Integer_Initial
;
3395 when Switch_String
=>
3396 Config
.Switches
(S
).String_Output
.all := new String'("");
3400 -- For all sections, and all switches within those sections
3403 C := Getopt (Switches => Getopt_Switches.all,
3404 Concatenate => Concatenate,
3408 -- Full_Switch already includes the leading '-'
3410 Do_Callback (Switch => Full_Switch (Parser),
3411 Parameter => Parameter (Parser),
3414 elsif C /= ASCII.NUL then
3415 if Full_Switch (Parser) = "h"
3417 Full_Switch (Parser) = "-help"
3419 Display_Help (Config);
3420 raise Exit_From_Command_Line;
3423 -- Do switch expansion if needed
3427 Section => Section_Name.all,
3428 Switch => Parser.Switch_Character & Full_Switch (Parser),
3429 Parameter => Parameter (Parser));
3432 if Current_Section = -1 then
3433 Current_Section := Config.Sections'First;
3435 Current_Section := Current_Section + 1;
3438 exit when Current_Section > Config.Sections'Last;
3440 Section_Name := Config.Sections (Current_Section);
3441 Goto_Section (Section_Name.all, Parser);
3443 Free (Getopt_Switches);
3444 Getopt_Switches := new String'
3446 (Config
, Parser
.Switch_Character
, Section_Name
.all));
3450 Free
(Getopt_Switches
);
3453 when Invalid_Switch
=>
3454 Free
(Getopt_Switches
);
3456 -- Message inspired by "ls" on Unix
3458 Put_Line
(Standard_Error
,
3459 Base_Name
(Ada
.Command_Line
.Command_Name
)
3460 & ": unrecognized option '"
3461 & Parser
.Switch_Character
& Full_Switch
(Parser
)
3463 Put_Line
(Standard_Error
,
3465 & Base_Name
(Ada
.Command_Line
.Command_Name
)
3466 & " --help` for more information.");
3471 Free
(Getopt_Switches
);
3480 (Line
: in out Command_Line
;
3481 Args
: out GNAT
.OS_Lib
.Argument_List_Access
;
3482 Expanded
: Boolean := False;
3483 Switch_Char
: Character := '-')
3485 Iter
: Command_Line_Iterator
;
3486 Count
: Natural := 0;
3489 Start
(Line
, Iter
, Expanded
=> Expanded
);
3490 while Has_More
(Iter
) loop
3491 if Is_New_Section
(Iter
) then
3499 Args
:= new Argument_List
(1 .. Count
);
3500 Count
:= Args
'First;
3502 Start
(Line
, Iter
, Expanded
=> Expanded
);
3503 while Has_More
(Iter
) loop
3504 if Is_New_Section
(Iter
) then
3505 Args
(Count
) := new String'(Switch_Char & Current_Section (Iter));
3509 Args (Count) := new String'(Current_Switch
(Iter
)
3510 & Current_Separator
(Iter
)
3511 & Current_Parameter
(Iter
));
3517 end GNAT
.Command_Line
;