PR target/35485
[official-gcc.git] / gcc / ada / g-comlin.adb
blob47f821d42713fb6752191cec046237594d872eeb
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-2008, 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 2, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, --
20 -- Boston, MA 02110-1301, USA. --
21 -- --
22 -- As a special exception, if other files instantiate generics from this --
23 -- unit, or you link this unit with other files to produce an executable, --
24 -- this unit does not by itself cause the resulting executable to be --
25 -- covered by the GNU General Public License. This exception does not --
26 -- however invalidate any other reasons why the executable file might be --
27 -- covered by the GNU Public License. --
28 -- --
29 -- GNAT was originally developed by the GNAT team at New York University. --
30 -- Extensive contributions were provided by Ada Core Technologies Inc. --
31 -- --
32 ------------------------------------------------------------------------------
34 with Ada.Unchecked_Deallocation;
35 with Ada.Strings.Unbounded;
37 with GNAT.OS_Lib; use GNAT.OS_Lib;
39 package body GNAT.Command_Line is
41 package CL renames Ada.Command_Line;
43 type Switch_Parameter_Type is
44 (Parameter_None,
45 Parameter_With_Optional_Space, -- ':' in getopt
46 Parameter_With_Space_Or_Equal, -- '=' in getopt
47 Parameter_No_Space, -- '!' in getopt
48 Parameter_Optional); -- '?' in getopt
50 procedure Set_Parameter
51 (Variable : out Parameter_Type;
52 Arg_Num : Positive;
53 First : Positive;
54 Last : Positive;
55 Extra : Character := ASCII.NUL);
56 pragma Inline (Set_Parameter);
57 -- Set the parameter that will be returned by Parameter below
58 -- Parameters need to be defined ???
60 function Goto_Next_Argument_In_Section (Parser : Opt_Parser) return Boolean;
61 -- Go to the next argument on the command line. If we are at the end of
62 -- the current section, we want to make sure there is no other identical
63 -- section on the command line (there might be multiple instances of
64 -- -largs). Returns True iff there is another argument.
66 function Get_File_Names_Case_Sensitive return Integer;
67 pragma Import (C, Get_File_Names_Case_Sensitive,
68 "__gnat_get_file_names_case_sensitive");
70 File_Names_Case_Sensitive : constant Boolean :=
71 Get_File_Names_Case_Sensitive /= 0;
73 procedure Canonical_Case_File_Name (S : in out String);
74 -- Given a file name, converts it to canonical case form. For systems where
75 -- file names are case sensitive, this procedure has no effect. If file
76 -- names are not case sensitive (i.e. for example if you have the file
77 -- "xyz.adb", you can refer to it as XYZ.adb or XyZ.AdB), then this call
78 -- converts the given string to canonical all lower case form, so that two
79 -- file names compare equal if they refer to the same file.
81 procedure Internal_Initialize_Option_Scan
82 (Parser : Opt_Parser;
83 Switch_Char : Character;
84 Stop_At_First_Non_Switch : Boolean;
85 Section_Delimiters : String);
86 -- Initialize Parser, which must have been allocated already
88 function Argument (Parser : Opt_Parser; Index : Integer) return String;
89 -- Return the index-th command line argument
91 procedure Find_Longest_Matching_Switch
92 (Switches : String;
93 Arg : String;
94 Index_In_Switches : out Integer;
95 Switch_Length : out Integer;
96 Param : out Switch_Parameter_Type);
97 -- return the Longest switch from Switches that matches at least
98 -- partially Arg. Index_In_Switches is set to 0 if none matches
100 procedure Unchecked_Free is new Ada.Unchecked_Deallocation
101 (Argument_List, Argument_List_Access);
103 procedure Unchecked_Free is new Ada.Unchecked_Deallocation
104 (Command_Line_Configuration_Record, Command_Line_Configuration);
106 procedure Remove (Line : in out Argument_List_Access; Index : Integer);
107 -- Remove a specific element from Line
109 procedure Add
110 (Line : in out Argument_List_Access;
111 Str : String_Access;
112 Before : Boolean := False);
113 -- Add a new element to Line. If Before is True, the item is inserted at
114 -- the beginning, else it is appended.
116 function Can_Have_Parameter (S : String) return Boolean;
117 -- True if S can have a parameter.
119 function Require_Parameter (S : String) return Boolean;
120 -- True if S requires a parameter.
122 function Actual_Switch (S : String) return String;
123 -- Remove any possible trailing '!', ':', '?' and '='
125 generic
126 with procedure Callback (Simple_Switch : String; Parameter : String);
127 procedure For_Each_Simple_Switch
128 (Cmd : Command_Line;
129 Switch : String;
130 Parameter : String := "";
131 Unalias : Boolean := True);
132 -- Breaks Switch into as simple switches as possible (expanding aliases and
133 -- ungrouping common prefixes when possible), and call Callback for each of
134 -- these.
136 procedure Sort_Sections
137 (Line : GNAT.OS_Lib.Argument_List_Access;
138 Sections : GNAT.OS_Lib.Argument_List_Access;
139 Params : GNAT.OS_Lib.Argument_List_Access);
140 -- Reorder the command line switches so that the switches belonging to a
141 -- section are grouped together.
143 procedure Group_Switches
144 (Cmd : Command_Line;
145 Result : Argument_List_Access;
146 Sections : Argument_List_Access;
147 Params : Argument_List_Access);
148 -- Group switches with common prefixes whenever possible. Once they have
149 -- been grouped, we also check items for possible aliasing.
151 procedure Alias_Switches
152 (Cmd : Command_Line;
153 Result : Argument_List_Access;
154 Params : Argument_List_Access);
155 -- When possible, replace one or more switches by an alias, i.e. a shorter
156 -- version.
158 function Looking_At
159 (Type_Str : String;
160 Index : Natural;
161 Substring : String) return Boolean;
162 -- Return True if the characters starting at Index in Type_Str are
163 -- equivalent to Substring.
165 --------------
166 -- Argument --
167 --------------
169 function Argument (Parser : Opt_Parser; Index : Integer) return String is
170 begin
171 if Parser.Arguments /= null then
172 return Parser.Arguments (Index + Parser.Arguments'First - 1).all;
173 else
174 return CL.Argument (Index);
175 end if;
176 end Argument;
178 ------------------------------
179 -- Canonical_Case_File_Name --
180 ------------------------------
182 procedure Canonical_Case_File_Name (S : in out String) is
183 begin
184 if not File_Names_Case_Sensitive then
185 for J in S'Range loop
186 if S (J) in 'A' .. 'Z' then
187 S (J) := Character'Val
188 (Character'Pos (S (J)) +
189 Character'Pos ('a') -
190 Character'Pos ('A'));
191 end if;
192 end loop;
193 end if;
194 end Canonical_Case_File_Name;
196 ---------------
197 -- Expansion --
198 ---------------
200 function Expansion (Iterator : Expansion_Iterator) return String is
201 use GNAT.Directory_Operations;
202 type Pointer is access all Expansion_Iterator;
204 It : constant Pointer := Iterator'Unrestricted_Access;
205 S : String (1 .. 1024);
206 Last : Natural;
208 Current : Depth := It.Current_Depth;
209 NL : Positive;
211 begin
212 -- It is assumed that a directory is opened at the current level.
213 -- Otherwise GNAT.Directory_Operations.Directory_Error will be raised
214 -- at the first call to Read.
216 loop
217 Read (It.Levels (Current).Dir, S, Last);
219 -- If we have exhausted the directory, close it and go back one level
221 if Last = 0 then
222 Close (It.Levels (Current).Dir);
224 -- If we are at level 1, we are finished; return an empty string
226 if Current = 1 then
227 return String'(1 .. 0 => ' ');
228 else
229 -- Otherwise continue with the directory at the previous level
231 Current := Current - 1;
232 It.Current_Depth := Current;
233 end if;
235 -- If this is a directory, that is neither "." or "..", attempt to
236 -- go to the next level.
238 elsif Is_Directory
239 (It.Dir_Name (1 .. It.Levels (Current).Name_Last) & S (1 .. Last))
240 and then S (1 .. Last) /= "."
241 and then S (1 .. Last) /= ".."
242 then
243 -- We can go to the next level only if we have not reached the
244 -- maximum depth,
246 if Current < It.Maximum_Depth then
247 NL := It.Levels (Current).Name_Last;
249 -- And if relative path of this new directory is not too long
251 if NL + Last + 1 < Max_Path_Length then
252 Current := Current + 1;
253 It.Current_Depth := Current;
254 It.Dir_Name (NL + 1 .. NL + Last) := S (1 .. Last);
255 NL := NL + Last + 1;
256 It.Dir_Name (NL) := Directory_Separator;
257 It.Levels (Current).Name_Last := NL;
258 Canonical_Case_File_Name (It.Dir_Name (1 .. NL));
260 -- Open the new directory, and read from it
262 GNAT.Directory_Operations.Open
263 (It.Levels (Current).Dir, It.Dir_Name (1 .. NL));
264 end if;
265 end if;
267 -- If not a directory, check the relative path against the pattern
269 else
270 declare
271 Name : String :=
272 It.Dir_Name (It.Start .. It.Levels (Current).Name_Last)
273 & S (1 .. Last);
274 begin
275 Canonical_Case_File_Name (Name);
277 -- If it matches return the relative path
279 if GNAT.Regexp.Match (Name, Iterator.Regexp) then
280 return Name;
281 end if;
282 end;
283 end if;
284 end loop;
285 end Expansion;
287 -----------------
288 -- Full_Switch --
289 -----------------
291 function Full_Switch
292 (Parser : Opt_Parser := Command_Line_Parser) return String
294 begin
295 if Parser.The_Switch.Extra = ASCII.NUL then
296 return Argument (Parser, Parser.The_Switch.Arg_Num)
297 (Parser.The_Switch.First .. Parser.The_Switch.Last);
298 else
299 return Parser.The_Switch.Extra
300 & Argument (Parser, Parser.The_Switch.Arg_Num)
301 (Parser.The_Switch.First .. Parser.The_Switch.Last);
302 end if;
303 end Full_Switch;
305 ------------------
306 -- Get_Argument --
307 ------------------
309 function Get_Argument
310 (Do_Expansion : Boolean := False;
311 Parser : Opt_Parser := Command_Line_Parser) return String
313 begin
314 if Parser.In_Expansion then
315 declare
316 S : constant String := Expansion (Parser.Expansion_It);
317 begin
318 if S'Length /= 0 then
319 return S;
320 else
321 Parser.In_Expansion := False;
322 end if;
323 end;
324 end if;
326 if Parser.Current_Argument > Parser.Arg_Count then
328 -- If this is the first time this function is called
330 if Parser.Current_Index = 1 then
331 Parser.Current_Argument := 1;
332 while Parser.Current_Argument <= Parser.Arg_Count
333 and then Parser.Section (Parser.Current_Argument) /=
334 Parser.Current_Section
335 loop
336 Parser.Current_Argument := Parser.Current_Argument + 1;
337 end loop;
338 else
339 return String'(1 .. 0 => ' ');
340 end if;
342 elsif Parser.Section (Parser.Current_Argument) = 0 then
343 while Parser.Current_Argument <= Parser.Arg_Count
344 and then Parser.Section (Parser.Current_Argument) /=
345 Parser.Current_Section
346 loop
347 Parser.Current_Argument := Parser.Current_Argument + 1;
348 end loop;
349 end if;
351 Parser.Current_Index := Integer'Last;
353 while Parser.Current_Argument <= Parser.Arg_Count
354 and then Parser.Is_Switch (Parser.Current_Argument)
355 loop
356 Parser.Current_Argument := Parser.Current_Argument + 1;
357 end loop;
359 if Parser.Current_Argument > Parser.Arg_Count then
360 return String'(1 .. 0 => ' ');
361 elsif Parser.Section (Parser.Current_Argument) = 0 then
362 return Get_Argument (Do_Expansion);
363 end if;
365 Parser.Current_Argument := Parser.Current_Argument + 1;
367 -- Could it be a file name with wild cards to expand?
369 if Do_Expansion then
370 declare
371 Arg : constant String :=
372 Argument (Parser, Parser.Current_Argument - 1);
373 Index : Positive;
375 begin
376 Index := Arg'First;
377 while Index <= Arg'Last loop
378 if Arg (Index) = '*'
379 or else Arg (Index) = '?'
380 or else Arg (Index) = '['
381 then
382 Parser.In_Expansion := True;
383 Start_Expansion (Parser.Expansion_It, Arg);
384 return Get_Argument (Do_Expansion);
385 end if;
387 Index := Index + 1;
388 end loop;
389 end;
390 end if;
392 return Argument (Parser, Parser.Current_Argument - 1);
393 end Get_Argument;
395 ----------------------------------
396 -- Find_Longest_Matching_Switch --
397 ----------------------------------
399 procedure Find_Longest_Matching_Switch
400 (Switches : String;
401 Arg : String;
402 Index_In_Switches : out Integer;
403 Switch_Length : out Integer;
404 Param : out Switch_Parameter_Type)
406 Index : Natural;
407 Length : Natural := 1;
408 P : Switch_Parameter_Type;
410 begin
411 Index_In_Switches := 0;
412 Switch_Length := 0;
414 -- Remove all leading spaces first to make sure that Index points
415 -- at the start of the first switch.
417 Index := Switches'First;
418 while Index <= Switches'Last and then Switches (Index) = ' ' loop
419 Index := Index + 1;
420 end loop;
422 while Index <= Switches'Last loop
424 -- Search the length of the parameter at this position in Switches
426 Length := Index;
427 while Length <= Switches'Last
428 and then Switches (Length) /= ' '
429 loop
430 Length := Length + 1;
431 end loop;
433 if Length = Index + 1 then
434 P := Parameter_None;
435 else
436 case Switches (Length - 1) is
437 when ':' =>
438 P := Parameter_With_Optional_Space;
439 Length := Length - 1;
440 when '=' =>
441 P := Parameter_With_Space_Or_Equal;
442 Length := Length - 1;
443 when '!' =>
444 P := Parameter_No_Space;
445 Length := Length - 1;
446 when '?' =>
447 P := Parameter_Optional;
448 Length := Length - 1;
449 when others =>
450 P := Parameter_None;
451 end case;
452 end if;
454 -- If it is the one we searched, it may be a candidate
456 if Arg'First + Length - 1 - Index <= Arg'Last
457 and then Switches (Index .. Length - 1) =
458 Arg (Arg'First .. Arg'First + Length - 1 - Index)
459 and then Length - Index > Switch_Length
460 then
461 Param := P;
462 Index_In_Switches := Index;
463 Switch_Length := Length - Index;
464 end if;
466 -- Look for the next switch in Switches
468 while Index <= Switches'Last
469 and then Switches (Index) /= ' '
470 loop
471 Index := Index + 1;
472 end loop;
474 Index := Index + 1;
475 end loop;
476 end Find_Longest_Matching_Switch;
478 ------------
479 -- Getopt --
480 ------------
482 function Getopt
483 (Switches : String;
484 Concatenate : Boolean := True;
485 Parser : Opt_Parser := Command_Line_Parser) return Character
487 Dummy : Boolean;
488 pragma Unreferenced (Dummy);
490 begin
491 <<Restart>>
493 -- If we have finished parsing the current command line item (there
494 -- might be multiple switches in a single item), then go to the next
495 -- element
497 if Parser.Current_Argument > Parser.Arg_Count
498 or else (Parser.Current_Index >
499 Argument (Parser, Parser.Current_Argument)'Last
500 and then not Goto_Next_Argument_In_Section (Parser))
501 then
502 return ASCII.NUL;
503 end if;
505 -- By default, the switch will not have a parameter
507 Parser.The_Parameter :=
508 (Integer'Last, Integer'Last, Integer'Last - 1, ASCII.NUL);
509 Parser.The_Separator := ASCII.NUL;
511 declare
512 Arg : constant String :=
513 Argument (Parser, Parser.Current_Argument);
514 Index_Switches : Natural := 0;
515 Max_Length : Natural := 0;
516 End_Index : Natural;
517 Param : Switch_Parameter_Type;
518 begin
519 -- If we are on a new item, test if this might be a switch
521 if Parser.Current_Index = Arg'First then
522 if Arg (Arg'First) /= Parser.Switch_Character then
524 -- If it isn't a switch, return it immediately. We also know it
525 -- isn't the parameter to a previous switch, since that has
526 -- already been handled
528 if Switches (Switches'First) = '*' then
529 Set_Parameter
530 (Parser.The_Switch,
531 Arg_Num => Parser.Current_Argument,
532 First => Arg'First,
533 Last => Arg'Last);
534 Parser.Is_Switch (Parser.Current_Argument) := True;
535 Dummy := Goto_Next_Argument_In_Section (Parser);
536 return '*';
537 end if;
539 if Parser.Stop_At_First then
540 Parser.Current_Argument := Positive'Last;
541 return ASCII.NUL;
543 elsif not Goto_Next_Argument_In_Section (Parser) then
544 return ASCII.NUL;
546 else
547 -- Recurse to get the next switch on the command line
549 goto Restart;
550 end if;
551 end if;
553 -- We are on the first character of a new command line argument,
554 -- which starts with Switch_Character. Further analysis is needed.
556 Parser.Current_Index := Parser.Current_Index + 1;
557 Parser.Is_Switch (Parser.Current_Argument) := True;
558 end if;
560 Find_Longest_Matching_Switch
561 (Switches => Switches,
562 Arg => Arg (Parser.Current_Index .. Arg'Last),
563 Index_In_Switches => Index_Switches,
564 Switch_Length => Max_Length,
565 Param => Param);
567 -- If switch is not accepted, it is either invalid or is returned
568 -- in the context of '*'.
570 if Index_Switches = 0 then
572 -- Depending on the value of Concatenate, the full switch is
573 -- a single character or the rest of the argument.
575 if Concatenate then
576 End_Index := Parser.Current_Index;
577 else
578 End_Index := Arg'Last;
579 end if;
581 if Switches (Switches'First) = '*' then
583 -- Always prepend the switch character, so that users know that
584 -- this comes from a switch on the command line. This is
585 -- especially important when Concatenate is False, since
586 -- otherwise the current argument first character is lost.
588 Set_Parameter
589 (Parser.The_Switch,
590 Arg_Num => Parser.Current_Argument,
591 First => Parser.Current_Index,
592 Last => Arg'Last,
593 Extra => Parser.Switch_Character);
594 Parser.Is_Switch (Parser.Current_Argument) := True;
595 Dummy := Goto_Next_Argument_In_Section (Parser);
596 return '*';
597 end if;
599 Set_Parameter
600 (Parser.The_Switch,
601 Arg_Num => Parser.Current_Argument,
602 First => Parser.Current_Index,
603 Last => End_Index);
604 Parser.Current_Index := End_Index + 1;
605 raise Invalid_Switch;
606 end if;
608 End_Index := Parser.Current_Index + Max_Length - 1;
609 Set_Parameter
610 (Parser.The_Switch,
611 Arg_Num => Parser.Current_Argument,
612 First => Parser.Current_Index,
613 Last => End_Index);
615 case Param is
616 when Parameter_With_Optional_Space =>
617 if End_Index < Arg'Last then
618 Set_Parameter
619 (Parser.The_Parameter,
620 Arg_Num => Parser.Current_Argument,
621 First => End_Index + 1,
622 Last => Arg'Last);
623 Dummy := Goto_Next_Argument_In_Section (Parser);
625 elsif Parser.Current_Argument < Parser.Arg_Count
626 and then Parser.Section (Parser.Current_Argument + 1) /= 0
627 then
628 Parser.Current_Argument := Parser.Current_Argument + 1;
629 Parser.The_Separator := ' ';
630 Set_Parameter
631 (Parser.The_Parameter,
632 Arg_Num => Parser.Current_Argument,
633 First => Argument (Parser, Parser.Current_Argument)'First,
634 Last => Argument (Parser, Parser.Current_Argument)'Last);
635 Parser.Is_Switch (Parser.Current_Argument) := True;
636 Dummy := Goto_Next_Argument_In_Section (Parser);
638 else
639 Parser.Current_Index := End_Index + 1;
640 raise Invalid_Parameter;
641 end if;
643 when Parameter_With_Space_Or_Equal =>
645 -- If the switch is of the form <switch>=xxx
647 if End_Index < Arg'Last then
649 if Arg (End_Index + 1) = '='
650 and then End_Index + 1 < Arg'Last
651 then
652 Parser.The_Separator := '=';
653 Set_Parameter
654 (Parser.The_Parameter,
655 Arg_Num => Parser.Current_Argument,
656 First => End_Index + 2,
657 Last => Arg'Last);
658 Dummy := Goto_Next_Argument_In_Section (Parser);
659 else
660 Parser.Current_Index := End_Index + 1;
661 raise Invalid_Parameter;
662 end if;
664 -- If the switch is of the form <switch> xxx
666 elsif Parser.Current_Argument < Parser.Arg_Count
667 and then Parser.Section (Parser.Current_Argument + 1) /= 0
668 then
669 Parser.Current_Argument := Parser.Current_Argument + 1;
670 Parser.The_Separator := ' ';
671 Set_Parameter
672 (Parser.The_Parameter,
673 Arg_Num => Parser.Current_Argument,
674 First => Argument (Parser, Parser.Current_Argument)'First,
675 Last => Argument (Parser, Parser.Current_Argument)'Last);
676 Parser.Is_Switch (Parser.Current_Argument) := True;
677 Dummy := Goto_Next_Argument_In_Section (Parser);
679 else
680 Parser.Current_Index := End_Index + 1;
681 raise Invalid_Parameter;
682 end if;
684 when Parameter_No_Space =>
686 if End_Index < Arg'Last then
687 Set_Parameter
688 (Parser.The_Parameter,
689 Arg_Num => Parser.Current_Argument,
690 First => End_Index + 1,
691 Last => Arg'Last);
692 Dummy := Goto_Next_Argument_In_Section (Parser);
694 else
695 Parser.Current_Index := End_Index + 1;
696 raise Invalid_Parameter;
697 end if;
699 when Parameter_Optional =>
701 if End_Index < Arg'Last then
702 Set_Parameter
703 (Parser.The_Parameter,
704 Arg_Num => Parser.Current_Argument,
705 First => End_Index + 1,
706 Last => Arg'Last);
707 end if;
709 Dummy := Goto_Next_Argument_In_Section (Parser);
711 when Parameter_None =>
713 if Concatenate or else End_Index = Arg'Last then
714 Parser.Current_Index := End_Index + 1;
716 else
717 -- If Concatenate is False and the full argument is not
718 -- recognized as a switch, this is an invalid switch.
720 if Switches (Switches'First) = '*' then
721 Set_Parameter
722 (Parser.The_Switch,
723 Arg_Num => Parser.Current_Argument,
724 First => Arg'First,
725 Last => Arg'Last);
726 Parser.Is_Switch (Parser.Current_Argument) := True;
727 Dummy := Goto_Next_Argument_In_Section (Parser);
728 return '*';
729 end if;
731 Set_Parameter
732 (Parser.The_Switch,
733 Arg_Num => Parser.Current_Argument,
734 First => Parser.Current_Index,
735 Last => Arg'Last);
736 Parser.Current_Index := Arg'Last + 1;
737 raise Invalid_Switch;
738 end if;
739 end case;
741 return Switches (Index_Switches);
742 end;
743 end Getopt;
745 -----------------------------------
746 -- Goto_Next_Argument_In_Section --
747 -----------------------------------
749 function Goto_Next_Argument_In_Section
750 (Parser : Opt_Parser) return Boolean
752 begin
753 Parser.Current_Argument := Parser.Current_Argument + 1;
755 if Parser.Current_Argument > Parser.Arg_Count
756 or else Parser.Section (Parser.Current_Argument) = 0
757 then
758 loop
759 Parser.Current_Argument := Parser.Current_Argument + 1;
761 if Parser.Current_Argument > Parser.Arg_Count then
762 Parser.Current_Index := 1;
763 return False;
764 end if;
766 exit when Parser.Section (Parser.Current_Argument) =
767 Parser.Current_Section;
768 end loop;
769 end if;
771 Parser.Current_Index :=
772 Argument (Parser, Parser.Current_Argument)'First;
774 return True;
775 end Goto_Next_Argument_In_Section;
777 ------------------
778 -- Goto_Section --
779 ------------------
781 procedure Goto_Section
782 (Name : String := "";
783 Parser : Opt_Parser := Command_Line_Parser)
785 Index : Integer;
787 begin
788 Parser.In_Expansion := False;
790 if Name = "" then
791 Parser.Current_Argument := 1;
792 Parser.Current_Index := 1;
793 Parser.Current_Section := 1;
794 return;
795 end if;
797 Index := 1;
798 while Index <= Parser.Arg_Count loop
799 if Parser.Section (Index) = 0
800 and then Argument (Parser, Index) = Parser.Switch_Character & Name
801 then
802 Parser.Current_Argument := Index + 1;
803 Parser.Current_Index := 1;
805 if Parser.Current_Argument <= Parser.Arg_Count then
806 Parser.Current_Section :=
807 Parser.Section (Parser.Current_Argument);
808 end if;
809 return;
810 end if;
812 Index := Index + 1;
813 end loop;
815 Parser.Current_Argument := Positive'Last;
816 Parser.Current_Index := 2; -- so that Get_Argument returns nothing
817 end Goto_Section;
819 ----------------------------
820 -- Initialize_Option_Scan --
821 ----------------------------
823 procedure Initialize_Option_Scan
824 (Switch_Char : Character := '-';
825 Stop_At_First_Non_Switch : Boolean := False;
826 Section_Delimiters : String := "")
828 begin
829 Internal_Initialize_Option_Scan
830 (Parser => Command_Line_Parser,
831 Switch_Char => Switch_Char,
832 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
833 Section_Delimiters => Section_Delimiters);
834 end Initialize_Option_Scan;
836 ----------------------------
837 -- Initialize_Option_Scan --
838 ----------------------------
840 procedure Initialize_Option_Scan
841 (Parser : out Opt_Parser;
842 Command_Line : GNAT.OS_Lib.Argument_List_Access;
843 Switch_Char : Character := '-';
844 Stop_At_First_Non_Switch : Boolean := False;
845 Section_Delimiters : String := "")
847 begin
848 Free (Parser);
850 if Command_Line = null then
851 Parser := new Opt_Parser_Data (CL.Argument_Count);
852 Initialize_Option_Scan
853 (Switch_Char => Switch_Char,
854 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
855 Section_Delimiters => Section_Delimiters);
856 else
857 Parser := new Opt_Parser_Data (Command_Line'Length);
858 Parser.Arguments := Command_Line;
859 Internal_Initialize_Option_Scan
860 (Parser => Parser,
861 Switch_Char => Switch_Char,
862 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
863 Section_Delimiters => Section_Delimiters);
864 end if;
865 end Initialize_Option_Scan;
867 -------------------------------------
868 -- Internal_Initialize_Option_Scan --
869 -------------------------------------
871 procedure Internal_Initialize_Option_Scan
872 (Parser : Opt_Parser;
873 Switch_Char : Character;
874 Stop_At_First_Non_Switch : Boolean;
875 Section_Delimiters : String)
877 Section_Num : Section_Number;
878 Section_Index : Integer;
879 Last : Integer;
880 Delimiter_Found : Boolean;
882 Discard : Boolean;
883 pragma Warnings (Off, Discard);
885 begin
886 Parser.Current_Argument := 0;
887 Parser.Current_Index := 0;
888 Parser.In_Expansion := False;
889 Parser.Switch_Character := Switch_Char;
890 Parser.Stop_At_First := Stop_At_First_Non_Switch;
892 -- If we are using sections, we have to preprocess the command line
893 -- to delimit them. A section can be repeated, so we just give each
894 -- item on the command line a section number
896 Section_Num := 1;
897 Section_Index := Section_Delimiters'First;
898 while Section_Index <= Section_Delimiters'Last loop
899 Last := Section_Index;
900 while Last <= Section_Delimiters'Last
901 and then Section_Delimiters (Last) /= ' '
902 loop
903 Last := Last + 1;
904 end loop;
906 Delimiter_Found := False;
907 Section_Num := Section_Num + 1;
909 for Index in 1 .. Parser.Arg_Count loop
910 if Argument (Parser, Index)(1) = Parser.Switch_Character
911 and then
912 Argument (Parser, Index) = Parser.Switch_Character &
913 Section_Delimiters
914 (Section_Index .. Last - 1)
915 then
916 Parser.Section (Index) := 0;
917 Delimiter_Found := True;
919 elsif Parser.Section (Index) = 0 then
920 Delimiter_Found := False;
922 elsif Delimiter_Found then
923 Parser.Section (Index) := Section_Num;
924 end if;
925 end loop;
927 Section_Index := Last + 1;
928 while Section_Index <= Section_Delimiters'Last
929 and then Section_Delimiters (Section_Index) = ' '
930 loop
931 Section_Index := Section_Index + 1;
932 end loop;
933 end loop;
935 Discard := Goto_Next_Argument_In_Section (Parser);
936 end Internal_Initialize_Option_Scan;
938 ---------------
939 -- Parameter --
940 ---------------
942 function Parameter
943 (Parser : Opt_Parser := Command_Line_Parser) return String
945 begin
946 if Parser.The_Parameter.First > Parser.The_Parameter.Last then
947 return String'(1 .. 0 => ' ');
948 else
949 return Argument (Parser, Parser.The_Parameter.Arg_Num)
950 (Parser.The_Parameter.First .. Parser.The_Parameter.Last);
951 end if;
952 end Parameter;
954 ---------------
955 -- Separator --
956 ---------------
958 function Separator
959 (Parser : Opt_Parser := Command_Line_Parser) return Character
961 begin
962 return Parser.The_Separator;
963 end Separator;
965 -------------------
966 -- Set_Parameter --
967 -------------------
969 procedure Set_Parameter
970 (Variable : out Parameter_Type;
971 Arg_Num : Positive;
972 First : Positive;
973 Last : Positive;
974 Extra : Character := ASCII.NUL)
976 begin
977 Variable.Arg_Num := Arg_Num;
978 Variable.First := First;
979 Variable.Last := Last;
980 Variable.Extra := Extra;
981 end Set_Parameter;
983 ---------------------
984 -- Start_Expansion --
985 ---------------------
987 procedure Start_Expansion
988 (Iterator : out Expansion_Iterator;
989 Pattern : String;
990 Directory : String := "";
991 Basic_Regexp : Boolean := True)
993 Directory_Separator : Character;
994 pragma Import (C, Directory_Separator, "__gnat_dir_separator");
996 First : Positive := Pattern'First;
997 Pat : String := Pattern;
999 begin
1000 Canonical_Case_File_Name (Pat);
1001 Iterator.Current_Depth := 1;
1003 -- If Directory is unspecified, use the current directory ("./" or ".\")
1005 if Directory = "" then
1006 Iterator.Dir_Name (1 .. 2) := "." & Directory_Separator;
1007 Iterator.Start := 3;
1009 else
1010 Iterator.Dir_Name (1 .. Directory'Length) := Directory;
1011 Iterator.Start := Directory'Length + 1;
1012 Canonical_Case_File_Name (Iterator.Dir_Name (1 .. Directory'Length));
1014 -- Make sure that the last character is a directory separator
1016 if Directory (Directory'Last) /= Directory_Separator then
1017 Iterator.Dir_Name (Iterator.Start) := Directory_Separator;
1018 Iterator.Start := Iterator.Start + 1;
1019 end if;
1020 end if;
1022 Iterator.Levels (1).Name_Last := Iterator.Start - 1;
1024 -- Open the initial Directory, at depth 1
1026 GNAT.Directory_Operations.Open
1027 (Iterator.Levels (1).Dir, Iterator.Dir_Name (1 .. Iterator.Start - 1));
1029 -- If in the current directory and the pattern starts with "./" or ".\",
1030 -- drop the "./" or ".\" from the pattern.
1032 if Directory = "" and then Pat'Length > 2
1033 and then Pat (Pat'First) = '.'
1034 and then Pat (Pat'First + 1) = Directory_Separator
1035 then
1036 First := Pat'First + 2;
1037 end if;
1039 Iterator.Regexp :=
1040 GNAT.Regexp.Compile (Pat (First .. Pat'Last), Basic_Regexp, True);
1042 Iterator.Maximum_Depth := 1;
1044 -- Maximum_Depth is equal to 1 plus the number of directory separators
1045 -- in the pattern.
1047 for Index in First .. Pat'Last loop
1048 if Pat (Index) = Directory_Separator then
1049 Iterator.Maximum_Depth := Iterator.Maximum_Depth + 1;
1050 exit when Iterator.Maximum_Depth = Max_Depth;
1051 end if;
1052 end loop;
1053 end Start_Expansion;
1055 ----------
1056 -- Free --
1057 ----------
1059 procedure Free (Parser : in out Opt_Parser) is
1060 procedure Unchecked_Free is new Ada.Unchecked_Deallocation
1061 (Opt_Parser_Data, Opt_Parser);
1062 begin
1063 if Parser /= null
1064 and then Parser /= Command_Line_Parser
1065 then
1066 Free (Parser.Arguments);
1067 Unchecked_Free (Parser);
1068 end if;
1069 end Free;
1071 ------------------
1072 -- Define_Alias --
1073 ------------------
1075 procedure Define_Alias
1076 (Config : in out Command_Line_Configuration;
1077 Switch : String;
1078 Expanded : String)
1080 begin
1081 if Config = null then
1082 Config := new Command_Line_Configuration_Record;
1083 end if;
1085 Add (Config.Aliases, new String'(Switch));
1086 Add (Config.Expansions, new String'(Expanded));
1087 end Define_Alias;
1089 -------------------
1090 -- Define_Prefix --
1091 -------------------
1093 procedure Define_Prefix
1094 (Config : in out Command_Line_Configuration;
1095 Prefix : String)
1097 begin
1098 if Config = null then
1099 Config := new Command_Line_Configuration_Record;
1100 end if;
1102 Add (Config.Prefixes, new String'(Prefix));
1103 end Define_Prefix;
1105 -------------------
1106 -- Define_Switch --
1107 -------------------
1109 procedure Define_Switch
1110 (Config : in out Command_Line_Configuration;
1111 Switch : String)
1113 begin
1114 if Config = null then
1115 Config := new Command_Line_Configuration_Record;
1116 end if;
1118 Add (Config.Switches, new String'(Switch));
1119 end Define_Switch;
1121 --------------------
1122 -- Define_Section --
1123 --------------------
1125 procedure Define_Section
1126 (Config : in out Command_Line_Configuration;
1127 Section : String)
1129 begin
1130 if Config = null then
1131 Config := new Command_Line_Configuration_Record;
1132 end if;
1134 Add (Config.Sections, new String'(Section));
1135 end Define_Section;
1137 ------------------
1138 -- Get_Switches --
1139 ------------------
1141 function Get_Switches
1142 (Config : Command_Line_Configuration;
1143 Switch_Char : Character)
1144 return String
1146 Ret : Ada.Strings.Unbounded.Unbounded_String;
1147 use type Ada.Strings.Unbounded.Unbounded_String;
1149 begin
1150 if Config = null or else Config.Switches = null then
1151 return "";
1152 end if;
1154 for J in Config.Switches'Range loop
1155 if Config.Switches (J) (Config.Switches (J)'First) = Switch_Char then
1156 Ret :=
1157 Ret & " " &
1158 Config.Switches (J)
1159 (Config.Switches (J)'First + 1 .. Config.Switches (J)'Last);
1160 else
1161 Ret := Ret & " " & Config.Switches (J).all;
1162 end if;
1163 end loop;
1165 return Ada.Strings.Unbounded.To_String (Ret);
1166 end Get_Switches;
1168 -----------------------
1169 -- Set_Configuration --
1170 -----------------------
1172 procedure Set_Configuration
1173 (Cmd : in out Command_Line;
1174 Config : Command_Line_Configuration)
1176 begin
1177 Cmd.Config := Config;
1178 end Set_Configuration;
1180 -----------------------
1181 -- Get_Configuration --
1182 -----------------------
1184 function Get_Configuration
1185 (Cmd : Command_Line) return Command_Line_Configuration is
1186 begin
1187 return Cmd.Config;
1188 end Get_Configuration;
1190 ----------------------
1191 -- Set_Command_Line --
1192 ----------------------
1194 procedure Set_Command_Line
1195 (Cmd : in out Command_Line;
1196 Switches : String;
1197 Getopt_Description : String := "";
1198 Switch_Char : Character := '-')
1200 Tmp : Argument_List_Access;
1201 Parser : Opt_Parser;
1202 S : Character;
1203 Section : String_Access := null;
1205 function Real_Full_Switch
1206 (S : Character;
1207 Parser : Opt_Parser) return String;
1208 -- Ensure that the returned switch value contains the
1209 -- Switch_Char prefix if needed.
1211 ----------------------
1212 -- Real_Full_Switch --
1213 ----------------------
1215 function Real_Full_Switch
1216 (S : Character;
1217 Parser : Opt_Parser) return String
1219 begin
1220 if S = '*' then
1221 return Full_Switch (Parser);
1222 else
1223 return Switch_Char & Full_Switch (Parser);
1224 end if;
1225 end Real_Full_Switch;
1227 -- Start of processing for Set_Command_Line
1229 begin
1230 Free (Cmd.Expanded);
1231 Free (Cmd.Params);
1233 if Switches /= "" then
1234 Tmp := Argument_String_To_List (Switches);
1235 Initialize_Option_Scan (Parser, Tmp, Switch_Char);
1237 loop
1238 begin
1239 S := Getopt (Switches => "* " & Getopt_Description,
1240 Concatenate => False,
1241 Parser => Parser);
1242 exit when S = ASCII.NUL;
1244 declare
1245 Sw : constant String :=
1246 Real_Full_Switch (S, Parser);
1247 Is_Section : Boolean := False;
1249 begin
1250 if Cmd.Config /= null
1251 and then Cmd.Config.Sections /= null
1252 then
1253 Section_Search :
1254 for S in Cmd.Config.Sections'Range loop
1255 if Sw = Cmd.Config.Sections (S).all then
1256 Section := Cmd.Config.Sections (S);
1257 Is_Section := True;
1259 exit Section_Search;
1260 end if;
1261 end loop Section_Search;
1262 end if;
1264 if not Is_Section then
1265 if Section = null then
1267 -- Work around some weird cases: some switches may
1268 -- expect parameters, but have the same value as
1269 -- longer switches: -gnaty3 (-gnaty, parameter=3) and
1270 -- -gnatya (-gnatya, no parameter).
1272 -- So we are calling add_switch here with parameter
1273 -- attached. This will be anyway correctly handled by
1274 -- Add_Switch if -gnaty3 is actually provided.
1276 if Separator (Parser) = ASCII.NUL then
1277 Add_Switch
1278 (Cmd, Sw & Parameter (Parser), "");
1279 else
1280 Add_Switch
1281 (Cmd, Sw, Parameter (Parser), Separator (Parser));
1282 end if;
1283 else
1284 if Separator (Parser) = ASCII.NUL then
1285 Add_Switch
1286 (Cmd, Sw & Parameter (Parser), "",
1287 Separator (Parser),
1288 Section.all);
1289 else
1290 Add_Switch
1291 (Cmd, Sw,
1292 Parameter (Parser),
1293 Separator (Parser),
1294 Section.all);
1295 end if;
1296 end if;
1297 end if;
1298 end;
1300 exception
1301 when Invalid_Parameter =>
1303 -- Add it with no parameter, if that's the way the user
1304 -- wants it.
1306 -- Specify the separator in all cases, as the switch might
1307 -- need to be unaliased, and the alias might contain
1308 -- switches with parameters.
1310 if Section = null then
1311 Add_Switch
1312 (Cmd, Switch_Char & Full_Switch (Parser),
1313 Separator => Separator (Parser));
1314 else
1315 Add_Switch
1316 (Cmd, Switch_Char & Full_Switch (Parser),
1317 Separator => Separator (Parser),
1318 Section => Section.all);
1319 end if;
1320 end;
1321 end loop;
1323 Free (Parser);
1324 end if;
1325 end Set_Command_Line;
1327 ----------------
1328 -- Looking_At --
1329 ----------------
1331 function Looking_At
1332 (Type_Str : String;
1333 Index : Natural;
1334 Substring : String) return Boolean is
1335 begin
1336 return Index + Substring'Length - 1 <= Type_Str'Last
1337 and then Type_Str (Index .. Index + Substring'Length - 1) = Substring;
1338 end Looking_At;
1340 ------------------------
1341 -- Can_Have_Parameter --
1342 ------------------------
1344 function Can_Have_Parameter (S : String) return Boolean is
1345 begin
1346 if S'Length <= 1 then
1347 return False;
1348 end if;
1350 case S (S'Last) is
1351 when '!' | ':' | '?' | '=' =>
1352 return True;
1353 when others =>
1354 return False;
1355 end case;
1356 end Can_Have_Parameter;
1358 -----------------------
1359 -- Require_Parameter --
1360 -----------------------
1362 function Require_Parameter (S : String) return Boolean is
1363 begin
1364 if S'Length <= 1 then
1365 return False;
1366 end if;
1368 case S (S'Last) is
1369 when '!' | ':' | '=' =>
1370 return True;
1371 when others =>
1372 return False;
1373 end case;
1374 end Require_Parameter;
1376 -------------------
1377 -- Actual_Switch --
1378 -------------------
1380 function Actual_Switch (S : String) return String is
1381 begin
1382 if S'Length <= 1 then
1383 return S;
1384 end if;
1386 case S (S'Last) is
1387 when '!' | ':' | '?' | '=' =>
1388 return S (S'First .. S'Last - 1);
1389 when others =>
1390 return S;
1391 end case;
1392 end Actual_Switch;
1394 ----------------------------
1395 -- For_Each_Simple_Switch --
1396 ----------------------------
1398 procedure For_Each_Simple_Switch
1399 (Cmd : Command_Line;
1400 Switch : String;
1401 Parameter : String := "";
1402 Unalias : Boolean := True)
1404 function Group_Analysis
1405 (Prefix : String;
1406 Group : String) return Boolean;
1407 -- Perform the analysis of a group of switches.
1409 --------------------
1410 -- Group_Analysis --
1411 --------------------
1413 function Group_Analysis
1414 (Prefix : String;
1415 Group : String) return Boolean
1417 Idx : Natural;
1418 Found : Boolean;
1420 begin
1421 Idx := Group'First;
1422 while Idx <= Group'Last loop
1423 Found := False;
1425 for S in Cmd.Config.Switches'Range loop
1426 declare
1427 Sw : constant String :=
1428 Actual_Switch
1429 (Cmd.Config.Switches (S).all);
1430 Full : constant String :=
1431 Prefix & Group (Idx .. Group'Last);
1432 Last : Natural;
1433 Param : Natural;
1435 begin
1436 if Sw'Length >= Prefix'Length
1438 -- Verify that sw starts with Prefix
1440 and then Looking_At (Sw, Sw'First, Prefix)
1442 -- Verify that the group starts with sw
1444 and then Looking_At (Full, Full'First, Sw)
1445 then
1446 Last := Idx + Sw'Length - Prefix'Length - 1;
1447 Param := Last + 1;
1449 if Can_Have_Parameter (Cmd.Config.Switches (S).all) then
1451 -- Include potential parameter to the recursive call.
1452 -- Only numbers are allowed.
1454 while Last < Group'Last
1455 and then Group (Last + 1) in '0' .. '9'
1456 loop
1457 Last := Last + 1;
1458 end loop;
1459 end if;
1461 if not Require_Parameter (Cmd.Config.Switches (S).all)
1462 or else Last >= Param
1463 then
1464 if Idx = Group'First
1465 and then Last = Group'Last
1466 and then Last < Param
1467 then
1468 -- The group only concerns a single switch. Do not
1469 -- perform recursive call.
1471 -- Note that we still perform a recursive call if
1472 -- a parameter is detected in the switch, as this
1473 -- is a way to correctly identify such a parameter
1474 -- in aliases.
1476 return False;
1477 end if;
1479 Found := True;
1481 -- Recursive call, using the detected parameter if any
1483 if Last >= Param then
1484 For_Each_Simple_Switch
1485 (Cmd,
1486 Prefix & Group (Idx .. Param - 1),
1487 Group (Param .. Last));
1488 else
1489 For_Each_Simple_Switch
1490 (Cmd, Prefix & Group (Idx .. Last), "");
1491 end if;
1493 Idx := Last + 1;
1494 exit;
1495 end if;
1496 end if;
1497 end;
1498 end loop;
1500 if not Found then
1501 For_Each_Simple_Switch (Cmd, Prefix & Group (Idx), "");
1502 Idx := Idx + 1;
1503 end if;
1504 end loop;
1506 return True;
1507 end Group_Analysis;
1509 begin
1510 -- Are we adding a switch that can in fact be expanded through aliases ?
1511 -- If yes, we add separately each of its expansion.
1513 -- This takes care of expansions like "-T" -> "-gnatwrs", where the
1514 -- alias and its expansion do not have the same prefix. Given the order
1515 -- in which we do things here, the expansion of the alias will itself
1516 -- be checked for a common prefix and further split into simple switches
1518 if Unalias
1519 and then Cmd.Config /= null
1520 and then Cmd.Config.Aliases /= null
1521 then
1522 for A in Cmd.Config.Aliases'Range loop
1523 if Cmd.Config.Aliases (A).all = Switch
1524 and then Parameter = ""
1525 then
1526 For_Each_Simple_Switch
1527 (Cmd, Cmd.Config.Expansions (A).all, "");
1528 return;
1529 end if;
1530 end loop;
1531 end if;
1533 -- Are we adding a switch grouping several switches ? If yes, add each
1534 -- of the simple switches instead.
1536 if Cmd.Config /= null
1537 and then Cmd.Config.Prefixes /= null
1538 then
1539 for P in Cmd.Config.Prefixes'Range loop
1540 if Switch'Length > Cmd.Config.Prefixes (P)'Length + 1
1541 and then Looking_At
1542 (Switch, Switch'First, Cmd.Config.Prefixes (P).all)
1543 then
1544 -- Alias expansion will be done recursively
1545 if Cmd.Config.Switches = null then
1546 for S in Switch'First + Cmd.Config.Prefixes (P)'Length
1547 .. Switch'Last
1548 loop
1549 For_Each_Simple_Switch
1550 (Cmd, Cmd.Config.Prefixes (P).all & Switch (S), "");
1551 end loop;
1553 return;
1555 elsif Group_Analysis
1556 (Cmd.Config.Prefixes (P).all,
1557 Switch
1558 (Switch'First + Cmd.Config.Prefixes (P)'Length
1559 .. Switch'Last))
1560 then
1561 -- Recursive calls already done on each switch of the
1562 -- group. Let's return to not call Callback.
1563 return;
1564 end if;
1565 end if;
1566 end loop;
1567 end if;
1569 -- Test if added switch is a known switch with parameter attached
1571 if Parameter = ""
1572 and then Cmd.Config /= null
1573 and then Cmd.Config.Switches /= null
1574 then
1575 for S in Cmd.Config.Switches'Range loop
1576 declare
1577 Sw : constant String :=
1578 Actual_Switch (Cmd.Config.Switches (S).all);
1579 Last : Natural;
1580 Param : Natural;
1582 begin
1583 -- Verify that switch starts with Sw
1584 -- What if the "verification" fails???
1586 if Switch'Length >= Sw'Length
1587 and then Looking_At (Switch, Switch'First, Sw)
1588 then
1589 Param := Switch'First + Sw'Length - 1;
1590 Last := Param;
1592 if Can_Have_Parameter (Cmd.Config.Switches (S).all) then
1593 while Last < Switch'Last
1594 and then Switch (Last + 1) in '0' .. '9'
1595 loop
1596 Last := Last + 1;
1597 end loop;
1598 end if;
1600 -- If full Switch is a known switch with attached parameter
1601 -- then we use this parameter in the callback.
1603 if Last = Switch'Last then
1604 Callback
1605 (Switch (Switch'First .. Param),
1606 Switch (Param + 1 .. Last));
1607 return;
1609 end if;
1610 end if;
1611 end;
1612 end loop;
1613 end if;
1615 Callback (Switch, Parameter);
1616 end For_Each_Simple_Switch;
1618 ----------------
1619 -- Add_Switch --
1620 ----------------
1622 procedure Add_Switch
1623 (Cmd : in out Command_Line;
1624 Switch : String;
1625 Parameter : String := "";
1626 Separator : Character := ' ';
1627 Section : String := "";
1628 Add_Before : Boolean := False)
1630 Success : Boolean;
1631 pragma Unreferenced (Success);
1632 begin
1633 Add_Switch
1634 (Cmd, Switch, Parameter, Separator, Section, Add_Before, Success);
1635 end Add_Switch;
1637 ----------------
1638 -- Add_Switch --
1639 ----------------
1641 procedure Add_Switch
1642 (Cmd : in out Command_Line;
1643 Switch : String;
1644 Parameter : String := "";
1645 Separator : Character := ' ';
1646 Section : String := "";
1647 Add_Before : Boolean := False;
1648 Success : out Boolean)
1650 procedure Add_Simple_Switch (Simple : String; Param : String);
1651 -- Add a new switch that has had all its aliases expanded, and switches
1652 -- ungrouped. We know there are no more aliases in Switches.
1654 -----------------------
1655 -- Add_Simple_Switch --
1656 -----------------------
1658 procedure Add_Simple_Switch (Simple : String; Param : String) is
1659 begin
1660 if Cmd.Expanded = null then
1661 Cmd.Expanded := new Argument_List'(1 .. 1 => new String'(Simple));
1663 if Param /= "" then
1664 Cmd.Params := new Argument_List'
1665 (1 .. 1 => new String'(Separator & Param));
1667 else
1668 Cmd.Params := new Argument_List'(1 .. 1 => null);
1669 end if;
1671 if Section = "" then
1672 Cmd.Sections := new Argument_List'(1 .. 1 => null);
1674 else
1675 Cmd.Sections := new Argument_List'
1676 (1 .. 1 => new String'(Section));
1677 end if;
1679 else
1680 -- Do we already have this switch?
1682 for C in Cmd.Expanded'Range loop
1683 if Cmd.Expanded (C).all = Simple
1684 and then
1685 ((Cmd.Params (C) = null and then Param = "")
1686 or else
1687 (Cmd.Params (C) /= null
1688 and then Cmd.Params (C).all = Separator & Param))
1689 and then
1690 ((Cmd.Sections (C) = null and then Section = "")
1691 or else
1692 (Cmd.Sections (C) /= null
1693 and then Cmd.Sections (C).all = Section))
1694 then
1695 return;
1696 end if;
1697 end loop;
1699 -- Inserting at least one switch
1701 Success := True;
1702 Add (Cmd.Expanded, new String'(Simple), Add_Before);
1704 if Param /= "" then
1706 (Cmd.Params,
1707 new String'(Separator & Param),
1708 Add_Before);
1710 else
1712 (Cmd.Params,
1713 null,
1714 Add_Before);
1715 end if;
1717 if Section = "" then
1719 (Cmd.Sections,
1720 null,
1721 Add_Before);
1722 else
1724 (Cmd.Sections,
1725 new String'(Section),
1726 Add_Before);
1727 end if;
1728 end if;
1729 end Add_Simple_Switch;
1731 procedure Add_Simple_Switches is
1732 new For_Each_Simple_Switch (Add_Simple_Switch);
1734 -- Start of processing for Add_Switch
1736 begin
1737 Success := False;
1738 Add_Simple_Switches (Cmd, Switch, Parameter);
1739 Free (Cmd.Coalesce);
1740 end Add_Switch;
1742 ------------
1743 -- Remove --
1744 ------------
1746 procedure Remove (Line : in out Argument_List_Access; Index : Integer) is
1747 Tmp : Argument_List_Access := Line;
1749 begin
1750 Line := new Argument_List (Tmp'First .. Tmp'Last - 1);
1752 if Index /= Tmp'First then
1753 Line (Tmp'First .. Index - 1) := Tmp (Tmp'First .. Index - 1);
1754 end if;
1756 Free (Tmp (Index));
1758 if Index /= Tmp'Last then
1759 Line (Index .. Tmp'Last - 1) := Tmp (Index + 1 .. Tmp'Last);
1760 end if;
1762 Unchecked_Free (Tmp);
1763 end Remove;
1765 ---------
1766 -- Add --
1767 ---------
1769 procedure Add
1770 (Line : in out Argument_List_Access;
1771 Str : String_Access;
1772 Before : Boolean := False)
1774 Tmp : Argument_List_Access := Line;
1776 begin
1777 if Tmp /= null then
1778 Line := new Argument_List (Tmp'First .. Tmp'Last + 1);
1780 if Before then
1781 Line (Tmp'First) := Str;
1782 Line (Tmp'First + 1 .. Tmp'Last + 1) := Tmp.all;
1783 else
1784 Line (Tmp'Range) := Tmp.all;
1785 Line (Tmp'Last + 1) := Str;
1786 end if;
1788 Unchecked_Free (Tmp);
1790 else
1791 Line := new Argument_List'(1 .. 1 => Str);
1792 end if;
1793 end Add;
1795 -------------------
1796 -- Remove_Switch --
1797 -------------------
1799 procedure Remove_Switch
1800 (Cmd : in out Command_Line;
1801 Switch : String;
1802 Remove_All : Boolean := False;
1803 Has_Parameter : Boolean := False;
1804 Section : String := "")
1806 Success : Boolean;
1807 pragma Unreferenced (Success);
1808 begin
1809 Remove_Switch (Cmd, Switch, Remove_All, Has_Parameter, Section, Success);
1810 end Remove_Switch;
1812 -------------------
1813 -- Remove_Switch --
1814 -------------------
1816 procedure Remove_Switch
1817 (Cmd : in out Command_Line;
1818 Switch : String;
1819 Remove_All : Boolean := False;
1820 Has_Parameter : Boolean := False;
1821 Section : String := "";
1822 Success : out Boolean)
1824 procedure Remove_Simple_Switch (Simple : String; Param : String);
1825 -- Removes a simple switch, with no aliasing or grouping
1827 --------------------------
1828 -- Remove_Simple_Switch --
1829 --------------------------
1831 procedure Remove_Simple_Switch (Simple : String; Param : String) is
1832 C : Integer;
1833 pragma Unreferenced (Param);
1835 begin
1836 if Cmd.Expanded /= null then
1837 C := Cmd.Expanded'First;
1838 while C <= Cmd.Expanded'Last loop
1839 if Cmd.Expanded (C).all = Simple
1840 and then
1841 (Remove_All
1842 or else (Cmd.Sections (C) = null
1843 and then Section = "")
1844 or else (Cmd.Sections (C) /= null
1845 and then Section = Cmd.Sections (C).all))
1846 and then (not Has_Parameter or else Cmd.Params (C) /= null)
1847 then
1848 Remove (Cmd.Expanded, C);
1849 Remove (Cmd.Params, C);
1850 Remove (Cmd.Sections, C);
1851 Success := True;
1853 if not Remove_All then
1854 return;
1855 end if;
1857 else
1858 C := C + 1;
1859 end if;
1860 end loop;
1861 end if;
1862 end Remove_Simple_Switch;
1864 procedure Remove_Simple_Switches is
1865 new For_Each_Simple_Switch (Remove_Simple_Switch);
1867 -- Start of processing for Remove_Switch
1869 begin
1870 Success := False;
1871 Remove_Simple_Switches (Cmd, Switch, "", Unalias => not Has_Parameter);
1872 Free (Cmd.Coalesce);
1873 end Remove_Switch;
1875 -------------------
1876 -- Remove_Switch --
1877 -------------------
1879 procedure Remove_Switch
1880 (Cmd : in out Command_Line;
1881 Switch : String;
1882 Parameter : String;
1883 Section : String := "")
1885 procedure Remove_Simple_Switch (Simple : String; Param : String);
1886 -- Removes a simple switch, with no aliasing or grouping
1888 --------------------------
1889 -- Remove_Simple_Switch --
1890 --------------------------
1892 procedure Remove_Simple_Switch (Simple : String; Param : String) is
1893 C : Integer;
1895 begin
1896 if Cmd.Expanded /= null then
1897 C := Cmd.Expanded'First;
1898 while C <= Cmd.Expanded'Last loop
1899 if Cmd.Expanded (C).all = Simple
1900 and then
1901 ((Cmd.Sections (C) = null
1902 and then Section = "")
1903 or else
1904 (Cmd.Sections (C) /= null
1905 and then Section = Cmd.Sections (C).all))
1906 and then
1907 ((Cmd.Params (C) = null and then Param = "")
1908 or else
1909 (Cmd.Params (C) /= null
1910 and then
1912 -- Ignore the separator stored in Parameter
1914 Cmd.Params (C) (Cmd.Params (C)'First + 1
1915 .. Cmd.Params (C)'Last) =
1916 Param))
1917 then
1918 Remove (Cmd.Expanded, C);
1919 Remove (Cmd.Params, C);
1920 Remove (Cmd.Sections, C);
1922 -- The switch is necessarily unique by construction of
1923 -- Add_Switch.
1925 return;
1927 else
1928 C := C + 1;
1929 end if;
1930 end loop;
1931 end if;
1932 end Remove_Simple_Switch;
1934 procedure Remove_Simple_Switches is
1935 new For_Each_Simple_Switch (Remove_Simple_Switch);
1937 -- Start of processing for Remove_Switch
1939 begin
1940 Remove_Simple_Switches (Cmd, Switch, Parameter);
1941 Free (Cmd.Coalesce);
1942 end Remove_Switch;
1944 --------------------
1945 -- Group_Switches --
1946 --------------------
1948 procedure Group_Switches
1949 (Cmd : Command_Line;
1950 Result : Argument_List_Access;
1951 Sections : Argument_List_Access;
1952 Params : Argument_List_Access)
1954 function Compatible_Parameter (Param : String_Access) return Boolean;
1955 -- True when the parameter can be part of a group
1957 --------------------------
1958 -- Compatible_Parameter --
1959 --------------------------
1961 function Compatible_Parameter (Param : String_Access) return Boolean is
1962 begin
1963 -- No parameter OK
1965 if Param = null then
1966 return True;
1968 -- We need parameters without separators
1970 elsif Param (Param'First) /= ASCII.NUL then
1971 return False;
1973 -- Parameters must be all digits
1975 else
1976 for J in Param'First + 1 .. Param'Last loop
1977 if Param (J) not in '0' .. '9' then
1978 return False;
1979 end if;
1980 end loop;
1982 return True;
1983 end if;
1984 end Compatible_Parameter;
1986 -- Local declarations
1988 Group : Ada.Strings.Unbounded.Unbounded_String;
1989 First : Natural;
1990 use type Ada.Strings.Unbounded.Unbounded_String;
1992 -- Start of processing for Group_Switches
1994 begin
1995 if Cmd.Config = null
1996 or else Cmd.Config.Prefixes = null
1997 then
1998 return;
1999 end if;
2001 for P in Cmd.Config.Prefixes'Range loop
2002 Group := Ada.Strings.Unbounded.Null_Unbounded_String;
2003 First := 0;
2005 for C in Result'Range loop
2006 if Result (C) /= null
2007 and then Compatible_Parameter (Params (C))
2008 and then Looking_At
2009 (Result (C).all, Result (C)'First, Cmd.Config.Prefixes (P).all)
2010 then
2011 -- If we are still in the same section, group the switches
2013 if First = 0
2014 or else
2015 (Sections (C) = null
2016 and then Sections (First) = null)
2017 or else
2018 (Sections (C) /= null
2019 and then Sections (First) /= null
2020 and then Sections (C).all = Sections (First).all)
2021 then
2022 Group :=
2023 Group &
2024 Result (C)
2025 (Result (C)'First + Cmd.Config.Prefixes (P)'Length ..
2026 Result (C)'Last);
2028 if Params (C) /= null then
2029 Group :=
2030 Group &
2031 Params (C) (Params (C)'First + 1 .. Params (C)'Last);
2032 Free (Params (C));
2033 end if;
2035 if First = 0 then
2036 First := C;
2037 end if;
2039 Free (Result (C));
2041 else
2042 -- We changed section: we put the grouped switches to the
2043 -- first place, on continue with the new section.
2045 Result (First) :=
2046 new String'
2047 (Cmd.Config.Prefixes (P).all &
2048 Ada.Strings.Unbounded.To_String (Group));
2049 Group :=
2050 Ada.Strings.Unbounded.To_Unbounded_String
2051 (Result (C)
2052 (Result (C)'First + Cmd.Config.Prefixes (P)'Length ..
2053 Result (C)'Last));
2054 First := C;
2055 end if;
2056 end if;
2057 end loop;
2059 if First > 0 then
2060 Result (First) :=
2061 new String'
2062 (Cmd.Config.Prefixes (P).all &
2063 Ada.Strings.Unbounded.To_String (Group));
2064 end if;
2065 end loop;
2066 end Group_Switches;
2068 --------------------
2069 -- Alias_Switches --
2070 --------------------
2072 procedure Alias_Switches
2073 (Cmd : Command_Line;
2074 Result : Argument_List_Access;
2075 Params : Argument_List_Access)
2077 Found : Boolean;
2078 First : Natural;
2080 procedure Check_Cb (Switch : String; Param : String);
2081 -- Comment required ???
2083 procedure Remove_Cb (Switch : String; Param : String);
2084 -- Comment required ???
2086 --------------
2087 -- Check_Cb --
2088 --------------
2090 procedure Check_Cb (Switch : String; Param : String) is
2091 begin
2092 if Found then
2093 for E in Result'Range loop
2094 if Result (E) /= null
2095 and then
2096 (Params (E) = null
2097 or else Params (E) (Params (E)'First + 1
2098 .. Params (E)'Last) = Param)
2099 and then Result (E).all = Switch
2100 then
2101 return;
2102 end if;
2103 end loop;
2105 Found := False;
2106 end if;
2107 end Check_Cb;
2109 ---------------
2110 -- Remove_Cb --
2111 ---------------
2113 procedure Remove_Cb (Switch : String; Param : String) is
2114 begin
2115 for E in Result'Range loop
2116 if Result (E) /= null
2117 and then
2118 (Params (E) = null
2119 or else Params (E) (Params (E)'First + 1
2120 .. Params (E)'Last) = Param)
2121 and then Result (E).all = Switch
2122 then
2123 if First > E then
2124 First := E;
2125 end if;
2126 Free (Result (E));
2127 Free (Params (E));
2128 return;
2129 end if;
2130 end loop;
2131 end Remove_Cb;
2133 procedure Check_All is new For_Each_Simple_Switch (Check_Cb);
2134 procedure Remove_All is new For_Each_Simple_Switch (Remove_Cb);
2136 -- Start of processing for Alias_Switches
2138 begin
2139 if Cmd.Config = null
2140 or else Cmd.Config.Aliases = null
2141 then
2142 return;
2143 end if;
2145 for A in Cmd.Config.Aliases'Range loop
2147 -- Compute the various simple switches that make up the alias. We
2148 -- split the expansion into as many simple switches as possible, and
2149 -- then check whether the expanded command line has all of them.
2151 Found := True;
2152 Check_All (Cmd, Cmd.Config.Expansions (A).all);
2154 if Found then
2155 First := Integer'Last;
2156 Remove_All (Cmd, Cmd.Config.Expansions (A).all);
2157 Result (First) := new String'(Cmd.Config.Aliases (A).all);
2158 end if;
2159 end loop;
2160 end Alias_Switches;
2162 -------------------
2163 -- Sort_Sections --
2164 -------------------
2166 procedure Sort_Sections
2167 (Line : GNAT.OS_Lib.Argument_List_Access;
2168 Sections : GNAT.OS_Lib.Argument_List_Access;
2169 Params : GNAT.OS_Lib.Argument_List_Access)
2171 Sections_List : Argument_List_Access :=
2172 new Argument_List'(1 .. 1 => null);
2173 Found : Boolean;
2174 Old_Line : constant Argument_List := Line.all;
2175 Old_Sections : constant Argument_List := Sections.all;
2176 Old_Params : constant Argument_List := Params.all;
2177 Index : Natural;
2179 begin
2180 if Line = null then
2181 return;
2182 end if;
2184 -- First construct a list of all sections
2186 for E in Line'Range loop
2187 if Sections (E) /= null then
2188 Found := False;
2189 for S in Sections_List'Range loop
2190 if (Sections_List (S) = null and then Sections (E) = null)
2191 or else
2192 (Sections_List (S) /= null
2193 and then Sections (E) /= null
2194 and then Sections_List (S).all = Sections (E).all)
2195 then
2196 Found := True;
2197 exit;
2198 end if;
2199 end loop;
2201 if not Found then
2202 Add (Sections_List, Sections (E));
2203 end if;
2204 end if;
2205 end loop;
2207 Index := Line'First;
2209 for S in Sections_List'Range loop
2210 for E in Old_Line'Range loop
2211 if (Sections_List (S) = null and then Old_Sections (E) = null)
2212 or else
2213 (Sections_List (S) /= null
2214 and then Old_Sections (E) /= null
2215 and then Sections_List (S).all = Old_Sections (E).all)
2216 then
2217 Line (Index) := Old_Line (E);
2218 Sections (Index) := Old_Sections (E);
2219 Params (Index) := Old_Params (E);
2220 Index := Index + 1;
2221 end if;
2222 end loop;
2223 end loop;
2224 end Sort_Sections;
2226 -----------
2227 -- Start --
2228 -----------
2230 procedure Start
2231 (Cmd : in out Command_Line;
2232 Iter : in out Command_Line_Iterator;
2233 Expanded : Boolean)
2235 begin
2236 if Cmd.Expanded = null then
2237 Iter.List := null;
2238 return;
2239 end if;
2241 -- Reorder the expanded line so that sections are grouped
2243 Sort_Sections (Cmd.Expanded, Cmd.Sections, Cmd.Params);
2245 -- Coalesce the switches as much as possible
2247 if not Expanded
2248 and then Cmd.Coalesce = null
2249 then
2250 Cmd.Coalesce := new Argument_List (Cmd.Expanded'Range);
2251 for E in Cmd.Expanded'Range loop
2252 Cmd.Coalesce (E) := new String'(Cmd.Expanded (E).all);
2253 end loop;
2255 Cmd.Coalesce_Sections := new Argument_List (Cmd.Sections'Range);
2256 for E in Cmd.Sections'Range loop
2257 if Cmd.Sections (E) = null then
2258 Cmd.Coalesce_Sections (E) := null;
2259 else
2260 Cmd.Coalesce_Sections (E) := new String'(Cmd.Sections (E).all);
2261 end if;
2262 end loop;
2264 Cmd.Coalesce_Params := new Argument_List (Cmd.Params'Range);
2265 for E in Cmd.Params'Range loop
2266 if Cmd.Params (E) = null then
2267 Cmd.Coalesce_Params (E) := null;
2268 else
2269 Cmd.Coalesce_Params (E) := new String'(Cmd.Params (E).all);
2270 end if;
2271 end loop;
2273 -- Not a clone, since we will not modify the parameters anyway
2275 Alias_Switches (Cmd, Cmd.Coalesce, Cmd.Coalesce_Params);
2276 Group_Switches
2277 (Cmd, Cmd.Coalesce, Cmd.Coalesce_Sections, Cmd.Coalesce_Params);
2278 end if;
2280 if Expanded then
2281 Iter.List := Cmd.Expanded;
2282 Iter.Params := Cmd.Params;
2283 Iter.Sections := Cmd.Sections;
2284 else
2285 Iter.List := Cmd.Coalesce;
2286 Iter.Params := Cmd.Coalesce_Params;
2287 Iter.Sections := Cmd.Coalesce_Sections;
2288 end if;
2290 if Iter.List = null then
2291 Iter.Current := Integer'Last;
2292 else
2293 Iter.Current := Iter.List'First;
2295 while Iter.Current <= Iter.List'Last
2296 and then Iter.List (Iter.Current) = null
2297 loop
2298 Iter.Current := Iter.Current + 1;
2299 end loop;
2300 end if;
2301 end Start;
2303 --------------------
2304 -- Current_Switch --
2305 --------------------
2307 function Current_Switch (Iter : Command_Line_Iterator) return String is
2308 begin
2309 return Iter.List (Iter.Current).all;
2310 end Current_Switch;
2312 --------------------
2313 -- Is_New_Section --
2314 --------------------
2316 function Is_New_Section (Iter : Command_Line_Iterator) return Boolean is
2317 Section : constant String := Current_Section (Iter);
2318 begin
2319 if Iter.Sections = null then
2320 return False;
2321 elsif Iter.Current = Iter.Sections'First
2322 or else Iter.Sections (Iter.Current - 1) = null
2323 then
2324 return Section /= "";
2325 end if;
2327 return Section /= Iter.Sections (Iter.Current - 1).all;
2328 end Is_New_Section;
2330 ---------------------
2331 -- Current_Section --
2332 ---------------------
2334 function Current_Section (Iter : Command_Line_Iterator) return String is
2335 begin
2336 if Iter.Sections = null
2337 or else Iter.Current > Iter.Sections'Last
2338 or else Iter.Sections (Iter.Current) = null
2339 then
2340 return "";
2341 end if;
2343 return Iter.Sections (Iter.Current).all;
2344 end Current_Section;
2346 -----------------------
2347 -- Current_Separator --
2348 -----------------------
2350 function Current_Separator (Iter : Command_Line_Iterator) return String is
2351 begin
2352 if Iter.Params = null
2353 or else Iter.Current > Iter.Params'Last
2354 or else Iter.Params (Iter.Current) = null
2355 then
2356 return "";
2358 else
2359 declare
2360 Sep : constant Character :=
2361 Iter.Params (Iter.Current) (Iter.Params (Iter.Current)'First);
2362 begin
2363 if Sep = ASCII.NUL then
2364 return "";
2365 else
2366 return "" & Sep;
2367 end if;
2368 end;
2369 end if;
2370 end Current_Separator;
2372 -----------------------
2373 -- Current_Parameter --
2374 -----------------------
2376 function Current_Parameter (Iter : Command_Line_Iterator) return String is
2377 begin
2378 if Iter.Params = null
2379 or else Iter.Current > Iter.Params'Last
2380 or else Iter.Params (Iter.Current) = null
2381 then
2382 return "";
2384 else
2385 declare
2386 P : constant String := Iter.Params (Iter.Current).all;
2388 begin
2389 -- Skip separator
2391 return P (P'First + 1 .. P'Last);
2392 end;
2393 end if;
2394 end Current_Parameter;
2396 --------------
2397 -- Has_More --
2398 --------------
2400 function Has_More (Iter : Command_Line_Iterator) return Boolean is
2401 begin
2402 return Iter.List /= null and then Iter.Current <= Iter.List'Last;
2403 end Has_More;
2405 ----------
2406 -- Next --
2407 ----------
2409 procedure Next (Iter : in out Command_Line_Iterator) is
2410 begin
2411 Iter.Current := Iter.Current + 1;
2412 while Iter.Current <= Iter.List'Last
2413 and then Iter.List (Iter.Current) = null
2414 loop
2415 Iter.Current := Iter.Current + 1;
2416 end loop;
2417 end Next;
2419 ----------
2420 -- Free --
2421 ----------
2423 procedure Free (Config : in out Command_Line_Configuration) is
2424 begin
2425 if Config /= null then
2426 Free (Config.Aliases);
2427 Free (Config.Expansions);
2428 Free (Config.Prefixes);
2429 Unchecked_Free (Config);
2430 end if;
2431 end Free;
2433 ----------
2434 -- Free --
2435 ----------
2437 procedure Free (Cmd : in out Command_Line) is
2438 begin
2439 Free (Cmd.Expanded);
2440 Free (Cmd.Coalesce);
2441 Free (Cmd.Params);
2442 end Free;
2444 end GNAT.Command_Line;