[gcc]
[official-gcc.git] / gcc / ada / g-comlin.adb
blobdc279153542f14ec7efced72e4ece5a92c73521e
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT COMPILER COMPONENTS --
4 -- --
5 -- G N A T . C O M M A N D _ L I N E --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 1999-2017, Free Software Foundation, Inc. --
10 -- --
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. --
17 -- --
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. --
21 -- --
22 -- You should have received a copy of the GNU General Public License and --
23 -- a copy of the GCC Runtime Library Exception along with this program; --
24 -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see --
25 -- <http://www.gnu.org/licenses/>. --
26 -- --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
29 -- --
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 -- General note: this entire body could use much more commenting. There
43 -- are large sections of uncommented code throughout, and many formal
44 -- parameters of local subprograms are not documented at all ???
46 package CL renames Ada.Command_Line;
48 type Switch_Parameter_Type is
49 (Parameter_None,
50 Parameter_With_Optional_Space, -- ':' in getopt
51 Parameter_With_Space_Or_Equal, -- '=' in getopt
52 Parameter_No_Space, -- '!' in getopt
53 Parameter_Optional); -- '?' in getopt
55 procedure Set_Parameter
56 (Variable : out Parameter_Type;
57 Arg_Num : Positive;
58 First : Positive;
59 Last : Natural;
60 Extra : Character := ASCII.NUL);
61 pragma Inline (Set_Parameter);
62 -- Set the parameter that will be returned by Parameter below
64 -- Extra is a character that needs to be added when reporting Full_Switch.
65 -- (it will in general be the switch character, for instance '-').
66 -- Otherwise, Full_Switch will report 'f' instead of '-f'. In particular,
67 -- it needs to be set when reporting an invalid switch or handling '*'.
69 -- Parameters need to be defined ???
71 function Goto_Next_Argument_In_Section (Parser : Opt_Parser) return Boolean;
72 -- Go to the next argument on the command line. If we are at the end of
73 -- the current section, we want to make sure there is no other identical
74 -- section on the command line (there might be multiple instances of
75 -- -largs). Returns True iff there is another argument.
77 function Get_File_Names_Case_Sensitive return Integer;
78 pragma Import (C, Get_File_Names_Case_Sensitive,
79 "__gnat_get_file_names_case_sensitive");
81 File_Names_Case_Sensitive : constant Boolean :=
82 Get_File_Names_Case_Sensitive /= 0;
84 procedure Canonical_Case_File_Name (S : in out String);
85 -- Given a file name, converts it to canonical case form. For systems where
86 -- file names are case sensitive, this procedure has no effect. If file
87 -- names are not case sensitive (i.e. for example if you have the file
88 -- "xyz.adb", you can refer to it as XYZ.adb or XyZ.AdB), then this call
89 -- converts the given string to canonical all lower case form, so that two
90 -- file names compare equal if they refer to the same file.
92 procedure Internal_Initialize_Option_Scan
93 (Parser : Opt_Parser;
94 Switch_Char : Character;
95 Stop_At_First_Non_Switch : Boolean;
96 Section_Delimiters : String);
97 -- Initialize Parser, which must have been allocated already
99 function Argument (Parser : Opt_Parser; Index : Integer) return String;
100 -- Return the index-th command line argument
102 procedure Find_Longest_Matching_Switch
103 (Switches : String;
104 Arg : String;
105 Index_In_Switches : out Integer;
106 Switch_Length : out Integer;
107 Param : out Switch_Parameter_Type);
108 -- Return the Longest switch from Switches that at least partially matches
109 -- Arg. Index_In_Switches is set to 0 if none matches. What are other
110 -- parameters??? in particular Param is not always set???
112 procedure Unchecked_Free is new Ada.Unchecked_Deallocation
113 (Argument_List, Argument_List_Access);
115 procedure Unchecked_Free is new Ada.Unchecked_Deallocation
116 (Command_Line_Configuration_Record, Command_Line_Configuration);
118 procedure Remove (Line : in out Argument_List_Access; Index : Integer);
119 -- Remove a specific element from Line
121 procedure Add
122 (Line : in out Argument_List_Access;
123 Str : String_Access;
124 Before : Boolean := False);
125 -- Add a new element to Line. If Before is True, the item is inserted at
126 -- the beginning, else it is appended.
128 procedure Add
129 (Config : in out Command_Line_Configuration;
130 Switch : Switch_Definition);
131 procedure Add
132 (Def : in out Alias_Definitions_List;
133 Alias : Alias_Definition);
134 -- Add a new element to Def
136 procedure Initialize_Switch_Def
137 (Def : out Switch_Definition;
138 Switch : String := "";
139 Long_Switch : String := "";
140 Help : String := "";
141 Section : String := "";
142 Argument : String := "ARG");
143 -- Initialize [Def] with the contents of the other parameters.
144 -- This also checks consistency of the switch parameters, and will raise
145 -- Invalid_Switch if they do not match.
147 procedure Decompose_Switch
148 (Switch : String;
149 Parameter_Type : out Switch_Parameter_Type;
150 Switch_Last : out Integer);
151 -- Given a switch definition ("name:" for instance), extracts the type of
152 -- parameter that is expected, and the name of the switch
154 function Can_Have_Parameter (S : String) return Boolean;
155 -- True if S can have a parameter
157 function Require_Parameter (S : String) return Boolean;
158 -- True if S requires a parameter
160 function Actual_Switch (S : String) return String;
161 -- Remove any possible trailing '!', ':', '?' and '='
163 generic
164 with procedure Callback
165 (Simple_Switch : String;
166 Separator : String;
167 Parameter : String;
168 Index : Integer); -- Index in Config.Switches, or -1
169 procedure For_Each_Simple_Switch
170 (Config : Command_Line_Configuration;
171 Section : String;
172 Switch : String;
173 Parameter : String := "";
174 Unalias : Boolean := True);
175 -- Breaks Switch into as simple switches as possible (expanding aliases and
176 -- ungrouping common prefixes when possible), and call Callback for each of
177 -- these.
179 procedure Sort_Sections
180 (Line : not null GNAT.OS_Lib.Argument_List_Access;
181 Sections : GNAT.OS_Lib.Argument_List_Access;
182 Params : GNAT.OS_Lib.Argument_List_Access);
183 -- Reorder the command line switches so that the switches belonging to a
184 -- section are grouped together.
186 procedure Group_Switches
187 (Cmd : Command_Line;
188 Result : Argument_List_Access;
189 Sections : Argument_List_Access;
190 Params : Argument_List_Access);
191 -- Group switches with common prefixes whenever possible. Once they have
192 -- been grouped, we also check items for possible aliasing.
194 procedure Alias_Switches
195 (Cmd : Command_Line;
196 Result : Argument_List_Access;
197 Params : Argument_List_Access);
198 -- When possible, replace one or more switches by an alias, i.e. a shorter
199 -- version.
201 function Looking_At
202 (Type_Str : String;
203 Index : Natural;
204 Substring : String) return Boolean;
205 -- Return True if the characters starting at Index in Type_Str are
206 -- equivalent to Substring.
208 generic
209 with function Callback (S : String; Index : Integer) return Boolean;
210 procedure Foreach_Switch
211 (Config : Command_Line_Configuration;
212 Section : String);
213 -- Iterate over all switches defined in Config, for a specific section.
214 -- Index is set to the index in Config.Switches. Stop iterating when
215 -- Callback returns False.
217 --------------
218 -- Argument --
219 --------------
221 function Argument (Parser : Opt_Parser; Index : Integer) return String is
222 begin
223 if Parser.Arguments /= null then
224 return Parser.Arguments (Index + Parser.Arguments'First - 1).all;
225 else
226 return CL.Argument (Index);
227 end if;
228 end Argument;
230 ------------------------------
231 -- Canonical_Case_File_Name --
232 ------------------------------
234 procedure Canonical_Case_File_Name (S : in out String) is
235 begin
236 if not File_Names_Case_Sensitive then
237 for J in S'Range loop
238 if S (J) in 'A' .. 'Z' then
239 S (J) := Character'Val
240 (Character'Pos (S (J)) +
241 (Character'Pos ('a') - Character'Pos ('A')));
242 end if;
243 end loop;
244 end if;
245 end Canonical_Case_File_Name;
247 ---------------
248 -- Expansion --
249 ---------------
251 function Expansion (Iterator : Expansion_Iterator) return String is
252 type Pointer is access all Expansion_Iterator;
254 It : constant Pointer := Iterator'Unrestricted_Access;
255 S : String (1 .. 1024);
256 Last : Natural;
258 Current : Depth := It.Current_Depth;
259 NL : Positive;
261 begin
262 -- It is assumed that a directory is opened at the current level.
263 -- Otherwise GNAT.Directory_Operations.Directory_Error will be raised
264 -- at the first call to Read.
266 loop
267 Read (It.Levels (Current).Dir, S, Last);
269 -- If we have exhausted the directory, close it and go back one level
271 if Last = 0 then
272 Close (It.Levels (Current).Dir);
274 -- If we are at level 1, we are finished; return an empty string
276 if Current = 1 then
277 return String'(1 .. 0 => ' ');
279 -- Otherwise continue with the directory at the previous level
281 else
282 Current := Current - 1;
283 It.Current_Depth := Current;
284 end if;
286 -- If this is a directory, that is neither "." or "..", attempt to
287 -- go to the next level.
289 elsif Is_Directory
290 (It.Dir_Name (1 .. It.Levels (Current).Name_Last) &
291 S (1 .. Last))
292 and then S (1 .. Last) /= "."
293 and then S (1 .. Last) /= ".."
294 then
295 -- We can go to the next level only if we have not reached the
296 -- maximum depth,
298 if Current < It.Maximum_Depth then
299 NL := It.Levels (Current).Name_Last;
301 -- And if relative path of this new directory is not too long
303 if NL + Last + 1 < Max_Path_Length then
304 Current := Current + 1;
305 It.Current_Depth := Current;
306 It.Dir_Name (NL + 1 .. NL + Last) := S (1 .. Last);
307 NL := NL + Last + 1;
308 It.Dir_Name (NL) := Directory_Separator;
309 It.Levels (Current).Name_Last := NL;
310 Canonical_Case_File_Name (It.Dir_Name (1 .. NL));
312 -- Open the new directory, and read from it
314 GNAT.Directory_Operations.Open
315 (It.Levels (Current).Dir, It.Dir_Name (1 .. NL));
316 end if;
317 end if;
318 end if;
320 -- Check the relative path against the pattern
322 -- Note that we try to match also against directory names, since
323 -- clients of this function may expect to retrieve directories.
325 declare
326 Name : String :=
327 It.Dir_Name (It.Start .. It.Levels (Current).Name_Last)
328 & S (1 .. Last);
330 begin
331 Canonical_Case_File_Name (Name);
333 -- If it matches return the relative path
335 if GNAT.Regexp.Match (Name, Iterator.Regexp) then
336 return Name;
337 end if;
338 end;
339 end loop;
340 end Expansion;
342 ---------------------
343 -- Current_Section --
344 ---------------------
346 function Current_Section
347 (Parser : Opt_Parser := Command_Line_Parser) return String
349 begin
350 if Parser.Current_Section = 1 then
351 return "";
352 end if;
354 for Index in reverse 1 .. Integer'Min (Parser.Current_Argument - 1,
355 Parser.Section'Last)
356 loop
357 if Parser.Section (Index) = 0 then
358 return Argument (Parser, Index);
359 end if;
360 end loop;
362 return "";
363 end Current_Section;
365 -----------------
366 -- Full_Switch --
367 -----------------
369 function Full_Switch
370 (Parser : Opt_Parser := Command_Line_Parser) return String
372 begin
373 if Parser.The_Switch.Extra = ASCII.NUL then
374 return Argument (Parser, Parser.The_Switch.Arg_Num)
375 (Parser.The_Switch.First .. Parser.The_Switch.Last);
376 else
377 return Parser.The_Switch.Extra
378 & Argument (Parser, Parser.The_Switch.Arg_Num)
379 (Parser.The_Switch.First .. Parser.The_Switch.Last);
380 end if;
381 end Full_Switch;
383 ------------------
384 -- Get_Argument --
385 ------------------
387 function Get_Argument
388 (Do_Expansion : Boolean := False;
389 Parser : Opt_Parser := Command_Line_Parser) return String
391 begin
392 if Parser.In_Expansion then
393 declare
394 S : constant String := Expansion (Parser.Expansion_It);
395 begin
396 if S'Length /= 0 then
397 return S;
398 else
399 Parser.In_Expansion := False;
400 end if;
401 end;
402 end if;
404 if Parser.Current_Argument > Parser.Arg_Count then
406 -- If this is the first time this function is called
408 if Parser.Current_Index = 1 then
409 Parser.Current_Argument := 1;
410 while Parser.Current_Argument <= Parser.Arg_Count
411 and then Parser.Section (Parser.Current_Argument) /=
412 Parser.Current_Section
413 loop
414 Parser.Current_Argument := Parser.Current_Argument + 1;
415 end loop;
417 else
418 return String'(1 .. 0 => ' ');
419 end if;
421 elsif Parser.Section (Parser.Current_Argument) = 0 then
422 while Parser.Current_Argument <= Parser.Arg_Count
423 and then Parser.Section (Parser.Current_Argument) /=
424 Parser.Current_Section
425 loop
426 Parser.Current_Argument := Parser.Current_Argument + 1;
427 end loop;
428 end if;
430 Parser.Current_Index := Integer'Last;
432 while Parser.Current_Argument <= Parser.Arg_Count
433 and then Parser.Is_Switch (Parser.Current_Argument)
434 loop
435 Parser.Current_Argument := Parser.Current_Argument + 1;
436 end loop;
438 if Parser.Current_Argument > Parser.Arg_Count then
439 return String'(1 .. 0 => ' ');
440 elsif Parser.Section (Parser.Current_Argument) = 0 then
441 return Get_Argument (Do_Expansion);
442 end if;
444 Parser.Current_Argument := Parser.Current_Argument + 1;
446 -- Could it be a file name with wild cards to expand?
448 if Do_Expansion then
449 declare
450 Arg : constant String :=
451 Argument (Parser, Parser.Current_Argument - 1);
452 begin
453 for Index in Arg'Range loop
454 if Arg (Index) = '*'
455 or else Arg (Index) = '?'
456 or else Arg (Index) = '['
457 then
458 Parser.In_Expansion := True;
459 Start_Expansion (Parser.Expansion_It, Arg);
460 return Get_Argument (Do_Expansion, Parser);
461 end if;
462 end loop;
463 end;
464 end if;
466 return Argument (Parser, Parser.Current_Argument - 1);
467 end Get_Argument;
469 ----------------------
470 -- Decompose_Switch --
471 ----------------------
473 procedure Decompose_Switch
474 (Switch : String;
475 Parameter_Type : out Switch_Parameter_Type;
476 Switch_Last : out Integer)
478 begin
479 if Switch = "" then
480 Parameter_Type := Parameter_None;
481 Switch_Last := Switch'Last;
482 return;
483 end if;
485 case Switch (Switch'Last) is
486 when ':' =>
487 Parameter_Type := Parameter_With_Optional_Space;
488 Switch_Last := Switch'Last - 1;
490 when '=' =>
491 Parameter_Type := Parameter_With_Space_Or_Equal;
492 Switch_Last := Switch'Last - 1;
494 when '!' =>
495 Parameter_Type := Parameter_No_Space;
496 Switch_Last := Switch'Last - 1;
498 when '?' =>
499 Parameter_Type := Parameter_Optional;
500 Switch_Last := Switch'Last - 1;
502 when others =>
503 Parameter_Type := Parameter_None;
504 Switch_Last := Switch'Last;
505 end case;
506 end Decompose_Switch;
508 ----------------------------------
509 -- Find_Longest_Matching_Switch --
510 ----------------------------------
512 procedure Find_Longest_Matching_Switch
513 (Switches : String;
514 Arg : String;
515 Index_In_Switches : out Integer;
516 Switch_Length : out Integer;
517 Param : out Switch_Parameter_Type)
519 Index : Natural;
520 Length : Natural := 1;
521 Last : Natural;
522 P : Switch_Parameter_Type;
524 begin
525 Index_In_Switches := 0;
526 Switch_Length := 0;
528 -- Remove all leading spaces first to make sure that Index points
529 -- at the start of the first switch.
531 Index := Switches'First;
532 while Index <= Switches'Last and then Switches (Index) = ' ' loop
533 Index := Index + 1;
534 end loop;
536 while Index <= Switches'Last loop
538 -- Search the length of the parameter at this position in Switches
540 Length := Index;
541 while Length <= Switches'Last
542 and then Switches (Length) /= ' '
543 loop
544 Length := Length + 1;
545 end loop;
547 -- Length now marks the separator after the current switch. Last will
548 -- mark the last character of the name of the switch.
550 if Length = Index + 1 then
551 P := Parameter_None;
552 Last := Index;
553 else
554 Decompose_Switch (Switches (Index .. Length - 1), P, Last);
555 end if;
557 -- If it is the one we searched, it may be a candidate
559 if Arg'First + Last - Index <= Arg'Last
560 and then Switches (Index .. Last) =
561 Arg (Arg'First .. Arg'First + Last - Index)
562 and then Last - Index + 1 > Switch_Length
563 and then
564 (P /= Parameter_With_Space_Or_Equal
565 or else Arg'Last = Arg'First + Last - Index
566 or else Arg (Arg'First + Last - Index + 1) = '=')
567 then
568 Param := P;
569 Index_In_Switches := Index;
570 Switch_Length := Last - Index + 1;
571 end if;
573 -- Look for the next switch in Switches
575 while Index <= Switches'Last
576 and then Switches (Index) /= ' '
577 loop
578 Index := Index + 1;
579 end loop;
581 Index := Index + 1;
582 end loop;
583 end Find_Longest_Matching_Switch;
585 ------------
586 -- Getopt --
587 ------------
589 function Getopt
590 (Switches : String;
591 Concatenate : Boolean := True;
592 Parser : Opt_Parser := Command_Line_Parser) return Character
594 Dummy : Boolean;
596 begin
597 <<Restart>>
599 -- If we have finished parsing the current command line item (there
600 -- might be multiple switches in a single item), then go to the next
601 -- element.
603 if Parser.Current_Argument > Parser.Arg_Count
604 or else (Parser.Current_Index >
605 Argument (Parser, Parser.Current_Argument)'Last
606 and then not Goto_Next_Argument_In_Section (Parser))
607 then
608 return ASCII.NUL;
609 end if;
611 -- By default, the switch will not have a parameter
613 Parser.The_Parameter :=
614 (Integer'Last, Integer'Last, Integer'Last - 1, ASCII.NUL);
615 Parser.The_Separator := ASCII.NUL;
617 declare
618 Arg : constant String :=
619 Argument (Parser, Parser.Current_Argument);
620 Index_Switches : Natural := 0;
621 Max_Length : Natural := 0;
622 End_Index : Natural;
623 Param : Switch_Parameter_Type;
624 begin
625 -- If we are on a new item, test if this might be a switch
627 if Parser.Current_Index = Arg'First then
628 if Arg = "" or else Arg (Arg'First) /= Parser.Switch_Character then
630 -- If it isn't a switch, return it immediately. We also know it
631 -- isn't the parameter to a previous switch, since that has
632 -- already been handled.
634 if Switches (Switches'First) = '*' then
635 Set_Parameter
636 (Parser.The_Switch,
637 Arg_Num => Parser.Current_Argument,
638 First => Arg'First,
639 Last => Arg'Last);
640 Parser.Is_Switch (Parser.Current_Argument) := True;
641 Dummy := Goto_Next_Argument_In_Section (Parser);
642 return '*';
643 end if;
645 if Parser.Stop_At_First then
646 Parser.Current_Argument := Positive'Last;
647 return ASCII.NUL;
649 elsif not Goto_Next_Argument_In_Section (Parser) then
650 return ASCII.NUL;
652 else
653 -- Recurse to get the next switch on the command line
655 goto Restart;
656 end if;
657 end if;
659 -- We are on the first character of a new command line argument,
660 -- which starts with Switch_Character. Further analysis is needed.
662 Parser.Current_Index := Parser.Current_Index + 1;
663 Parser.Is_Switch (Parser.Current_Argument) := True;
664 end if;
666 Find_Longest_Matching_Switch
667 (Switches => Switches,
668 Arg => Arg (Parser.Current_Index .. Arg'Last),
669 Index_In_Switches => Index_Switches,
670 Switch_Length => Max_Length,
671 Param => Param);
673 -- If switch is not accepted, it is either invalid or is returned
674 -- in the context of '*'.
676 if Index_Switches = 0 then
678 -- Find the current switch that we did not recognize. This is in
679 -- fact difficult because Getopt does not know explicitly about
680 -- short and long switches. Ideally, we would want the following
681 -- behavior:
683 -- * for short switches, with Concatenate:
684 -- if -a is not recognized, and the command line has -daf
685 -- we should report the invalid switch as "-a".
687 -- * for short switches, wihtout Concatenate:
688 -- we should report the invalid switch as "-daf".
690 -- * for long switches:
691 -- if the commadn line is "--long" we should report --long
692 -- as unrecongized.
694 -- Unfortunately, the fact that long switches start with a
695 -- duplicate switch character is just a convention (so we could
696 -- have a long switch "-long" for instance). We'll still rely on
697 -- this convention here to try and get as helpful an error message
698 -- as possible.
700 -- Long switch case (starting with double switch character)
702 if Arg (Arg'First + 1) = Parser.Switch_Character then
703 End_Index := Arg'Last;
705 -- Short switch case
707 else
708 End_Index :=
709 (if Concatenate then Parser.Current_Index else Arg'Last);
710 end if;
712 if Switches /= "" and then Switches (Switches'First) = '*' then
714 -- Always prepend the switch character, so that users know
715 -- that this comes from a switch on the command line. This
716 -- is especially important when Concatenate is False, since
717 -- otherwise the current argument first character is lost.
719 if Parser.Section (Parser.Current_Argument) = 0 then
721 -- A section transition should not be returned to the user
723 Dummy := Goto_Next_Argument_In_Section (Parser);
724 goto Restart;
726 else
727 Set_Parameter
728 (Parser.The_Switch,
729 Arg_Num => Parser.Current_Argument,
730 First => Parser.Current_Index,
731 Last => Arg'Last,
732 Extra => Parser.Switch_Character);
733 Parser.Is_Switch (Parser.Current_Argument) := True;
734 Dummy := Goto_Next_Argument_In_Section (Parser);
735 return '*';
736 end if;
737 end if;
739 if Parser.Current_Index = Arg'First then
740 Set_Parameter
741 (Parser.The_Switch,
742 Arg_Num => Parser.Current_Argument,
743 First => Parser.Current_Index,
744 Last => End_Index);
745 else
746 Set_Parameter
747 (Parser.The_Switch,
748 Arg_Num => Parser.Current_Argument,
749 First => Parser.Current_Index,
750 Last => End_Index,
751 Extra => Parser.Switch_Character);
752 end if;
754 Parser.Current_Index := End_Index + 1;
756 raise Invalid_Switch;
757 end if;
759 End_Index := Parser.Current_Index + Max_Length - 1;
760 Set_Parameter
761 (Parser.The_Switch,
762 Arg_Num => Parser.Current_Argument,
763 First => Parser.Current_Index,
764 Last => End_Index);
766 case Param is
767 when Parameter_With_Optional_Space =>
768 if End_Index < Arg'Last then
769 Set_Parameter
770 (Parser.The_Parameter,
771 Arg_Num => Parser.Current_Argument,
772 First => End_Index + 1,
773 Last => Arg'Last);
774 Dummy := Goto_Next_Argument_In_Section (Parser);
776 elsif Parser.Current_Argument < Parser.Arg_Count
777 and then Parser.Section (Parser.Current_Argument + 1) /= 0
778 then
779 Parser.Current_Argument := Parser.Current_Argument + 1;
780 Parser.The_Separator := ' ';
781 Set_Parameter
782 (Parser.The_Parameter,
783 Arg_Num => Parser.Current_Argument,
784 First => Argument (Parser, Parser.Current_Argument)'First,
785 Last => Argument (Parser, Parser.Current_Argument)'Last);
786 Parser.Is_Switch (Parser.Current_Argument) := True;
787 Dummy := Goto_Next_Argument_In_Section (Parser);
789 else
790 Parser.Current_Index := End_Index + 1;
791 raise Invalid_Parameter;
792 end if;
794 when Parameter_With_Space_Or_Equal =>
796 -- If the switch is of the form <switch>=xxx
798 if End_Index < Arg'Last then
799 if Arg (End_Index + 1) = '='
800 and then End_Index + 1 < Arg'Last
801 then
802 Parser.The_Separator := '=';
803 Set_Parameter
804 (Parser.The_Parameter,
805 Arg_Num => Parser.Current_Argument,
806 First => End_Index + 2,
807 Last => Arg'Last);
808 Dummy := Goto_Next_Argument_In_Section (Parser);
810 else
811 Parser.Current_Index := End_Index + 1;
812 raise Invalid_Parameter;
813 end if;
815 -- Case of switch of the form <switch> xxx
817 elsif Parser.Current_Argument < Parser.Arg_Count
818 and then Parser.Section (Parser.Current_Argument + 1) /= 0
819 then
820 Parser.Current_Argument := Parser.Current_Argument + 1;
821 Parser.The_Separator := ' ';
822 Set_Parameter
823 (Parser.The_Parameter,
824 Arg_Num => Parser.Current_Argument,
825 First => Argument (Parser, Parser.Current_Argument)'First,
826 Last => Argument (Parser, Parser.Current_Argument)'Last);
827 Parser.Is_Switch (Parser.Current_Argument) := True;
828 Dummy := Goto_Next_Argument_In_Section (Parser);
830 else
831 Parser.Current_Index := End_Index + 1;
832 raise Invalid_Parameter;
833 end if;
835 when Parameter_No_Space =>
836 if End_Index < Arg'Last then
837 Set_Parameter
838 (Parser.The_Parameter,
839 Arg_Num => Parser.Current_Argument,
840 First => End_Index + 1,
841 Last => Arg'Last);
842 Dummy := Goto_Next_Argument_In_Section (Parser);
844 else
845 Parser.Current_Index := End_Index + 1;
846 raise Invalid_Parameter;
847 end if;
849 when Parameter_Optional =>
850 if End_Index < Arg'Last then
851 Set_Parameter
852 (Parser.The_Parameter,
853 Arg_Num => Parser.Current_Argument,
854 First => End_Index + 1,
855 Last => Arg'Last);
856 end if;
858 Dummy := Goto_Next_Argument_In_Section (Parser);
860 when Parameter_None =>
861 if Concatenate or else End_Index = Arg'Last then
862 Parser.Current_Index := End_Index + 1;
864 else
865 -- If Concatenate is False and the full argument is not
866 -- recognized as a switch, this is an invalid switch.
868 if Switches (Switches'First) = '*' then
869 Set_Parameter
870 (Parser.The_Switch,
871 Arg_Num => Parser.Current_Argument,
872 First => Arg'First,
873 Last => Arg'Last);
874 Parser.Is_Switch (Parser.Current_Argument) := True;
875 Dummy := Goto_Next_Argument_In_Section (Parser);
876 return '*';
877 end if;
879 Set_Parameter
880 (Parser.The_Switch,
881 Arg_Num => Parser.Current_Argument,
882 First => Parser.Current_Index,
883 Last => Arg'Last,
884 Extra => Parser.Switch_Character);
885 Parser.Current_Index := Arg'Last + 1;
886 raise Invalid_Switch;
887 end if;
888 end case;
890 return Switches (Index_Switches);
891 end;
892 end Getopt;
894 -----------------------------------
895 -- Goto_Next_Argument_In_Section --
896 -----------------------------------
898 function Goto_Next_Argument_In_Section
899 (Parser : Opt_Parser) return Boolean
901 begin
902 Parser.Current_Argument := Parser.Current_Argument + 1;
904 if Parser.Current_Argument > Parser.Arg_Count
905 or else Parser.Section (Parser.Current_Argument) = 0
906 then
907 loop
908 Parser.Current_Argument := Parser.Current_Argument + 1;
910 if Parser.Current_Argument > Parser.Arg_Count then
911 Parser.Current_Index := 1;
912 return False;
913 end if;
915 exit when Parser.Section (Parser.Current_Argument) =
916 Parser.Current_Section;
917 end loop;
918 end if;
920 Parser.Current_Index :=
921 Argument (Parser, Parser.Current_Argument)'First;
923 return True;
924 end Goto_Next_Argument_In_Section;
926 ------------------
927 -- Goto_Section --
928 ------------------
930 procedure Goto_Section
931 (Name : String := "";
932 Parser : Opt_Parser := Command_Line_Parser)
934 Index : Integer;
936 begin
937 Parser.In_Expansion := False;
939 if Name = "" then
940 Parser.Current_Argument := 1;
941 Parser.Current_Index := 1;
942 Parser.Current_Section := 1;
943 return;
944 end if;
946 Index := 1;
947 while Index <= Parser.Arg_Count loop
948 if Parser.Section (Index) = 0
949 and then Argument (Parser, Index) = Parser.Switch_Character & Name
950 then
951 Parser.Current_Argument := Index + 1;
952 Parser.Current_Index := 1;
954 if Parser.Current_Argument <= Parser.Arg_Count then
955 Parser.Current_Section :=
956 Parser.Section (Parser.Current_Argument);
957 end if;
959 -- Exit from loop if we have the start of another section
961 if Index = Parser.Section'Last
962 or else Parser.Section (Index + 1) /= 0
963 then
964 return;
965 end if;
966 end if;
968 Index := Index + 1;
969 end loop;
971 Parser.Current_Argument := Positive'Last;
972 Parser.Current_Index := 2; -- so that Get_Argument returns nothing
973 end Goto_Section;
975 ----------------------------
976 -- Initialize_Option_Scan --
977 ----------------------------
979 procedure Initialize_Option_Scan
980 (Switch_Char : Character := '-';
981 Stop_At_First_Non_Switch : Boolean := False;
982 Section_Delimiters : String := "")
984 begin
985 Internal_Initialize_Option_Scan
986 (Parser => Command_Line_Parser,
987 Switch_Char => Switch_Char,
988 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
989 Section_Delimiters => Section_Delimiters);
990 end Initialize_Option_Scan;
992 ----------------------------
993 -- Initialize_Option_Scan --
994 ----------------------------
996 procedure Initialize_Option_Scan
997 (Parser : out Opt_Parser;
998 Command_Line : GNAT.OS_Lib.Argument_List_Access;
999 Switch_Char : Character := '-';
1000 Stop_At_First_Non_Switch : Boolean := False;
1001 Section_Delimiters : String := "")
1003 begin
1004 Free (Parser);
1006 if Command_Line = null then
1007 Parser := new Opt_Parser_Data (CL.Argument_Count);
1008 Internal_Initialize_Option_Scan
1009 (Parser => Parser,
1010 Switch_Char => Switch_Char,
1011 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
1012 Section_Delimiters => Section_Delimiters);
1013 else
1014 Parser := new Opt_Parser_Data (Command_Line'Length);
1015 Parser.Arguments := Command_Line;
1016 Internal_Initialize_Option_Scan
1017 (Parser => Parser,
1018 Switch_Char => Switch_Char,
1019 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
1020 Section_Delimiters => Section_Delimiters);
1021 end if;
1022 end Initialize_Option_Scan;
1024 -------------------------------------
1025 -- Internal_Initialize_Option_Scan --
1026 -------------------------------------
1028 procedure Internal_Initialize_Option_Scan
1029 (Parser : Opt_Parser;
1030 Switch_Char : Character;
1031 Stop_At_First_Non_Switch : Boolean;
1032 Section_Delimiters : String)
1034 Section_Num : Section_Number;
1035 Section_Index : Integer;
1036 Last : Integer;
1037 Delimiter_Found : Boolean;
1039 Discard : Boolean;
1040 pragma Warnings (Off, Discard);
1042 begin
1043 Parser.Current_Argument := 0;
1044 Parser.Current_Index := 0;
1045 Parser.In_Expansion := False;
1046 Parser.Switch_Character := Switch_Char;
1047 Parser.Stop_At_First := Stop_At_First_Non_Switch;
1048 Parser.Section := (others => 1);
1050 -- If we are using sections, we have to preprocess the command line to
1051 -- delimit them. A section can be repeated, so we just give each item
1052 -- on the command line a section number
1054 Section_Num := 1;
1055 Section_Index := Section_Delimiters'First;
1056 while Section_Index <= Section_Delimiters'Last loop
1057 Last := Section_Index;
1058 while Last <= Section_Delimiters'Last
1059 and then Section_Delimiters (Last) /= ' '
1060 loop
1061 Last := Last + 1;
1062 end loop;
1064 Delimiter_Found := False;
1065 Section_Num := Section_Num + 1;
1067 for Index in 1 .. Parser.Arg_Count loop
1068 pragma Assert (Argument (Parser, Index)'First = 1);
1069 if Argument (Parser, Index) /= ""
1070 and then Argument (Parser, Index)(1) = Parser.Switch_Character
1071 and then
1072 Argument (Parser, Index) = Parser.Switch_Character &
1073 Section_Delimiters
1074 (Section_Index .. Last - 1)
1075 then
1076 Parser.Section (Index) := 0;
1077 Delimiter_Found := True;
1079 elsif Parser.Section (Index) = 0 then
1081 -- A previous section delimiter
1083 Delimiter_Found := False;
1085 elsif Delimiter_Found then
1086 Parser.Section (Index) := Section_Num;
1087 end if;
1088 end loop;
1090 Section_Index := Last + 1;
1091 while Section_Index <= Section_Delimiters'Last
1092 and then Section_Delimiters (Section_Index) = ' '
1093 loop
1094 Section_Index := Section_Index + 1;
1095 end loop;
1096 end loop;
1098 Discard := Goto_Next_Argument_In_Section (Parser);
1099 end Internal_Initialize_Option_Scan;
1101 ---------------
1102 -- Parameter --
1103 ---------------
1105 function Parameter
1106 (Parser : Opt_Parser := Command_Line_Parser) return String
1108 begin
1109 if Parser.The_Parameter.First > Parser.The_Parameter.Last then
1110 return String'(1 .. 0 => ' ');
1111 else
1112 return Argument (Parser, Parser.The_Parameter.Arg_Num)
1113 (Parser.The_Parameter.First .. Parser.The_Parameter.Last);
1114 end if;
1115 end Parameter;
1117 ---------------
1118 -- Separator --
1119 ---------------
1121 function Separator
1122 (Parser : Opt_Parser := Command_Line_Parser) return Character
1124 begin
1125 return Parser.The_Separator;
1126 end Separator;
1128 -------------------
1129 -- Set_Parameter --
1130 -------------------
1132 procedure Set_Parameter
1133 (Variable : out Parameter_Type;
1134 Arg_Num : Positive;
1135 First : Positive;
1136 Last : Natural;
1137 Extra : Character := ASCII.NUL)
1139 begin
1140 Variable.Arg_Num := Arg_Num;
1141 Variable.First := First;
1142 Variable.Last := Last;
1143 Variable.Extra := Extra;
1144 end Set_Parameter;
1146 ---------------------
1147 -- Start_Expansion --
1148 ---------------------
1150 procedure Start_Expansion
1151 (Iterator : out Expansion_Iterator;
1152 Pattern : String;
1153 Directory : String := "";
1154 Basic_Regexp : Boolean := True)
1156 Directory_Separator : Character;
1157 pragma Import (C, Directory_Separator, "__gnat_dir_separator");
1159 First : Positive := Pattern'First;
1160 Pat : String := Pattern;
1162 begin
1163 Canonical_Case_File_Name (Pat);
1164 Iterator.Current_Depth := 1;
1166 -- If Directory is unspecified, use the current directory ("./" or ".\")
1168 if Directory = "" then
1169 Iterator.Dir_Name (1 .. 2) := "." & Directory_Separator;
1170 Iterator.Start := 3;
1172 else
1173 Iterator.Dir_Name (1 .. Directory'Length) := Directory;
1174 Iterator.Start := Directory'Length + 1;
1175 Canonical_Case_File_Name (Iterator.Dir_Name (1 .. Directory'Length));
1177 -- Make sure that the last character is a directory separator
1179 if Directory (Directory'Last) /= Directory_Separator then
1180 Iterator.Dir_Name (Iterator.Start) := Directory_Separator;
1181 Iterator.Start := Iterator.Start + 1;
1182 end if;
1183 end if;
1185 Iterator.Levels (1).Name_Last := Iterator.Start - 1;
1187 -- Open the initial Directory, at depth 1
1189 GNAT.Directory_Operations.Open
1190 (Iterator.Levels (1).Dir, Iterator.Dir_Name (1 .. Iterator.Start - 1));
1192 -- If in the current directory and the pattern starts with "./" or ".\",
1193 -- drop the "./" or ".\" from the pattern.
1195 if Directory = "" and then Pat'Length > 2
1196 and then Pat (Pat'First) = '.'
1197 and then Pat (Pat'First + 1) = Directory_Separator
1198 then
1199 First := Pat'First + 2;
1200 end if;
1202 Iterator.Regexp :=
1203 GNAT.Regexp.Compile (Pat (First .. Pat'Last), Basic_Regexp, True);
1205 Iterator.Maximum_Depth := 1;
1207 -- Maximum_Depth is equal to 1 plus the number of directory separators
1208 -- in the pattern.
1210 for Index in First .. Pat'Last loop
1211 if Pat (Index) = Directory_Separator then
1212 Iterator.Maximum_Depth := Iterator.Maximum_Depth + 1;
1213 exit when Iterator.Maximum_Depth = Max_Depth;
1214 end if;
1215 end loop;
1216 end Start_Expansion;
1218 ----------
1219 -- Free --
1220 ----------
1222 procedure Free (Parser : in out Opt_Parser) is
1223 procedure Unchecked_Free is new
1224 Ada.Unchecked_Deallocation (Opt_Parser_Data, Opt_Parser);
1225 begin
1226 if Parser /= null and then Parser /= Command_Line_Parser then
1227 Free (Parser.Arguments);
1228 Unchecked_Free (Parser);
1229 end if;
1230 end Free;
1232 ------------------
1233 -- Define_Alias --
1234 ------------------
1236 procedure Define_Alias
1237 (Config : in out Command_Line_Configuration;
1238 Switch : String;
1239 Expanded : String;
1240 Section : String := "")
1242 Def : Alias_Definition;
1244 begin
1245 if Config = null then
1246 Config := new Command_Line_Configuration_Record;
1247 end if;
1249 Def.Alias := new String'(Switch);
1250 Def.Expansion := new String'(Expanded);
1251 Def.Section := new String'(Section);
1252 Add (Config.Aliases, Def);
1253 end Define_Alias;
1255 -------------------
1256 -- Define_Prefix --
1257 -------------------
1259 procedure Define_Prefix
1260 (Config : in out Command_Line_Configuration;
1261 Prefix : String)
1263 begin
1264 if Config = null then
1265 Config := new Command_Line_Configuration_Record;
1266 end if;
1268 Add (Config.Prefixes, new String'(Prefix));
1269 end Define_Prefix;
1271 ---------
1272 -- Add --
1273 ---------
1275 procedure Add
1276 (Config : in out Command_Line_Configuration;
1277 Switch : Switch_Definition)
1279 procedure Unchecked_Free is new Ada.Unchecked_Deallocation
1280 (Switch_Definitions, Switch_Definitions_List);
1282 Tmp : Switch_Definitions_List;
1284 begin
1285 if Config = null then
1286 Config := new Command_Line_Configuration_Record;
1287 end if;
1289 Tmp := Config.Switches;
1291 if Tmp = null then
1292 Config.Switches := new Switch_Definitions (1 .. 1);
1293 else
1294 Config.Switches := new Switch_Definitions (1 .. Tmp'Length + 1);
1295 Config.Switches (1 .. Tmp'Length) := Tmp.all;
1296 Unchecked_Free (Tmp);
1297 end if;
1299 if Switch.Switch /= null and then Switch.Switch.all = "*" then
1300 Config.Star_Switch := True;
1301 end if;
1303 Config.Switches (Config.Switches'Last) := Switch;
1304 end Add;
1306 ---------
1307 -- Add --
1308 ---------
1310 procedure Add
1311 (Def : in out Alias_Definitions_List;
1312 Alias : Alias_Definition)
1314 procedure Unchecked_Free is new
1315 Ada.Unchecked_Deallocation
1316 (Alias_Definitions, Alias_Definitions_List);
1318 Tmp : Alias_Definitions_List := Def;
1320 begin
1321 if Tmp = null then
1322 Def := new Alias_Definitions (1 .. 1);
1323 else
1324 Def := new Alias_Definitions (1 .. Tmp'Length + 1);
1325 Def (1 .. Tmp'Length) := Tmp.all;
1326 Unchecked_Free (Tmp);
1327 end if;
1329 Def (Def'Last) := Alias;
1330 end Add;
1332 ---------------------------
1333 -- Initialize_Switch_Def --
1334 ---------------------------
1336 procedure Initialize_Switch_Def
1337 (Def : out Switch_Definition;
1338 Switch : String := "";
1339 Long_Switch : String := "";
1340 Help : String := "";
1341 Section : String := "";
1342 Argument : String := "ARG")
1344 P1, P2 : Switch_Parameter_Type := Parameter_None;
1345 Last1, Last2 : Integer;
1347 begin
1348 if Switch /= "" then
1349 Def.Switch := new String'(Switch);
1350 Decompose_Switch (Switch, P1, Last1);
1351 end if;
1353 if Long_Switch /= "" then
1354 Def.Long_Switch := new String'(Long_Switch);
1355 Decompose_Switch (Long_Switch, P2, Last2);
1356 end if;
1358 if Switch /= "" and then Long_Switch /= "" then
1359 if (P1 = Parameter_None and then P2 /= P1)
1360 or else (P2 = Parameter_None and then P1 /= P2)
1361 or else (P1 = Parameter_Optional and then P2 /= P1)
1362 or else (P2 = Parameter_Optional and then P2 /= P1)
1363 then
1364 raise Invalid_Switch
1365 with "Inconsistent parameter types for "
1366 & Switch & " and " & Long_Switch;
1367 end if;
1368 end if;
1370 if Section /= "" then
1371 Def.Section := new String'(Section);
1372 end if;
1374 if Argument /= "ARG" then
1375 Def.Argument := new String'(Argument);
1376 end if;
1378 if Help /= "" then
1379 Def.Help := new String'(Help);
1380 end if;
1381 end Initialize_Switch_Def;
1383 -------------------
1384 -- Define_Switch --
1385 -------------------
1387 procedure Define_Switch
1388 (Config : in out Command_Line_Configuration;
1389 Switch : String := "";
1390 Long_Switch : String := "";
1391 Help : String := "";
1392 Section : String := "";
1393 Argument : String := "ARG")
1395 Def : Switch_Definition;
1396 begin
1397 if Switch /= "" or else Long_Switch /= "" then
1398 Initialize_Switch_Def
1399 (Def, Switch, Long_Switch, Help, Section, Argument);
1400 Add (Config, Def);
1401 end if;
1402 end Define_Switch;
1404 -------------------
1405 -- Define_Switch --
1406 -------------------
1408 procedure Define_Switch
1409 (Config : in out Command_Line_Configuration;
1410 Output : access Boolean;
1411 Switch : String := "";
1412 Long_Switch : String := "";
1413 Help : String := "";
1414 Section : String := "";
1415 Value : Boolean := True)
1417 Def : Switch_Definition (Switch_Boolean);
1418 begin
1419 if Switch /= "" or else Long_Switch /= "" then
1420 Initialize_Switch_Def (Def, Switch, Long_Switch, Help, Section);
1421 Def.Boolean_Output := Output.all'Unchecked_Access;
1422 Def.Boolean_Value := Value;
1423 Add (Config, Def);
1424 end if;
1425 end Define_Switch;
1427 -------------------
1428 -- Define_Switch --
1429 -------------------
1431 procedure Define_Switch
1432 (Config : in out Command_Line_Configuration;
1433 Output : access Integer;
1434 Switch : String := "";
1435 Long_Switch : String := "";
1436 Help : String := "";
1437 Section : String := "";
1438 Initial : Integer := 0;
1439 Default : Integer := 1;
1440 Argument : String := "ARG")
1442 Def : Switch_Definition (Switch_Integer);
1443 begin
1444 if Switch /= "" or else Long_Switch /= "" then
1445 Initialize_Switch_Def
1446 (Def, Switch, Long_Switch, Help, Section, Argument);
1447 Def.Integer_Output := Output.all'Unchecked_Access;
1448 Def.Integer_Default := Default;
1449 Def.Integer_Initial := Initial;
1450 Add (Config, Def);
1451 end if;
1452 end Define_Switch;
1454 -------------------
1455 -- Define_Switch --
1456 -------------------
1458 procedure Define_Switch
1459 (Config : in out Command_Line_Configuration;
1460 Output : access GNAT.Strings.String_Access;
1461 Switch : String := "";
1462 Long_Switch : String := "";
1463 Help : String := "";
1464 Section : String := "";
1465 Argument : String := "ARG")
1467 Def : Switch_Definition (Switch_String);
1468 begin
1469 if Switch /= "" or else Long_Switch /= "" then
1470 Initialize_Switch_Def
1471 (Def, Switch, Long_Switch, Help, Section, Argument);
1472 Def.String_Output := Output.all'Unchecked_Access;
1473 Add (Config, Def);
1474 end if;
1475 end Define_Switch;
1477 --------------------
1478 -- Define_Section --
1479 --------------------
1481 procedure Define_Section
1482 (Config : in out Command_Line_Configuration;
1483 Section : String)
1485 begin
1486 if Config = null then
1487 Config := new Command_Line_Configuration_Record;
1488 end if;
1490 Add (Config.Sections, new String'(Section));
1491 end Define_Section;
1493 --------------------
1494 -- Foreach_Switch --
1495 --------------------
1497 procedure Foreach_Switch
1498 (Config : Command_Line_Configuration;
1499 Section : String)
1501 begin
1502 if Config /= null and then Config.Switches /= null then
1503 for J in Config.Switches'Range loop
1504 if (Section = "" and then Config.Switches (J).Section = null)
1505 or else
1506 (Config.Switches (J).Section /= null
1507 and then Config.Switches (J).Section.all = Section)
1508 then
1509 exit when Config.Switches (J).Switch /= null
1510 and then not Callback (Config.Switches (J).Switch.all, J);
1512 exit when Config.Switches (J).Long_Switch /= null
1513 and then
1514 not Callback (Config.Switches (J).Long_Switch.all, J);
1515 end if;
1516 end loop;
1517 end if;
1518 end Foreach_Switch;
1520 ------------------
1521 -- Get_Switches --
1522 ------------------
1524 function Get_Switches
1525 (Config : Command_Line_Configuration;
1526 Switch_Char : Character := '-';
1527 Section : String := "") return String
1529 Ret : Ada.Strings.Unbounded.Unbounded_String;
1530 use Ada.Strings.Unbounded;
1532 function Add_Switch (S : String; Index : Integer) return Boolean;
1533 -- Add a switch to Ret
1535 ----------------
1536 -- Add_Switch --
1537 ----------------
1539 function Add_Switch (S : String; Index : Integer) return Boolean is
1540 pragma Unreferenced (Index);
1541 begin
1542 if S = "*" then
1543 Ret := "*" & Ret; -- Always first
1544 elsif S (S'First) = Switch_Char then
1545 Append (Ret, " " & S (S'First + 1 .. S'Last));
1546 else
1547 Append (Ret, " " & S);
1548 end if;
1550 return True;
1551 end Add_Switch;
1553 Tmp : Boolean;
1554 pragma Unreferenced (Tmp);
1556 procedure Foreach is new Foreach_Switch (Add_Switch);
1558 -- Start of processing for Get_Switches
1560 begin
1561 if Config = null then
1562 return "";
1563 end if;
1565 Foreach (Config, Section => Section);
1567 -- Add relevant aliases
1569 if Config.Aliases /= null then
1570 for A in Config.Aliases'Range loop
1571 if Config.Aliases (A).Section.all = Section then
1572 Tmp := Add_Switch (Config.Aliases (A).Alias.all, -1);
1573 end if;
1574 end loop;
1575 end if;
1577 return To_String (Ret);
1578 end Get_Switches;
1580 ------------------------
1581 -- Section_Delimiters --
1582 ------------------------
1584 function Section_Delimiters
1585 (Config : Command_Line_Configuration) return String
1587 use Ada.Strings.Unbounded;
1588 Result : Unbounded_String;
1590 begin
1591 if Config /= null and then Config.Sections /= null then
1592 for S in Config.Sections'Range loop
1593 Append (Result, " " & Config.Sections (S).all);
1594 end loop;
1595 end if;
1597 return To_String (Result);
1598 end Section_Delimiters;
1600 -----------------------
1601 -- Set_Configuration --
1602 -----------------------
1604 procedure Set_Configuration
1605 (Cmd : in out Command_Line;
1606 Config : Command_Line_Configuration)
1608 begin
1609 Cmd.Config := Config;
1610 end Set_Configuration;
1612 -----------------------
1613 -- Get_Configuration --
1614 -----------------------
1616 function Get_Configuration
1617 (Cmd : Command_Line) return Command_Line_Configuration
1619 begin
1620 return Cmd.Config;
1621 end Get_Configuration;
1623 ----------------------
1624 -- Set_Command_Line --
1625 ----------------------
1627 procedure Set_Command_Line
1628 (Cmd : in out Command_Line;
1629 Switches : String;
1630 Getopt_Description : String := "";
1631 Switch_Char : Character := '-')
1633 Tmp : Argument_List_Access;
1634 Parser : Opt_Parser;
1635 S : Character;
1636 Section : String_Access := null;
1638 function Real_Full_Switch
1639 (S : Character;
1640 Parser : Opt_Parser) return String;
1641 -- Ensure that the returned switch value contains the Switch_Char prefix
1642 -- if needed.
1644 ----------------------
1645 -- Real_Full_Switch --
1646 ----------------------
1648 function Real_Full_Switch
1649 (S : Character;
1650 Parser : Opt_Parser) return String
1652 begin
1653 if S = '*' then
1654 return Full_Switch (Parser);
1655 else
1656 return Switch_Char & Full_Switch (Parser);
1657 end if;
1658 end Real_Full_Switch;
1660 -- Start of processing for Set_Command_Line
1662 begin
1663 Free (Cmd.Expanded);
1664 Free (Cmd.Params);
1666 if Switches /= "" then
1667 Tmp := Argument_String_To_List (Switches);
1668 Initialize_Option_Scan (Parser, Tmp, Switch_Char);
1670 loop
1671 begin
1672 if Cmd.Config /= null then
1674 -- Do not use Getopt_Description in this case. Otherwise,
1675 -- if we have defined a prefix -gnaty, and two switches
1676 -- -gnatya and -gnatyL!, we would have a different behavior
1677 -- depending on the order of switches:
1679 -- -gnatyL1a => -gnatyL with argument "1a"
1680 -- -gnatyaL1 => -gnatya and -gnatyL with argument "1"
1682 -- This is because the call to Getopt below knows nothing
1683 -- about prefixes, and in the first case finds a valid
1684 -- switch with arguments, so returns it without analyzing
1685 -- the argument. In the second case, the switch matches "*",
1686 -- and is then decomposed below.
1688 -- Note: When a Command_Line object is associated with a
1689 -- Command_Line_Config (which is mostly the case for tools
1690 -- that let users choose the command line before spawning
1691 -- other tools, for instance IDEs), the configuration of
1692 -- the switches must be taken from the Command_Line_Config.
1694 S := Getopt (Switches => "* " & Get_Switches (Cmd.Config),
1695 Concatenate => False,
1696 Parser => Parser);
1698 else
1699 S := Getopt (Switches => "* " & Getopt_Description,
1700 Concatenate => False,
1701 Parser => Parser);
1702 end if;
1704 exit when S = ASCII.NUL;
1706 declare
1707 Sw : constant String := Real_Full_Switch (S, Parser);
1708 Is_Section : Boolean := False;
1710 begin
1711 if Cmd.Config /= null
1712 and then Cmd.Config.Sections /= null
1713 then
1714 Section_Search :
1715 for S in Cmd.Config.Sections'Range loop
1716 if Sw = Cmd.Config.Sections (S).all then
1717 Section := Cmd.Config.Sections (S);
1718 Is_Section := True;
1720 exit Section_Search;
1721 end if;
1722 end loop Section_Search;
1723 end if;
1725 if not Is_Section then
1726 if Section = null then
1727 Add_Switch (Cmd, Sw, Parameter (Parser));
1728 else
1729 Add_Switch
1730 (Cmd, Sw, Parameter (Parser),
1731 Section => Section.all);
1732 end if;
1733 end if;
1734 end;
1736 exception
1737 when Invalid_Parameter =>
1739 -- Add it with no parameter, if that's the way the user
1740 -- wants it.
1742 -- Specify the separator in all cases, as the switch might
1743 -- need to be unaliased, and the alias might contain
1744 -- switches with parameters.
1746 if Section = null then
1747 Add_Switch
1748 (Cmd, Switch_Char & Full_Switch (Parser));
1749 else
1750 Add_Switch
1751 (Cmd, Switch_Char & Full_Switch (Parser),
1752 Section => Section.all);
1753 end if;
1754 end;
1755 end loop;
1757 Free (Parser);
1758 end if;
1759 end Set_Command_Line;
1761 ----------------
1762 -- Looking_At --
1763 ----------------
1765 function Looking_At
1766 (Type_Str : String;
1767 Index : Natural;
1768 Substring : String) return Boolean
1770 begin
1771 return Index + Substring'Length - 1 <= Type_Str'Last
1772 and then Type_Str (Index .. Index + Substring'Length - 1) = Substring;
1773 end Looking_At;
1775 ------------------------
1776 -- Can_Have_Parameter --
1777 ------------------------
1779 function Can_Have_Parameter (S : String) return Boolean is
1780 begin
1781 if S'Length <= 1 then
1782 return False;
1783 end if;
1785 case S (S'Last) is
1786 when '!' | ':' | '?' | '=' =>
1787 return True;
1788 when others =>
1789 return False;
1790 end case;
1791 end Can_Have_Parameter;
1793 -----------------------
1794 -- Require_Parameter --
1795 -----------------------
1797 function Require_Parameter (S : String) return Boolean is
1798 begin
1799 if S'Length <= 1 then
1800 return False;
1801 end if;
1803 case S (S'Last) is
1804 when '!' | ':' | '=' =>
1805 return True;
1806 when others =>
1807 return False;
1808 end case;
1809 end Require_Parameter;
1811 -------------------
1812 -- Actual_Switch --
1813 -------------------
1815 function Actual_Switch (S : String) return String is
1816 begin
1817 if S'Length <= 1 then
1818 return S;
1819 end if;
1821 case S (S'Last) is
1822 when '!' | ':' | '?' | '=' =>
1823 return S (S'First .. S'Last - 1);
1824 when others =>
1825 return S;
1826 end case;
1827 end Actual_Switch;
1829 ----------------------------
1830 -- For_Each_Simple_Switch --
1831 ----------------------------
1833 procedure For_Each_Simple_Switch
1834 (Config : Command_Line_Configuration;
1835 Section : String;
1836 Switch : String;
1837 Parameter : String := "";
1838 Unalias : Boolean := True)
1840 function Group_Analysis
1841 (Prefix : String;
1842 Group : String) return Boolean;
1843 -- Perform the analysis of a group of switches
1845 Found_In_Config : Boolean := False;
1846 function Is_In_Config
1847 (Config_Switch : String; Index : Integer) return Boolean;
1848 -- If Switch is the same as Config_Switch, run the callback and sets
1849 -- Found_In_Config to True.
1851 function Starts_With
1852 (Config_Switch : String; Index : Integer) return Boolean;
1853 -- if Switch starts with Config_Switch, sets Found_In_Config to True.
1854 -- The return value is for the Foreach_Switch iterator.
1856 --------------------
1857 -- Group_Analysis --
1858 --------------------
1860 function Group_Analysis
1861 (Prefix : String;
1862 Group : String) return Boolean
1864 Idx : Natural;
1865 Found : Boolean;
1867 function Analyze_Simple_Switch
1868 (Switch : String; Index : Integer) return Boolean;
1869 -- "Switches" is one of the switch definitions passed to the
1870 -- configuration, not one of the switches found on the command line.
1872 ---------------------------
1873 -- Analyze_Simple_Switch --
1874 ---------------------------
1876 function Analyze_Simple_Switch
1877 (Switch : String; Index : Integer) return Boolean
1879 pragma Unreferenced (Index);
1881 Full : constant String := Prefix & Group (Idx .. Group'Last);
1883 Sw : constant String := Actual_Switch (Switch);
1884 -- Switches definition minus argument definition
1886 Last : Natural;
1887 Param : Natural;
1889 begin
1890 -- Verify that sw starts with Prefix
1892 if Looking_At (Sw, Sw'First, Prefix)
1894 -- Verify that the group starts with sw
1896 and then Looking_At (Full, Full'First, Sw)
1897 then
1898 Last := Idx + Sw'Length - Prefix'Length - 1;
1899 Param := Last + 1;
1901 if Can_Have_Parameter (Switch) then
1903 -- Include potential parameter to the recursive call. Only
1904 -- numbers are allowed.
1906 while Last < Group'Last
1907 and then Group (Last + 1) in '0' .. '9'
1908 loop
1909 Last := Last + 1;
1910 end loop;
1911 end if;
1913 if not Require_Parameter (Switch) or else Last >= Param then
1914 if Idx = Group'First
1915 and then Last = Group'Last
1916 and then Last < Param
1917 then
1918 -- The group only concerns a single switch. Do not
1919 -- perform recursive call.
1921 -- Note that we still perform a recursive call if
1922 -- a parameter is detected in the switch, as this
1923 -- is a way to correctly identify such a parameter
1924 -- in aliases.
1926 return False;
1927 end if;
1929 Found := True;
1931 -- Recursive call, using the detected parameter if any
1933 if Last >= Param then
1934 For_Each_Simple_Switch
1935 (Config,
1936 Section,
1937 Prefix & Group (Idx .. Param - 1),
1938 Group (Param .. Last));
1940 else
1941 For_Each_Simple_Switch
1942 (Config, Section, Prefix & Group (Idx .. Last), "");
1943 end if;
1945 Idx := Last + 1;
1946 return False;
1947 end if;
1948 end if;
1950 return True;
1951 end Analyze_Simple_Switch;
1953 procedure Foreach is new Foreach_Switch (Analyze_Simple_Switch);
1955 -- Start of processing for Group_Analysis
1957 begin
1958 Idx := Group'First;
1959 while Idx <= Group'Last loop
1960 Found := False;
1961 Foreach (Config, Section);
1963 if not Found then
1964 For_Each_Simple_Switch
1965 (Config, Section, Prefix & Group (Idx), "");
1966 Idx := Idx + 1;
1967 end if;
1968 end loop;
1970 return True;
1971 end Group_Analysis;
1973 ------------------
1974 -- Is_In_Config --
1975 ------------------
1977 function Is_In_Config
1978 (Config_Switch : String; Index : Integer) return Boolean
1980 Last : Natural;
1981 P : Switch_Parameter_Type;
1983 begin
1984 Decompose_Switch (Config_Switch, P, Last);
1986 if Config_Switch (Config_Switch'First .. Last) = Switch then
1987 case P is
1988 when Parameter_None =>
1989 if Parameter = "" then
1990 Callback (Switch, "", "", Index => Index);
1991 Found_In_Config := True;
1992 return False;
1993 end if;
1995 when Parameter_With_Optional_Space =>
1996 Callback (Switch, " ", Parameter, Index => Index);
1997 Found_In_Config := True;
1998 return False;
2000 when Parameter_With_Space_Or_Equal =>
2001 Callback (Switch, "=", Parameter, Index => Index);
2002 Found_In_Config := True;
2003 return False;
2005 when Parameter_No_Space
2006 | Parameter_Optional
2008 Callback (Switch, "", Parameter, Index);
2009 Found_In_Config := True;
2010 return False;
2011 end case;
2012 end if;
2014 return True;
2015 end Is_In_Config;
2017 -----------------
2018 -- Starts_With --
2019 -----------------
2021 function Starts_With
2022 (Config_Switch : String; Index : Integer) return Boolean
2024 Last : Natural;
2025 Param : Natural;
2026 P : Switch_Parameter_Type;
2028 begin
2029 -- This function is called when we believe the parameter was
2030 -- specified as part of the switch, instead of separately. Thus we
2031 -- look in the config to find all possible switches.
2033 Decompose_Switch (Config_Switch, P, Last);
2035 if Looking_At
2036 (Switch, Switch'First,
2037 Config_Switch (Config_Switch'First .. Last))
2038 then
2039 -- Set first char of Param, and last char of Switch
2041 Param := Switch'First + Last;
2042 Last := Switch'First + Last - Config_Switch'First;
2044 case P is
2046 -- None is already handled in Is_In_Config
2048 when Parameter_None =>
2049 null;
2051 when Parameter_With_Space_Or_Equal =>
2052 if Param <= Switch'Last
2053 and then
2054 (Switch (Param) = ' ' or else Switch (Param) = '=')
2055 then
2056 Callback (Switch (Switch'First .. Last),
2057 "=", Switch (Param + 1 .. Switch'Last), Index);
2058 Found_In_Config := True;
2059 return False;
2060 end if;
2062 when Parameter_With_Optional_Space =>
2063 if Param <= Switch'Last and then Switch (Param) = ' ' then
2064 Param := Param + 1;
2065 end if;
2067 Callback (Switch (Switch'First .. Last),
2068 " ", Switch (Param .. Switch'Last), Index);
2069 Found_In_Config := True;
2070 return False;
2072 when Parameter_No_Space
2073 | Parameter_Optional
2075 Callback (Switch (Switch'First .. Last),
2076 "", Switch (Param .. Switch'Last), Index);
2077 Found_In_Config := True;
2078 return False;
2079 end case;
2080 end if;
2081 return True;
2082 end Starts_With;
2084 procedure Foreach_In_Config is new Foreach_Switch (Is_In_Config);
2085 procedure Foreach_Starts_With is new Foreach_Switch (Starts_With);
2087 -- Start of processing for For_Each_Simple_Switch
2089 begin
2090 -- First determine if the switch corresponds to one belonging to the
2091 -- configuration. If so, run callback and exit.
2093 -- ??? Is this necessary. On simple tests, we seem to have the same
2094 -- results with or without this call.
2096 Foreach_In_Config (Config, Section);
2098 if Found_In_Config then
2099 return;
2100 end if;
2102 -- If adding a switch that can in fact be expanded through aliases,
2103 -- add separately each of its expansions.
2105 -- This takes care of expansions like "-T" -> "-gnatwrs", where the
2106 -- alias and its expansion do not have the same prefix. Given the order
2107 -- in which we do things here, the expansion of the alias will itself
2108 -- be checked for a common prefix and split into simple switches.
2110 if Unalias
2111 and then Config /= null
2112 and then Config.Aliases /= null
2113 then
2114 for A in Config.Aliases'Range loop
2115 if Config.Aliases (A).Section.all = Section
2116 and then Config.Aliases (A).Alias.all = Switch
2117 and then Parameter = ""
2118 then
2119 For_Each_Simple_Switch
2120 (Config, Section, Config.Aliases (A).Expansion.all, "");
2121 return;
2122 end if;
2123 end loop;
2124 end if;
2126 -- If adding a switch grouping several switches, add each of the simple
2127 -- switches instead.
2129 if Config /= null and then Config.Prefixes /= null then
2130 for P in Config.Prefixes'Range loop
2131 if Switch'Length > Config.Prefixes (P)'Length + 1
2132 and then
2133 Looking_At (Switch, Switch'First, Config.Prefixes (P).all)
2134 then
2135 -- Alias expansion will be done recursively
2137 if Config.Switches = null then
2138 for S in Switch'First + Config.Prefixes (P)'Length
2139 .. Switch'Last
2140 loop
2141 For_Each_Simple_Switch
2142 (Config, Section,
2143 Config.Prefixes (P).all & Switch (S), "");
2144 end loop;
2146 return;
2148 elsif Group_Analysis
2149 (Config.Prefixes (P).all,
2150 Switch
2151 (Switch'First + Config.Prefixes (P)'Length .. Switch'Last))
2152 then
2153 -- Recursive calls already done on each switch of the group:
2154 -- Return without executing Callback.
2156 return;
2157 end if;
2158 end if;
2159 end loop;
2160 end if;
2162 -- Test if added switch is a known switch with parameter attached
2163 -- instead of being specified separately
2165 if Parameter = ""
2166 and then Config /= null
2167 and then Config.Switches /= null
2168 then
2169 Found_In_Config := False;
2170 Foreach_Starts_With (Config, Section);
2172 if Found_In_Config then
2173 return;
2174 end if;
2175 end if;
2177 -- The switch is invalid in the config, but we still want to report it.
2178 -- The config could, for instance, include "*" to specify it accepts
2179 -- all switches.
2181 Callback (Switch, " ", Parameter, Index => -1);
2182 end For_Each_Simple_Switch;
2184 ----------------
2185 -- Add_Switch --
2186 ----------------
2188 procedure Add_Switch
2189 (Cmd : in out Command_Line;
2190 Switch : String;
2191 Parameter : String := "";
2192 Separator : Character := ASCII.NUL;
2193 Section : String := "";
2194 Add_Before : Boolean := False)
2196 Success : Boolean;
2197 pragma Unreferenced (Success);
2198 begin
2199 Add_Switch (Cmd, Switch, Parameter, Separator,
2200 Section, Add_Before, Success);
2201 end Add_Switch;
2203 ----------------
2204 -- Add_Switch --
2205 ----------------
2207 procedure Add_Switch
2208 (Cmd : in out Command_Line;
2209 Switch : String;
2210 Parameter : String := "";
2211 Separator : Character := ASCII.NUL;
2212 Section : String := "";
2213 Add_Before : Boolean := False;
2214 Success : out Boolean)
2216 procedure Add_Simple_Switch
2217 (Simple : String;
2218 Sepa : String;
2219 Param : String;
2220 Index : Integer);
2221 -- Add a new switch that has had all its aliases expanded, and switches
2222 -- ungrouped. We know there are no more aliases in Switches.
2224 -----------------------
2225 -- Add_Simple_Switch --
2226 -----------------------
2228 procedure Add_Simple_Switch
2229 (Simple : String;
2230 Sepa : String;
2231 Param : String;
2232 Index : Integer)
2234 Sep : Character;
2236 begin
2237 if Index = -1
2238 and then Cmd.Config /= null
2239 and then not Cmd.Config.Star_Switch
2240 then
2241 raise Invalid_Switch
2242 with "Invalid switch " & Simple;
2243 end if;
2245 if Separator /= ASCII.NUL then
2246 Sep := Separator;
2248 elsif Sepa = "" then
2249 Sep := ASCII.NUL;
2250 else
2251 Sep := Sepa (Sepa'First);
2252 end if;
2254 if Cmd.Expanded = null then
2255 Cmd.Expanded := new Argument_List'(1 .. 1 => new String'(Simple));
2257 if Param /= "" then
2258 Cmd.Params :=
2259 new Argument_List'(1 .. 1 => new String'(Sep & Param));
2260 else
2261 Cmd.Params := new Argument_List'(1 .. 1 => null);
2262 end if;
2264 if Section = "" then
2265 Cmd.Sections := new Argument_List'(1 .. 1 => null);
2266 else
2267 Cmd.Sections :=
2268 new Argument_List'(1 .. 1 => new String'(Section));
2269 end if;
2271 else
2272 -- Do we already have this switch?
2274 for C in Cmd.Expanded'Range loop
2275 if Cmd.Expanded (C).all = Simple
2276 and then
2277 ((Cmd.Params (C) = null and then Param = "")
2278 or else
2279 (Cmd.Params (C) /= null
2280 and then Cmd.Params (C).all = Sep & Param))
2281 and then
2282 ((Cmd.Sections (C) = null and then Section = "")
2283 or else
2284 (Cmd.Sections (C) /= null
2285 and then Cmd.Sections (C).all = Section))
2286 then
2287 return;
2288 end if;
2289 end loop;
2291 -- Inserting at least one switch
2293 Success := True;
2294 Add (Cmd.Expanded, new String'(Simple), Add_Before);
2296 if Param /= "" then
2298 (Cmd.Params,
2299 new String'(Sep & Param),
2300 Add_Before);
2301 else
2303 (Cmd.Params,
2304 null,
2305 Add_Before);
2306 end if;
2308 if Section = "" then
2310 (Cmd.Sections,
2311 null,
2312 Add_Before);
2313 else
2315 (Cmd.Sections,
2316 new String'(Section),
2317 Add_Before);
2318 end if;
2319 end if;
2320 end Add_Simple_Switch;
2322 procedure Add_Simple_Switches is
2323 new For_Each_Simple_Switch (Add_Simple_Switch);
2325 -- Local Variables
2327 Section_Valid : Boolean := False;
2329 -- Start of processing for Add_Switch
2331 begin
2332 if Section /= "" and then Cmd.Config /= null then
2333 for S in Cmd.Config.Sections'Range loop
2334 if Section = Cmd.Config.Sections (S).all then
2335 Section_Valid := True;
2336 exit;
2337 end if;
2338 end loop;
2340 if not Section_Valid then
2341 raise Invalid_Section;
2342 end if;
2343 end if;
2345 Success := False;
2346 Add_Simple_Switches (Cmd.Config, Section, Switch, Parameter);
2347 Free (Cmd.Coalesce);
2348 end Add_Switch;
2350 ------------
2351 -- Remove --
2352 ------------
2354 procedure Remove (Line : in out Argument_List_Access; Index : Integer) is
2355 Tmp : Argument_List_Access := Line;
2357 begin
2358 Line := new Argument_List (Tmp'First .. Tmp'Last - 1);
2360 if Index /= Tmp'First then
2361 Line (Tmp'First .. Index - 1) := Tmp (Tmp'First .. Index - 1);
2362 end if;
2364 Free (Tmp (Index));
2366 if Index /= Tmp'Last then
2367 Line (Index .. Tmp'Last - 1) := Tmp (Index + 1 .. Tmp'Last);
2368 end if;
2370 Unchecked_Free (Tmp);
2371 end Remove;
2373 ---------
2374 -- Add --
2375 ---------
2377 procedure Add
2378 (Line : in out Argument_List_Access;
2379 Str : String_Access;
2380 Before : Boolean := False)
2382 Tmp : Argument_List_Access := Line;
2384 begin
2385 if Tmp /= null then
2386 Line := new Argument_List (Tmp'First .. Tmp'Last + 1);
2388 if Before then
2389 Line (Tmp'First) := Str;
2390 Line (Tmp'First + 1 .. Tmp'Last + 1) := Tmp.all;
2391 else
2392 Line (Tmp'Range) := Tmp.all;
2393 Line (Tmp'Last + 1) := Str;
2394 end if;
2396 Unchecked_Free (Tmp);
2398 else
2399 Line := new Argument_List'(1 .. 1 => Str);
2400 end if;
2401 end Add;
2403 -------------------
2404 -- Remove_Switch --
2405 -------------------
2407 procedure Remove_Switch
2408 (Cmd : in out Command_Line;
2409 Switch : String;
2410 Remove_All : Boolean := False;
2411 Has_Parameter : Boolean := False;
2412 Section : String := "")
2414 Success : Boolean;
2415 pragma Unreferenced (Success);
2416 begin
2417 Remove_Switch (Cmd, Switch, Remove_All, Has_Parameter, Section, Success);
2418 end Remove_Switch;
2420 -------------------
2421 -- Remove_Switch --
2422 -------------------
2424 procedure Remove_Switch
2425 (Cmd : in out Command_Line;
2426 Switch : String;
2427 Remove_All : Boolean := False;
2428 Has_Parameter : Boolean := False;
2429 Section : String := "";
2430 Success : out Boolean)
2432 procedure Remove_Simple_Switch
2433 (Simple, Separator, Param : String; Index : Integer);
2434 -- Removes a simple switch, with no aliasing or grouping
2436 --------------------------
2437 -- Remove_Simple_Switch --
2438 --------------------------
2440 procedure Remove_Simple_Switch
2441 (Simple, Separator, Param : String; Index : Integer)
2443 C : Integer;
2444 pragma Unreferenced (Param, Separator, Index);
2446 begin
2447 if Cmd.Expanded /= null then
2448 C := Cmd.Expanded'First;
2449 while C <= Cmd.Expanded'Last loop
2450 if Cmd.Expanded (C).all = Simple
2451 and then
2452 (Remove_All
2453 or else (Cmd.Sections (C) = null
2454 and then Section = "")
2455 or else (Cmd.Sections (C) /= null
2456 and then Section = Cmd.Sections (C).all))
2457 and then (not Has_Parameter or else Cmd.Params (C) /= null)
2458 then
2459 Remove (Cmd.Expanded, C);
2460 Remove (Cmd.Params, C);
2461 Remove (Cmd.Sections, C);
2462 Success := True;
2464 if not Remove_All then
2465 return;
2466 end if;
2468 else
2469 C := C + 1;
2470 end if;
2471 end loop;
2472 end if;
2473 end Remove_Simple_Switch;
2475 procedure Remove_Simple_Switches is
2476 new For_Each_Simple_Switch (Remove_Simple_Switch);
2478 -- Start of processing for Remove_Switch
2480 begin
2481 Success := False;
2482 Remove_Simple_Switches
2483 (Cmd.Config, Section, Switch, "", Unalias => not Has_Parameter);
2484 Free (Cmd.Coalesce);
2485 end Remove_Switch;
2487 -------------------
2488 -- Remove_Switch --
2489 -------------------
2491 procedure Remove_Switch
2492 (Cmd : in out Command_Line;
2493 Switch : String;
2494 Parameter : String;
2495 Section : String := "")
2497 procedure Remove_Simple_Switch
2498 (Simple, Separator, Param : String; Index : Integer);
2499 -- Removes a simple switch, with no aliasing or grouping
2501 --------------------------
2502 -- Remove_Simple_Switch --
2503 --------------------------
2505 procedure Remove_Simple_Switch
2506 (Simple, Separator, Param : String; Index : Integer)
2508 pragma Unreferenced (Separator, Index);
2509 C : Integer;
2511 begin
2512 if Cmd.Expanded /= null then
2513 C := Cmd.Expanded'First;
2514 while C <= Cmd.Expanded'Last loop
2515 if Cmd.Expanded (C).all = Simple
2516 and then
2517 ((Cmd.Sections (C) = null
2518 and then Section = "")
2519 or else
2520 (Cmd.Sections (C) /= null
2521 and then Section = Cmd.Sections (C).all))
2522 and then
2523 ((Cmd.Params (C) = null and then Param = "")
2524 or else
2525 (Cmd.Params (C) /= null
2527 -- Ignore the separator stored in Parameter
2529 and then
2530 Cmd.Params (C) (Cmd.Params (C)'First + 1
2531 .. Cmd.Params (C)'Last) = Param))
2532 then
2533 Remove (Cmd.Expanded, C);
2534 Remove (Cmd.Params, C);
2535 Remove (Cmd.Sections, C);
2537 -- The switch is necessarily unique by construction of
2538 -- Add_Switch.
2540 return;
2542 else
2543 C := C + 1;
2544 end if;
2545 end loop;
2546 end if;
2547 end Remove_Simple_Switch;
2549 procedure Remove_Simple_Switches is
2550 new For_Each_Simple_Switch (Remove_Simple_Switch);
2552 -- Start of processing for Remove_Switch
2554 begin
2555 Remove_Simple_Switches (Cmd.Config, Section, Switch, Parameter);
2556 Free (Cmd.Coalesce);
2557 end Remove_Switch;
2559 --------------------
2560 -- Group_Switches --
2561 --------------------
2563 procedure Group_Switches
2564 (Cmd : Command_Line;
2565 Result : Argument_List_Access;
2566 Sections : Argument_List_Access;
2567 Params : Argument_List_Access)
2569 function Compatible_Parameter (Param : String_Access) return Boolean;
2570 -- True when the parameter can be part of a group
2572 --------------------------
2573 -- Compatible_Parameter --
2574 --------------------------
2576 function Compatible_Parameter (Param : String_Access) return Boolean is
2577 begin
2578 -- No parameter OK
2580 if Param = null then
2581 return True;
2583 -- We need parameters without separators
2585 elsif Param (Param'First) /= ASCII.NUL then
2586 return False;
2588 -- Parameters must be all digits
2590 else
2591 for J in Param'First + 1 .. Param'Last loop
2592 if Param (J) not in '0' .. '9' then
2593 return False;
2594 end if;
2595 end loop;
2597 return True;
2598 end if;
2599 end Compatible_Parameter;
2601 -- Local declarations
2603 Group : Ada.Strings.Unbounded.Unbounded_String;
2604 First : Natural;
2605 use type Ada.Strings.Unbounded.Unbounded_String;
2607 -- Start of processing for Group_Switches
2609 begin
2610 if Cmd.Config = null or else Cmd.Config.Prefixes = null then
2611 return;
2612 end if;
2614 for P in Cmd.Config.Prefixes'Range loop
2615 Group := Ada.Strings.Unbounded.Null_Unbounded_String;
2616 First := 0;
2618 for C in Result'Range loop
2619 if Result (C) /= null
2620 and then Compatible_Parameter (Params (C))
2621 and then Looking_At
2622 (Result (C).all,
2623 Result (C)'First,
2624 Cmd.Config.Prefixes (P).all)
2625 then
2626 -- If we are still in the same section, group the switches
2628 if First = 0
2629 or else
2630 (Sections (C) = null
2631 and then Sections (First) = null)
2632 or else
2633 (Sections (C) /= null
2634 and then Sections (First) /= null
2635 and then Sections (C).all = Sections (First).all)
2636 then
2637 Group :=
2638 Group &
2639 Result (C)
2640 (Result (C)'First + Cmd.Config.Prefixes (P)'Length ..
2641 Result (C)'Last);
2643 if Params (C) /= null then
2644 Group :=
2645 Group &
2646 Params (C) (Params (C)'First + 1 .. Params (C)'Last);
2647 Free (Params (C));
2648 end if;
2650 if First = 0 then
2651 First := C;
2652 end if;
2654 Free (Result (C));
2656 -- We changed section: we put the grouped switches to the first
2657 -- place, on continue with the new section.
2659 else
2660 Result (First) :=
2661 new String'
2662 (Cmd.Config.Prefixes (P).all &
2663 Ada.Strings.Unbounded.To_String (Group));
2664 Group :=
2665 Ada.Strings.Unbounded.To_Unbounded_String
2666 (Result (C)
2667 (Result (C)'First + Cmd.Config.Prefixes (P)'Length ..
2668 Result (C)'Last));
2669 First := C;
2670 end if;
2671 end if;
2672 end loop;
2674 if First > 0 then
2675 Result (First) :=
2676 new String'
2677 (Cmd.Config.Prefixes (P).all &
2678 Ada.Strings.Unbounded.To_String (Group));
2679 end if;
2680 end loop;
2681 end Group_Switches;
2683 --------------------
2684 -- Alias_Switches --
2685 --------------------
2687 procedure Alias_Switches
2688 (Cmd : Command_Line;
2689 Result : Argument_List_Access;
2690 Params : Argument_List_Access)
2692 Found : Boolean;
2693 First : Natural;
2695 procedure Check_Cb (Switch, Separator, Param : String; Index : Integer);
2696 -- Checks whether the command line contains [Switch]. Sets the global
2697 -- variable [Found] appropriately. This is called for each simple switch
2698 -- that make up an alias, to know whether the alias should be applied.
2700 procedure Remove_Cb (Switch, Separator, Param : String; Index : Integer);
2701 -- Remove the simple switch [Switch] from the command line, since it is
2702 -- part of a simpler alias
2704 --------------
2705 -- Check_Cb --
2706 --------------
2708 procedure Check_Cb
2709 (Switch, Separator, Param : String; Index : Integer)
2711 pragma Unreferenced (Separator, Index);
2713 begin
2714 if Found then
2715 for E in Result'Range loop
2716 if Result (E) /= null
2717 and then
2718 (Params (E) = null
2719 or else Params (E) (Params (E)'First + 1 ..
2720 Params (E)'Last) = Param)
2721 and then Result (E).all = Switch
2722 then
2723 return;
2724 end if;
2725 end loop;
2727 Found := False;
2728 end if;
2729 end Check_Cb;
2731 ---------------
2732 -- Remove_Cb --
2733 ---------------
2735 procedure Remove_Cb (Switch, Separator, Param : String; Index : Integer)
2737 pragma Unreferenced (Separator, Index);
2739 begin
2740 for E in Result'Range loop
2741 if Result (E) /= null
2742 and then
2743 (Params (E) = null
2744 or else Params (E) (Params (E)'First + 1
2745 .. Params (E)'Last) = Param)
2746 and then Result (E).all = Switch
2747 then
2748 if First > E then
2749 First := E;
2750 end if;
2752 Free (Result (E));
2753 Free (Params (E));
2754 return;
2755 end if;
2756 end loop;
2757 end Remove_Cb;
2759 procedure Check_All is new For_Each_Simple_Switch (Check_Cb);
2760 procedure Remove_All is new For_Each_Simple_Switch (Remove_Cb);
2762 -- Start of processing for Alias_Switches
2764 begin
2765 if Cmd.Config = null or else Cmd.Config.Aliases = null then
2766 return;
2767 end if;
2769 for A in Cmd.Config.Aliases'Range loop
2771 -- Compute the various simple switches that make up the alias. We
2772 -- split the expansion into as many simple switches as possible, and
2773 -- then check whether the expanded command line has all of them.
2775 Found := True;
2776 Check_All (Cmd.Config,
2777 Switch => Cmd.Config.Aliases (A).Expansion.all,
2778 Section => Cmd.Config.Aliases (A).Section.all);
2780 if Found then
2781 First := Integer'Last;
2782 Remove_All (Cmd.Config,
2783 Switch => Cmd.Config.Aliases (A).Expansion.all,
2784 Section => Cmd.Config.Aliases (A).Section.all);
2785 Result (First) := new String'(Cmd.Config.Aliases (A).Alias.all);
2786 end if;
2787 end loop;
2788 end Alias_Switches;
2790 -------------------
2791 -- Sort_Sections --
2792 -------------------
2794 procedure Sort_Sections
2795 (Line : not null GNAT.OS_Lib.Argument_List_Access;
2796 Sections : GNAT.OS_Lib.Argument_List_Access;
2797 Params : GNAT.OS_Lib.Argument_List_Access)
2799 Sections_List : Argument_List_Access :=
2800 new Argument_List'(1 .. 1 => null);
2801 Found : Boolean;
2802 Old_Line : constant Argument_List := Line.all;
2803 Old_Sections : constant Argument_List := Sections.all;
2804 Old_Params : constant Argument_List := Params.all;
2805 Index : Natural;
2807 begin
2808 -- First construct a list of all sections
2810 for E in Line'Range loop
2811 if Sections (E) /= null then
2812 Found := False;
2813 for S in Sections_List'Range loop
2814 if (Sections_List (S) = null and then Sections (E) = null)
2815 or else
2816 (Sections_List (S) /= null
2817 and then Sections (E) /= null
2818 and then Sections_List (S).all = Sections (E).all)
2819 then
2820 Found := True;
2821 exit;
2822 end if;
2823 end loop;
2825 if not Found then
2826 Add (Sections_List, Sections (E));
2827 end if;
2828 end if;
2829 end loop;
2831 Index := Line'First;
2833 for S in Sections_List'Range loop
2834 for E in Old_Line'Range loop
2835 if (Sections_List (S) = null and then Old_Sections (E) = null)
2836 or else
2837 (Sections_List (S) /= null
2838 and then Old_Sections (E) /= null
2839 and then Sections_List (S).all = Old_Sections (E).all)
2840 then
2841 Line (Index) := Old_Line (E);
2842 Sections (Index) := Old_Sections (E);
2843 Params (Index) := Old_Params (E);
2844 Index := Index + 1;
2845 end if;
2846 end loop;
2847 end loop;
2849 Unchecked_Free (Sections_List);
2850 end Sort_Sections;
2852 -----------
2853 -- Start --
2854 -----------
2856 procedure Start
2857 (Cmd : in out Command_Line;
2858 Iter : in out Command_Line_Iterator;
2859 Expanded : Boolean := False)
2861 begin
2862 if Cmd.Expanded = null then
2863 Iter.List := null;
2864 return;
2865 end if;
2867 -- Reorder the expanded line so that sections are grouped
2869 Sort_Sections (Cmd.Expanded, Cmd.Sections, Cmd.Params);
2871 -- Coalesce the switches as much as possible
2873 if not Expanded
2874 and then Cmd.Coalesce = null
2875 then
2876 Cmd.Coalesce := new Argument_List (Cmd.Expanded'Range);
2877 for E in Cmd.Expanded'Range loop
2878 Cmd.Coalesce (E) := new String'(Cmd.Expanded (E).all);
2879 end loop;
2881 Free (Cmd.Coalesce_Sections);
2882 Cmd.Coalesce_Sections := new Argument_List (Cmd.Sections'Range);
2883 for E in Cmd.Sections'Range loop
2884 Cmd.Coalesce_Sections (E) :=
2885 (if Cmd.Sections (E) = null then null
2886 else new String'(Cmd.Sections (E).all));
2887 end loop;
2889 Free (Cmd.Coalesce_Params);
2890 Cmd.Coalesce_Params := new Argument_List (Cmd.Params'Range);
2891 for E in Cmd.Params'Range loop
2892 Cmd.Coalesce_Params (E) :=
2893 (if Cmd.Params (E) = null then null
2894 else new String'(Cmd.Params (E).all));
2895 end loop;
2897 -- Not a clone, since we will not modify the parameters anyway
2899 Alias_Switches (Cmd, Cmd.Coalesce, Cmd.Coalesce_Params);
2900 Group_Switches
2901 (Cmd, Cmd.Coalesce, Cmd.Coalesce_Sections, Cmd.Coalesce_Params);
2902 end if;
2904 if Expanded then
2905 Iter.List := Cmd.Expanded;
2906 Iter.Params := Cmd.Params;
2907 Iter.Sections := Cmd.Sections;
2908 else
2909 Iter.List := Cmd.Coalesce;
2910 Iter.Params := Cmd.Coalesce_Params;
2911 Iter.Sections := Cmd.Coalesce_Sections;
2912 end if;
2914 if Iter.List = null then
2915 Iter.Current := Integer'Last;
2916 else
2917 Iter.Current := Iter.List'First - 1;
2918 Next (Iter);
2919 end if;
2920 end Start;
2922 --------------------
2923 -- Current_Switch --
2924 --------------------
2926 function Current_Switch (Iter : Command_Line_Iterator) return String is
2927 begin
2928 return Iter.List (Iter.Current).all;
2929 end Current_Switch;
2931 --------------------
2932 -- Is_New_Section --
2933 --------------------
2935 function Is_New_Section (Iter : Command_Line_Iterator) return Boolean is
2936 Section : constant String := Current_Section (Iter);
2938 begin
2939 if Iter.Sections = null then
2940 return False;
2942 elsif Iter.Current = Iter.Sections'First
2943 or else Iter.Sections (Iter.Current - 1) = null
2944 then
2945 return Section /= "";
2947 else
2948 return Section /= Iter.Sections (Iter.Current - 1).all;
2949 end if;
2950 end Is_New_Section;
2952 ---------------------
2953 -- Current_Section --
2954 ---------------------
2956 function Current_Section (Iter : Command_Line_Iterator) return String is
2957 begin
2958 if Iter.Sections = null
2959 or else Iter.Current > Iter.Sections'Last
2960 or else Iter.Sections (Iter.Current) = null
2961 then
2962 return "";
2963 end if;
2965 return Iter.Sections (Iter.Current).all;
2966 end Current_Section;
2968 -----------------------
2969 -- Current_Separator --
2970 -----------------------
2972 function Current_Separator (Iter : Command_Line_Iterator) return String is
2973 begin
2974 if Iter.Params = null
2975 or else Iter.Current > Iter.Params'Last
2976 or else Iter.Params (Iter.Current) = null
2977 then
2978 return "";
2980 else
2981 declare
2982 Sep : constant Character :=
2983 Iter.Params (Iter.Current) (Iter.Params (Iter.Current)'First);
2984 begin
2985 if Sep = ASCII.NUL then
2986 return "";
2987 else
2988 return "" & Sep;
2989 end if;
2990 end;
2991 end if;
2992 end Current_Separator;
2994 -----------------------
2995 -- Current_Parameter --
2996 -----------------------
2998 function Current_Parameter (Iter : Command_Line_Iterator) return String is
2999 begin
3000 if Iter.Params = null
3001 or else Iter.Current > Iter.Params'Last
3002 or else Iter.Params (Iter.Current) = null
3003 then
3004 return "";
3006 else
3007 -- Return result, skipping separator
3009 declare
3010 P : constant String := Iter.Params (Iter.Current).all;
3011 begin
3012 return P (P'First + 1 .. P'Last);
3013 end;
3014 end if;
3015 end Current_Parameter;
3017 --------------
3018 -- Has_More --
3019 --------------
3021 function Has_More (Iter : Command_Line_Iterator) return Boolean is
3022 begin
3023 return Iter.List /= null and then Iter.Current <= Iter.List'Last;
3024 end Has_More;
3026 ----------
3027 -- Next --
3028 ----------
3030 procedure Next (Iter : in out Command_Line_Iterator) is
3031 begin
3032 Iter.Current := Iter.Current + 1;
3033 while Iter.Current <= Iter.List'Last
3034 and then Iter.List (Iter.Current) = null
3035 loop
3036 Iter.Current := Iter.Current + 1;
3037 end loop;
3038 end Next;
3040 ----------
3041 -- Free --
3042 ----------
3044 procedure Free (Config : in out Command_Line_Configuration) is
3045 procedure Unchecked_Free is new
3046 Ada.Unchecked_Deallocation
3047 (Switch_Definitions, Switch_Definitions_List);
3049 procedure Unchecked_Free is new
3050 Ada.Unchecked_Deallocation
3051 (Alias_Definitions, Alias_Definitions_List);
3053 begin
3054 if Config /= null then
3055 Free (Config.Prefixes);
3056 Free (Config.Sections);
3057 Free (Config.Usage);
3058 Free (Config.Help);
3059 Free (Config.Help_Msg);
3061 if Config.Aliases /= null then
3062 for A in Config.Aliases'Range loop
3063 Free (Config.Aliases (A).Alias);
3064 Free (Config.Aliases (A).Expansion);
3065 Free (Config.Aliases (A).Section);
3066 end loop;
3068 Unchecked_Free (Config.Aliases);
3069 end if;
3071 if Config.Switches /= null then
3072 for S in Config.Switches'Range loop
3073 Free (Config.Switches (S).Switch);
3074 Free (Config.Switches (S).Long_Switch);
3075 Free (Config.Switches (S).Help);
3076 Free (Config.Switches (S).Section);
3077 Free (Config.Switches (S).Argument);
3078 end loop;
3080 Unchecked_Free (Config.Switches);
3081 end if;
3083 Unchecked_Free (Config);
3084 end if;
3085 end Free;
3087 ----------
3088 -- Free --
3089 ----------
3091 procedure Free (Cmd : in out Command_Line) is
3092 begin
3093 Free (Cmd.Expanded);
3094 Free (Cmd.Coalesce);
3095 Free (Cmd.Coalesce_Sections);
3096 Free (Cmd.Coalesce_Params);
3097 Free (Cmd.Params);
3098 Free (Cmd.Sections);
3099 end Free;
3101 ---------------
3102 -- Set_Usage --
3103 ---------------
3105 procedure Set_Usage
3106 (Config : in out Command_Line_Configuration;
3107 Usage : String := "[switches] [arguments]";
3108 Help : String := "";
3109 Help_Msg : String := "")
3111 begin
3112 if Config = null then
3113 Config := new Command_Line_Configuration_Record;
3114 end if;
3116 Free (Config.Usage);
3117 Free (Config.Help);
3118 Free (Config.Help_Msg);
3120 Config.Usage := new String'(Usage);
3121 Config.Help := new String'(Help);
3122 Config.Help_Msg := new String'(Help_Msg);
3123 end Set_Usage;
3125 ------------------
3126 -- Display_Help --
3127 ------------------
3129 procedure Display_Help (Config : Command_Line_Configuration) is
3130 function Switch_Name
3131 (Def : Switch_Definition;
3132 Section : String) return String;
3133 -- Return the "-short, --long=ARG" string for Def.
3134 -- Returns "" if the switch is not in the section.
3136 function Param_Name
3137 (P : Switch_Parameter_Type;
3138 Name : String := "ARG") return String;
3139 -- Return the display for a switch parameter
3141 procedure Display_Section_Help (Section : String);
3142 -- Display the help for a specific section ("" is the default section)
3144 --------------------------
3145 -- Display_Section_Help --
3146 --------------------------
3148 procedure Display_Section_Help (Section : String) is
3149 Max_Len : Natural := 0;
3151 begin
3152 -- ??? Special display for "*"
3154 New_Line;
3156 if Section /= "" and then Config.Switches /= null then
3157 Put_Line ("Switches after " & Section);
3158 end if;
3160 -- Compute size of the switches column
3162 if Config.Switches /= null then
3163 for S in Config.Switches'Range loop
3164 Max_Len := Natural'Max
3165 (Max_Len, Switch_Name (Config.Switches (S), Section)'Length);
3166 end loop;
3167 end if;
3169 if Config.Aliases /= null then
3170 for A in Config.Aliases'Range loop
3171 if Config.Aliases (A).Section.all = Section then
3172 Max_Len := Natural'Max
3173 (Max_Len, Config.Aliases (A).Alias'Length);
3174 end if;
3175 end loop;
3176 end if;
3178 -- Display the switches
3180 if Config.Switches /= null then
3181 for S in Config.Switches'Range loop
3182 declare
3183 N : constant String :=
3184 Switch_Name (Config.Switches (S), Section);
3186 begin
3187 if N /= "" then
3188 Put (" ");
3189 Put (N);
3190 Put ((1 .. Max_Len - N'Length + 1 => ' '));
3192 if Config.Switches (S).Help /= null then
3193 Put (Config.Switches (S).Help.all);
3194 end if;
3196 New_Line;
3197 end if;
3198 end;
3199 end loop;
3200 end if;
3202 -- Display the aliases
3204 if Config.Aliases /= null then
3205 for A in Config.Aliases'Range loop
3206 if Config.Aliases (A).Section.all = Section then
3207 Put (" ");
3208 Put (Config.Aliases (A).Alias.all);
3209 Put ((1 .. Max_Len - Config.Aliases (A).Alias'Length + 1
3210 => ' '));
3211 Put ("Equivalent to " & Config.Aliases (A).Expansion.all);
3212 New_Line;
3213 end if;
3214 end loop;
3215 end if;
3216 end Display_Section_Help;
3218 ----------------
3219 -- Param_Name --
3220 ----------------
3222 function Param_Name
3223 (P : Switch_Parameter_Type;
3224 Name : String := "ARG") return String
3226 begin
3227 case P is
3228 when Parameter_None =>
3229 return "";
3231 when Parameter_With_Optional_Space =>
3232 return " " & To_Upper (Name);
3234 when Parameter_With_Space_Or_Equal =>
3235 return "=" & To_Upper (Name);
3237 when Parameter_No_Space =>
3238 return To_Upper (Name);
3240 when Parameter_Optional =>
3241 return '[' & To_Upper (Name) & ']';
3242 end case;
3243 end Param_Name;
3245 -----------------
3246 -- Switch_Name --
3247 -----------------
3249 function Switch_Name
3250 (Def : Switch_Definition;
3251 Section : String) return String
3253 use Ada.Strings.Unbounded;
3254 Result : Unbounded_String;
3255 P1, P2 : Switch_Parameter_Type;
3256 Last1, Last2 : Integer := 0;
3258 begin
3259 if (Section = "" and then Def.Section = null)
3260 or else (Def.Section /= null and then Def.Section.all = Section)
3261 then
3262 if Def.Switch /= null and then Def.Switch.all = "*" then
3263 return "[any switch]";
3264 end if;
3266 if Def.Switch /= null then
3267 Decompose_Switch (Def.Switch.all, P1, Last1);
3268 Append (Result, Def.Switch (Def.Switch'First .. Last1));
3270 if Def.Long_Switch /= null then
3271 Decompose_Switch (Def.Long_Switch.all, P2, Last2);
3272 Append (Result, ", "
3273 & Def.Long_Switch (Def.Long_Switch'First .. Last2));
3275 if Def.Argument = null then
3276 Append (Result, Param_Name (P2, "ARG"));
3277 else
3278 Append (Result, Param_Name (P2, Def.Argument.all));
3279 end if;
3281 else
3282 if Def.Argument = null then
3283 Append (Result, Param_Name (P1, "ARG"));
3284 else
3285 Append (Result, Param_Name (P1, Def.Argument.all));
3286 end if;
3287 end if;
3289 -- Def.Switch is null (Long_Switch must be non-null)
3291 else
3292 Decompose_Switch (Def.Long_Switch.all, P2, Last2);
3293 Append (Result,
3294 Def.Long_Switch (Def.Long_Switch'First .. Last2));
3296 if Def.Argument = null then
3297 Append (Result, Param_Name (P2, "ARG"));
3298 else
3299 Append (Result, Param_Name (P2, Def.Argument.all));
3300 end if;
3301 end if;
3302 end if;
3304 return To_String (Result);
3305 end Switch_Name;
3307 -- Start of processing for Display_Help
3309 begin
3310 if Config = null then
3311 return;
3312 end if;
3314 if Config.Help /= null and then Config.Help.all /= "" then
3315 Put_Line (Config.Help.all);
3316 end if;
3318 if Config.Usage /= null then
3319 Put_Line ("Usage: "
3320 & Base_Name
3321 (Ada.Command_Line.Command_Name) & " " & Config.Usage.all);
3322 else
3323 Put_Line ("Usage: " & Base_Name (Ada.Command_Line.Command_Name)
3324 & " [switches] [arguments]");
3325 end if;
3327 if Config.Help_Msg /= null and then Config.Help_Msg.all /= "" then
3328 Put_Line (Config.Help_Msg.all);
3330 else
3331 Display_Section_Help ("");
3333 if Config.Sections /= null and then Config.Switches /= null then
3334 for S in Config.Sections'Range loop
3335 Display_Section_Help (Config.Sections (S).all);
3336 end loop;
3337 end if;
3338 end if;
3339 end Display_Help;
3341 ------------
3342 -- Getopt --
3343 ------------
3345 procedure Getopt
3346 (Config : Command_Line_Configuration;
3347 Callback : Switch_Handler := null;
3348 Parser : Opt_Parser := Command_Line_Parser;
3349 Concatenate : Boolean := True)
3351 Getopt_Switches : String_Access;
3352 C : Character := ASCII.NUL;
3354 Empty_Name : aliased constant String := "";
3355 Current_Section : Integer := -1;
3356 Section_Name : not null access constant String := Empty_Name'Access;
3358 procedure Simple_Callback
3359 (Simple_Switch : String;
3360 Separator : String;
3361 Parameter : String;
3362 Index : Integer);
3363 -- Needs comments ???
3365 procedure Do_Callback (Switch, Parameter : String; Index : Integer);
3367 -----------------
3368 -- Do_Callback --
3369 -----------------
3371 procedure Do_Callback (Switch, Parameter : String; Index : Integer) is
3372 begin
3373 -- Do automatic handling when possible
3375 if Index /= -1 then
3376 case Config.Switches (Index).Typ is
3377 when Switch_Untyped =>
3378 null; -- no automatic handling
3380 when Switch_Boolean =>
3381 Config.Switches (Index).Boolean_Output.all :=
3382 Config.Switches (Index).Boolean_Value;
3383 return;
3385 when Switch_Integer =>
3386 begin
3387 if Parameter = "" then
3388 Config.Switches (Index).Integer_Output.all :=
3389 Config.Switches (Index).Integer_Default;
3390 else
3391 Config.Switches (Index).Integer_Output.all :=
3392 Integer'Value (Parameter);
3393 end if;
3395 exception
3396 when Constraint_Error =>
3397 raise Invalid_Parameter
3398 with "Expected integer parameter for '"
3399 & Switch & "'";
3400 end;
3402 return;
3404 when Switch_String =>
3405 Free (Config.Switches (Index).String_Output.all);
3406 Config.Switches (Index).String_Output.all :=
3407 new String'(Parameter);
3408 return;
3409 end case;
3410 end if;
3412 -- Otherwise calls the user callback if one was defined
3414 if Callback /= null then
3415 Callback (Switch => Switch,
3416 Parameter => Parameter,
3417 Section => Section_Name.all);
3418 end if;
3419 end Do_Callback;
3421 procedure For_Each_Simple
3422 is new For_Each_Simple_Switch (Simple_Callback);
3424 ---------------------
3425 -- Simple_Callback --
3426 ---------------------
3428 procedure Simple_Callback
3429 (Simple_Switch : String;
3430 Separator : String;
3431 Parameter : String;
3432 Index : Integer)
3434 pragma Unreferenced (Separator);
3435 begin
3436 Do_Callback (Switch => Simple_Switch,
3437 Parameter => Parameter,
3438 Index => Index);
3439 end Simple_Callback;
3441 -- Start of processing for Getopt
3443 begin
3444 -- Initialize sections
3446 if Config.Sections = null then
3447 Config.Sections := new Argument_List'(1 .. 0 => null);
3448 end if;
3450 Internal_Initialize_Option_Scan
3451 (Parser => Parser,
3452 Switch_Char => Parser.Switch_Character,
3453 Stop_At_First_Non_Switch => Parser.Stop_At_First,
3454 Section_Delimiters => Section_Delimiters (Config));
3456 Getopt_Switches := new String'
3457 (Get_Switches (Config, Parser.Switch_Character, Section_Name.all)
3458 & " h -help");
3460 -- Initialize output values for automatically handled switches
3462 if Config.Switches /= null then
3463 for S in Config.Switches'Range loop
3464 case Config.Switches (S).Typ is
3465 when Switch_Untyped =>
3466 null; -- Nothing to do
3468 when Switch_Boolean =>
3469 Config.Switches (S).Boolean_Output.all :=
3470 not Config.Switches (S).Boolean_Value;
3472 when Switch_Integer =>
3473 Config.Switches (S).Integer_Output.all :=
3474 Config.Switches (S).Integer_Initial;
3476 when Switch_String =>
3477 if Config.Switches (S).String_Output.all = null then
3478 Config.Switches (S).String_Output.all := new String'("");
3479 end if;
3480 end case;
3481 end loop;
3482 end if;
3484 -- For all sections, and all switches within those sections
3486 loop
3487 C := Getopt (Switches => Getopt_Switches.all,
3488 Concatenate => Concatenate,
3489 Parser => Parser);
3491 if C = '*' then
3492 -- Full_Switch already includes the leading '-'
3494 Do_Callback (Switch => Full_Switch (Parser),
3495 Parameter => Parameter (Parser),
3496 Index => -1);
3498 elsif C /= ASCII.NUL then
3499 if Full_Switch (Parser) = "h"
3500 or else
3501 Full_Switch (Parser) = "-help"
3502 then
3503 Display_Help (Config);
3504 raise Exit_From_Command_Line;
3505 end if;
3507 -- Do switch expansion if needed
3509 For_Each_Simple
3510 (Config,
3511 Section => Section_Name.all,
3512 Switch => Parser.Switch_Character & Full_Switch (Parser),
3513 Parameter => Parameter (Parser));
3515 else
3516 if Current_Section = -1 then
3517 Current_Section := Config.Sections'First;
3518 else
3519 Current_Section := Current_Section + 1;
3520 end if;
3522 exit when Current_Section > Config.Sections'Last;
3524 Section_Name := Config.Sections (Current_Section);
3525 Goto_Section (Section_Name.all, Parser);
3527 Free (Getopt_Switches);
3528 Getopt_Switches := new String'
3529 (Get_Switches
3530 (Config, Parser.Switch_Character, Section_Name.all));
3531 end if;
3532 end loop;
3534 Free (Getopt_Switches);
3536 exception
3537 when Invalid_Switch =>
3538 Free (Getopt_Switches);
3540 -- Message inspired by "ls" on Unix
3542 Put_Line (Standard_Error,
3543 Base_Name (Ada.Command_Line.Command_Name)
3544 & ": unrecognized option '"
3545 & Full_Switch (Parser)
3546 & "'");
3547 Try_Help;
3549 raise;
3551 when others =>
3552 Free (Getopt_Switches);
3553 raise;
3554 end Getopt;
3556 -----------
3557 -- Build --
3558 -----------
3560 procedure Build
3561 (Line : in out Command_Line;
3562 Args : out GNAT.OS_Lib.Argument_List_Access;
3563 Expanded : Boolean := False;
3564 Switch_Char : Character := '-')
3566 Iter : Command_Line_Iterator;
3567 Count : Natural := 0;
3569 begin
3570 Start (Line, Iter, Expanded => Expanded);
3571 while Has_More (Iter) loop
3572 if Is_New_Section (Iter) then
3573 Count := Count + 1;
3574 end if;
3576 Count := Count + 1;
3577 Next (Iter);
3578 end loop;
3580 Args := new Argument_List (1 .. Count);
3581 Count := Args'First;
3583 Start (Line, Iter, Expanded => Expanded);
3584 while Has_More (Iter) loop
3585 if Is_New_Section (Iter) then
3586 Args (Count) := new String'(Switch_Char & Current_Section (Iter));
3587 Count := Count + 1;
3588 end if;
3590 Args (Count) := new String'(Current_Switch (Iter)
3591 & Current_Separator (Iter)
3592 & Current_Parameter (Iter));
3593 Count := Count + 1;
3594 Next (Iter);
3595 end loop;
3596 end Build;
3598 --------------
3599 -- Try_Help --
3600 --------------
3602 -- Note: Any change to the message displayed should also be done in
3603 -- gnatbind.adb that does not use this interface.
3605 procedure Try_Help is
3606 begin
3607 Put_Line
3608 (Standard_Error,
3609 "try """ & Base_Name (Ada.Command_Line.Command_Name)
3610 & " --help"" for more information.");
3611 end Try_Help;
3613 end GNAT.Command_Line;