* config/rs6000/rs6000.c (rs6000_option_override_internal): Do not
[official-gcc.git] / gcc / ada / g-comlin.adb
blob723ff120ff6ee6d7911590e4ee6e96250a02a7ec
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-2012, 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 package CL renames Ada.Command_Line;
44 type Switch_Parameter_Type is
45 (Parameter_None,
46 Parameter_With_Optional_Space, -- ':' in getopt
47 Parameter_With_Space_Or_Equal, -- '=' in getopt
48 Parameter_No_Space, -- '!' in getopt
49 Parameter_Optional); -- '?' in getopt
51 procedure Set_Parameter
52 (Variable : out Parameter_Type;
53 Arg_Num : Positive;
54 First : Positive;
55 Last : Positive;
56 Extra : Character := ASCII.NUL);
57 pragma Inline (Set_Parameter);
58 -- Set the parameter that will be returned by Parameter below
59 -- Parameters need to be defined ???
61 function Goto_Next_Argument_In_Section (Parser : Opt_Parser) return Boolean;
62 -- Go to the next argument on the command line. If we are at the end of
63 -- the current section, we want to make sure there is no other identical
64 -- section on the command line (there might be multiple instances of
65 -- -largs). Returns True iff there is another argument.
67 function Get_File_Names_Case_Sensitive return Integer;
68 pragma Import (C, Get_File_Names_Case_Sensitive,
69 "__gnat_get_file_names_case_sensitive");
71 File_Names_Case_Sensitive : constant Boolean :=
72 Get_File_Names_Case_Sensitive /= 0;
74 procedure Canonical_Case_File_Name (S : in out String);
75 -- Given a file name, converts it to canonical case form. For systems where
76 -- file names are case sensitive, this procedure has no effect. If file
77 -- names are not case sensitive (i.e. for example if you have the file
78 -- "xyz.adb", you can refer to it as XYZ.adb or XyZ.AdB), then this call
79 -- converts the given string to canonical all lower case form, so that two
80 -- file names compare equal if they refer to the same file.
82 procedure Internal_Initialize_Option_Scan
83 (Parser : Opt_Parser;
84 Switch_Char : Character;
85 Stop_At_First_Non_Switch : Boolean;
86 Section_Delimiters : String);
87 -- Initialize Parser, which must have been allocated already
89 function Argument (Parser : Opt_Parser; Index : Integer) return String;
90 -- Return the index-th command line argument
92 procedure Find_Longest_Matching_Switch
93 (Switches : String;
94 Arg : String;
95 Index_In_Switches : out Integer;
96 Switch_Length : out Integer;
97 Param : out Switch_Parameter_Type);
98 -- Return the Longest switch from Switches that at least partially
99 -- partially Arg. Index_In_Switches is set to 0 if none matches.
100 -- What are other parameters??? in particular Param is not always set???
102 procedure Unchecked_Free is new Ada.Unchecked_Deallocation
103 (Argument_List, Argument_List_Access);
105 procedure Unchecked_Free is new Ada.Unchecked_Deallocation
106 (Command_Line_Configuration_Record, Command_Line_Configuration);
108 procedure Remove (Line : in out Argument_List_Access; Index : Integer);
109 -- Remove a specific element from Line
111 procedure Add
112 (Line : in out Argument_List_Access;
113 Str : String_Access;
114 Before : Boolean := False);
115 -- Add a new element to Line. If Before is True, the item is inserted at
116 -- the beginning, else it is appended.
118 procedure Add
119 (Config : in out Command_Line_Configuration;
120 Switch : Switch_Definition);
121 procedure Add
122 (Def : in out Alias_Definitions_List;
123 Alias : Alias_Definition);
124 -- Add a new element to Def
126 procedure Initialize_Switch_Def
127 (Def : out Switch_Definition;
128 Switch : String := "";
129 Long_Switch : String := "";
130 Help : String := "";
131 Section : String := "";
132 Argument : String := "ARG");
133 -- Initialize [Def] with the contents of the other parameters.
134 -- This also checks consistency of the switch parameters, and will raise
135 -- Invalid_Switch if they do not match.
137 procedure Decompose_Switch
138 (Switch : String;
139 Parameter_Type : out Switch_Parameter_Type;
140 Switch_Last : out Integer);
141 -- Given a switch definition ("name:" for instance), extracts the type of
142 -- parameter that is expected, and the name of the switch
144 function Can_Have_Parameter (S : String) return Boolean;
145 -- True if S can have a parameter
147 function Require_Parameter (S : String) return Boolean;
148 -- True if S requires a parameter
150 function Actual_Switch (S : String) return String;
151 -- Remove any possible trailing '!', ':', '?' and '='
153 generic
154 with procedure Callback
155 (Simple_Switch : String;
156 Separator : String;
157 Parameter : String;
158 Index : Integer); -- Index in Config.Switches, or -1
159 procedure For_Each_Simple_Switch
160 (Config : Command_Line_Configuration;
161 Section : String;
162 Switch : String;
163 Parameter : String := "";
164 Unalias : Boolean := True);
165 -- Breaks Switch into as simple switches as possible (expanding aliases and
166 -- ungrouping common prefixes when possible), and call Callback for each of
167 -- these.
169 procedure Sort_Sections
170 (Line : GNAT.OS_Lib.Argument_List_Access;
171 Sections : GNAT.OS_Lib.Argument_List_Access;
172 Params : GNAT.OS_Lib.Argument_List_Access);
173 -- Reorder the command line switches so that the switches belonging to a
174 -- section are grouped together.
176 procedure Group_Switches
177 (Cmd : Command_Line;
178 Result : Argument_List_Access;
179 Sections : Argument_List_Access;
180 Params : Argument_List_Access);
181 -- Group switches with common prefixes whenever possible. Once they have
182 -- been grouped, we also check items for possible aliasing.
184 procedure Alias_Switches
185 (Cmd : Command_Line;
186 Result : Argument_List_Access;
187 Params : Argument_List_Access);
188 -- When possible, replace one or more switches by an alias, i.e. a shorter
189 -- version.
191 function Looking_At
192 (Type_Str : String;
193 Index : Natural;
194 Substring : String) return Boolean;
195 -- Return True if the characters starting at Index in Type_Str are
196 -- equivalent to Substring.
198 generic
199 with function Callback (S : String; Index : Integer) return Boolean;
200 procedure Foreach_Switch
201 (Config : Command_Line_Configuration;
202 Section : String);
203 -- Iterate over all switches defined in Config, for a specific section.
204 -- Index is set to the index in Config.Switches. Stop iterating when
205 -- Callback returns False.
207 --------------
208 -- Argument --
209 --------------
211 function Argument (Parser : Opt_Parser; Index : Integer) return String is
212 begin
213 if Parser.Arguments /= null then
214 return Parser.Arguments (Index + Parser.Arguments'First - 1).all;
215 else
216 return CL.Argument (Index);
217 end if;
218 end Argument;
220 ------------------------------
221 -- Canonical_Case_File_Name --
222 ------------------------------
224 procedure Canonical_Case_File_Name (S : in out String) is
225 begin
226 if not File_Names_Case_Sensitive then
227 for J in S'Range loop
228 if S (J) in 'A' .. 'Z' then
229 S (J) := Character'Val
230 (Character'Pos (S (J)) +
231 (Character'Pos ('a') - Character'Pos ('A')));
232 end if;
233 end loop;
234 end if;
235 end Canonical_Case_File_Name;
237 ---------------
238 -- Expansion --
239 ---------------
241 function Expansion (Iterator : Expansion_Iterator) return String is
242 type Pointer is access all Expansion_Iterator;
244 It : constant Pointer := Iterator'Unrestricted_Access;
245 S : String (1 .. 1024);
246 Last : Natural;
248 Current : Depth := It.Current_Depth;
249 NL : Positive;
251 begin
252 -- It is assumed that a directory is opened at the current level.
253 -- Otherwise GNAT.Directory_Operations.Directory_Error will be raised
254 -- at the first call to Read.
256 loop
257 Read (It.Levels (Current).Dir, S, Last);
259 -- If we have exhausted the directory, close it and go back one level
261 if Last = 0 then
262 Close (It.Levels (Current).Dir);
264 -- If we are at level 1, we are finished; return an empty string
266 if Current = 1 then
267 return String'(1 .. 0 => ' ');
269 -- Otherwise continue with the directory at the previous level
271 else
272 Current := Current - 1;
273 It.Current_Depth := Current;
274 end if;
276 -- If this is a directory, that is neither "." or "..", attempt to
277 -- go to the next level.
279 elsif Is_Directory
280 (It.Dir_Name (1 .. It.Levels (Current).Name_Last) &
281 S (1 .. Last))
282 and then S (1 .. Last) /= "."
283 and then S (1 .. Last) /= ".."
284 then
285 -- We can go to the next level only if we have not reached the
286 -- maximum depth,
288 if Current < It.Maximum_Depth then
289 NL := It.Levels (Current).Name_Last;
291 -- And if relative path of this new directory is not too long
293 if NL + Last + 1 < Max_Path_Length then
294 Current := Current + 1;
295 It.Current_Depth := Current;
296 It.Dir_Name (NL + 1 .. NL + Last) := S (1 .. Last);
297 NL := NL + Last + 1;
298 It.Dir_Name (NL) := Directory_Separator;
299 It.Levels (Current).Name_Last := NL;
300 Canonical_Case_File_Name (It.Dir_Name (1 .. NL));
302 -- Open the new directory, and read from it
304 GNAT.Directory_Operations.Open
305 (It.Levels (Current).Dir, It.Dir_Name (1 .. NL));
306 end if;
307 end if;
308 end if;
310 -- Check the relative path against the pattern
312 -- Note that we try to match also against directory names, since
313 -- clients of this function may expect to retrieve directories.
315 declare
316 Name : String :=
317 It.Dir_Name (It.Start .. It.Levels (Current).Name_Last)
318 & S (1 .. Last);
320 begin
321 Canonical_Case_File_Name (Name);
323 -- If it matches return the relative path
325 if GNAT.Regexp.Match (Name, Iterator.Regexp) then
326 return Name;
327 end if;
328 end;
329 end loop;
330 end Expansion;
332 ---------------------
333 -- Current_Section --
334 ---------------------
336 function Current_Section
337 (Parser : Opt_Parser := Command_Line_Parser) return String
339 begin
340 if Parser.Current_Section = 1 then
341 return "";
342 end if;
344 for Index in reverse 1 .. Integer'Min (Parser.Current_Argument - 1,
345 Parser.Section'Last)
346 loop
347 if Parser.Section (Index) = 0 then
348 return Argument (Parser, Index);
349 end if;
350 end loop;
352 return "";
353 end Current_Section;
355 -----------------
356 -- Full_Switch --
357 -----------------
359 function Full_Switch
360 (Parser : Opt_Parser := Command_Line_Parser) return String
362 begin
363 if Parser.The_Switch.Extra = ASCII.NUL then
364 return Argument (Parser, Parser.The_Switch.Arg_Num)
365 (Parser.The_Switch.First .. Parser.The_Switch.Last);
366 else
367 return Parser.The_Switch.Extra
368 & Argument (Parser, Parser.The_Switch.Arg_Num)
369 (Parser.The_Switch.First .. Parser.The_Switch.Last);
370 end if;
371 end Full_Switch;
373 ------------------
374 -- Get_Argument --
375 ------------------
377 function Get_Argument
378 (Do_Expansion : Boolean := False;
379 Parser : Opt_Parser := Command_Line_Parser) return String
381 begin
382 if Parser.In_Expansion then
383 declare
384 S : constant String := Expansion (Parser.Expansion_It);
385 begin
386 if S'Length /= 0 then
387 return S;
388 else
389 Parser.In_Expansion := False;
390 end if;
391 end;
392 end if;
394 if Parser.Current_Argument > Parser.Arg_Count then
396 -- If this is the first time this function is called
398 if Parser.Current_Index = 1 then
399 Parser.Current_Argument := 1;
400 while Parser.Current_Argument <= Parser.Arg_Count
401 and then Parser.Section (Parser.Current_Argument) /=
402 Parser.Current_Section
403 loop
404 Parser.Current_Argument := Parser.Current_Argument + 1;
405 end loop;
407 else
408 return String'(1 .. 0 => ' ');
409 end if;
411 elsif Parser.Section (Parser.Current_Argument) = 0 then
412 while Parser.Current_Argument <= Parser.Arg_Count
413 and then Parser.Section (Parser.Current_Argument) /=
414 Parser.Current_Section
415 loop
416 Parser.Current_Argument := Parser.Current_Argument + 1;
417 end loop;
418 end if;
420 Parser.Current_Index := Integer'Last;
422 while Parser.Current_Argument <= Parser.Arg_Count
423 and then Parser.Is_Switch (Parser.Current_Argument)
424 loop
425 Parser.Current_Argument := Parser.Current_Argument + 1;
426 end loop;
428 if Parser.Current_Argument > Parser.Arg_Count then
429 return String'(1 .. 0 => ' ');
430 elsif Parser.Section (Parser.Current_Argument) = 0 then
431 return Get_Argument (Do_Expansion);
432 end if;
434 Parser.Current_Argument := Parser.Current_Argument + 1;
436 -- Could it be a file name with wild cards to expand?
438 if Do_Expansion then
439 declare
440 Arg : constant String :=
441 Argument (Parser, Parser.Current_Argument - 1);
442 Index : Positive;
444 begin
445 Index := Arg'First;
446 while Index <= Arg'Last loop
447 if Arg (Index) = '*'
448 or else Arg (Index) = '?'
449 or else Arg (Index) = '['
450 then
451 Parser.In_Expansion := True;
452 Start_Expansion (Parser.Expansion_It, Arg);
453 return Get_Argument (Do_Expansion);
454 end if;
456 Index := Index + 1;
457 end loop;
458 end;
459 end if;
461 return Argument (Parser, Parser.Current_Argument - 1);
462 end Get_Argument;
464 ----------------------
465 -- Decompose_Switch --
466 ----------------------
468 procedure Decompose_Switch
469 (Switch : String;
470 Parameter_Type : out Switch_Parameter_Type;
471 Switch_Last : out Integer)
473 begin
474 if Switch = "" then
475 Parameter_Type := Parameter_None;
476 Switch_Last := Switch'Last;
477 return;
478 end if;
480 case Switch (Switch'Last) is
481 when ':' =>
482 Parameter_Type := Parameter_With_Optional_Space;
483 Switch_Last := Switch'Last - 1;
484 when '=' =>
485 Parameter_Type := Parameter_With_Space_Or_Equal;
486 Switch_Last := Switch'Last - 1;
487 when '!' =>
488 Parameter_Type := Parameter_No_Space;
489 Switch_Last := Switch'Last - 1;
490 when '?' =>
491 Parameter_Type := Parameter_Optional;
492 Switch_Last := Switch'Last - 1;
493 when others =>
494 Parameter_Type := Parameter_None;
495 Switch_Last := Switch'Last;
496 end case;
497 end Decompose_Switch;
499 ----------------------------------
500 -- Find_Longest_Matching_Switch --
501 ----------------------------------
503 procedure Find_Longest_Matching_Switch
504 (Switches : String;
505 Arg : String;
506 Index_In_Switches : out Integer;
507 Switch_Length : out Integer;
508 Param : out Switch_Parameter_Type)
510 Index : Natural;
511 Length : Natural := 1;
512 Last : Natural;
513 P : Switch_Parameter_Type;
515 begin
516 Index_In_Switches := 0;
517 Switch_Length := 0;
519 -- Remove all leading spaces first to make sure that Index points
520 -- at the start of the first switch.
522 Index := Switches'First;
523 while Index <= Switches'Last and then Switches (Index) = ' ' loop
524 Index := Index + 1;
525 end loop;
527 while Index <= Switches'Last loop
529 -- Search the length of the parameter at this position in Switches
531 Length := Index;
532 while Length <= Switches'Last
533 and then Switches (Length) /= ' '
534 loop
535 Length := Length + 1;
536 end loop;
538 -- Length now marks the separator after the current switch. Last will
539 -- mark the last character of the name of the switch.
541 if Length = Index + 1 then
542 P := Parameter_None;
543 Last := Index;
544 else
545 Decompose_Switch (Switches (Index .. Length - 1), P, Last);
546 end if;
548 -- If it is the one we searched, it may be a candidate
550 if Arg'First + Last - Index <= Arg'Last
551 and then Switches (Index .. Last) =
552 Arg (Arg'First .. Arg'First + Last - Index)
553 and then Last - Index + 1 > Switch_Length
554 then
555 Param := P;
556 Index_In_Switches := Index;
557 Switch_Length := Last - Index + 1;
558 end if;
560 -- Look for the next switch in Switches
562 while Index <= Switches'Last
563 and then Switches (Index) /= ' '
564 loop
565 Index := Index + 1;
566 end loop;
568 Index := Index + 1;
569 end loop;
570 end Find_Longest_Matching_Switch;
572 ------------
573 -- Getopt --
574 ------------
576 function Getopt
577 (Switches : String;
578 Concatenate : Boolean := True;
579 Parser : Opt_Parser := Command_Line_Parser) return Character
581 Dummy : Boolean;
582 pragma Unreferenced (Dummy);
584 begin
585 <<Restart>>
587 -- If we have finished parsing the current command line item (there
588 -- might be multiple switches in a single item), then go to the next
589 -- element.
591 if Parser.Current_Argument > Parser.Arg_Count
592 or else (Parser.Current_Index >
593 Argument (Parser, Parser.Current_Argument)'Last
594 and then not Goto_Next_Argument_In_Section (Parser))
595 then
596 return ASCII.NUL;
597 end if;
599 -- By default, the switch will not have a parameter
601 Parser.The_Parameter :=
602 (Integer'Last, Integer'Last, Integer'Last - 1, ASCII.NUL);
603 Parser.The_Separator := ASCII.NUL;
605 declare
606 Arg : constant String :=
607 Argument (Parser, Parser.Current_Argument);
608 Index_Switches : Natural := 0;
609 Max_Length : Natural := 0;
610 End_Index : Natural;
611 Param : Switch_Parameter_Type;
612 begin
613 -- If we are on a new item, test if this might be a switch
615 if Parser.Current_Index = Arg'First then
616 if Arg (Arg'First) /= Parser.Switch_Character then
618 -- If it isn't a switch, return it immediately. We also know it
619 -- isn't the parameter to a previous switch, since that has
620 -- already been handled.
622 if Switches (Switches'First) = '*' then
623 Set_Parameter
624 (Parser.The_Switch,
625 Arg_Num => Parser.Current_Argument,
626 First => Arg'First,
627 Last => Arg'Last);
628 Parser.Is_Switch (Parser.Current_Argument) := True;
629 Dummy := Goto_Next_Argument_In_Section (Parser);
630 return '*';
631 end if;
633 if Parser.Stop_At_First then
634 Parser.Current_Argument := Positive'Last;
635 return ASCII.NUL;
637 elsif not Goto_Next_Argument_In_Section (Parser) then
638 return ASCII.NUL;
640 else
641 -- Recurse to get the next switch on the command line
643 goto Restart;
644 end if;
645 end if;
647 -- We are on the first character of a new command line argument,
648 -- which starts with Switch_Character. Further analysis is needed.
650 Parser.Current_Index := Parser.Current_Index + 1;
651 Parser.Is_Switch (Parser.Current_Argument) := True;
652 end if;
654 Find_Longest_Matching_Switch
655 (Switches => Switches,
656 Arg => Arg (Parser.Current_Index .. Arg'Last),
657 Index_In_Switches => Index_Switches,
658 Switch_Length => Max_Length,
659 Param => Param);
661 -- If switch is not accepted, it is either invalid or is returned
662 -- in the context of '*'.
664 if Index_Switches = 0 then
666 -- Depending on the value of Concatenate, the full switch is
667 -- a single character or the rest of the argument.
669 End_Index :=
670 (if Concatenate then Parser.Current_Index else Arg'Last);
672 if Switches (Switches'First) = '*' then
674 -- Always prepend the switch character, so that users know that
675 -- this comes from a switch on the command line. This is
676 -- especially important when Concatenate is False, since
677 -- otherwise the current argument first character is lost.
679 if Parser.Section (Parser.Current_Argument) = 0 then
681 -- A section transition should not be returned to the user
683 Dummy := Goto_Next_Argument_In_Section (Parser);
684 goto Restart;
686 else
687 Set_Parameter
688 (Parser.The_Switch,
689 Arg_Num => Parser.Current_Argument,
690 First => Parser.Current_Index,
691 Last => Arg'Last,
692 Extra => Parser.Switch_Character);
693 Parser.Is_Switch (Parser.Current_Argument) := True;
694 Dummy := Goto_Next_Argument_In_Section (Parser);
695 return '*';
696 end if;
697 end if;
699 Set_Parameter
700 (Parser.The_Switch,
701 Arg_Num => Parser.Current_Argument,
702 First => Parser.Current_Index,
703 Last => End_Index);
704 Parser.Current_Index := End_Index + 1;
706 raise Invalid_Switch;
707 end if;
709 End_Index := Parser.Current_Index + Max_Length - 1;
710 Set_Parameter
711 (Parser.The_Switch,
712 Arg_Num => Parser.Current_Argument,
713 First => Parser.Current_Index,
714 Last => End_Index);
716 case Param is
717 when Parameter_With_Optional_Space =>
718 if End_Index < Arg'Last then
719 Set_Parameter
720 (Parser.The_Parameter,
721 Arg_Num => Parser.Current_Argument,
722 First => End_Index + 1,
723 Last => Arg'Last);
724 Dummy := Goto_Next_Argument_In_Section (Parser);
726 elsif Parser.Current_Argument < Parser.Arg_Count
727 and then Parser.Section (Parser.Current_Argument + 1) /= 0
728 then
729 Parser.Current_Argument := Parser.Current_Argument + 1;
730 Parser.The_Separator := ' ';
731 Set_Parameter
732 (Parser.The_Parameter,
733 Arg_Num => Parser.Current_Argument,
734 First => Argument (Parser, Parser.Current_Argument)'First,
735 Last => Argument (Parser, Parser.Current_Argument)'Last);
736 Parser.Is_Switch (Parser.Current_Argument) := True;
737 Dummy := Goto_Next_Argument_In_Section (Parser);
739 else
740 Parser.Current_Index := End_Index + 1;
741 raise Invalid_Parameter;
742 end if;
744 when Parameter_With_Space_Or_Equal =>
746 -- If the switch is of the form <switch>=xxx
748 if End_Index < Arg'Last then
749 if Arg (End_Index + 1) = '='
750 and then End_Index + 1 < Arg'Last
751 then
752 Parser.The_Separator := '=';
753 Set_Parameter
754 (Parser.The_Parameter,
755 Arg_Num => Parser.Current_Argument,
756 First => End_Index + 2,
757 Last => Arg'Last);
758 Dummy := Goto_Next_Argument_In_Section (Parser);
760 else
761 Parser.Current_Index := End_Index + 1;
762 raise Invalid_Parameter;
763 end if;
765 -- If the switch is of the form <switch> xxx
767 elsif Parser.Current_Argument < Parser.Arg_Count
768 and then Parser.Section (Parser.Current_Argument + 1) /= 0
769 then
770 Parser.Current_Argument := Parser.Current_Argument + 1;
771 Parser.The_Separator := ' ';
772 Set_Parameter
773 (Parser.The_Parameter,
774 Arg_Num => Parser.Current_Argument,
775 First => Argument (Parser, Parser.Current_Argument)'First,
776 Last => Argument (Parser, Parser.Current_Argument)'Last);
777 Parser.Is_Switch (Parser.Current_Argument) := True;
778 Dummy := Goto_Next_Argument_In_Section (Parser);
780 else
781 Parser.Current_Index := End_Index + 1;
782 raise Invalid_Parameter;
783 end if;
785 when Parameter_No_Space =>
786 if End_Index < Arg'Last then
787 Set_Parameter
788 (Parser.The_Parameter,
789 Arg_Num => Parser.Current_Argument,
790 First => End_Index + 1,
791 Last => Arg'Last);
792 Dummy := Goto_Next_Argument_In_Section (Parser);
794 else
795 Parser.Current_Index := End_Index + 1;
796 raise Invalid_Parameter;
797 end if;
799 when Parameter_Optional =>
800 if End_Index < Arg'Last then
801 Set_Parameter
802 (Parser.The_Parameter,
803 Arg_Num => Parser.Current_Argument,
804 First => End_Index + 1,
805 Last => Arg'Last);
806 end if;
808 Dummy := Goto_Next_Argument_In_Section (Parser);
810 when Parameter_None =>
811 if Concatenate or else End_Index = Arg'Last then
812 Parser.Current_Index := End_Index + 1;
814 else
815 -- If Concatenate is False and the full argument is not
816 -- recognized as a switch, this is an invalid switch.
818 if Switches (Switches'First) = '*' then
819 Set_Parameter
820 (Parser.The_Switch,
821 Arg_Num => Parser.Current_Argument,
822 First => Arg'First,
823 Last => Arg'Last);
824 Parser.Is_Switch (Parser.Current_Argument) := True;
825 Dummy := Goto_Next_Argument_In_Section (Parser);
826 return '*';
827 end if;
829 Set_Parameter
830 (Parser.The_Switch,
831 Arg_Num => Parser.Current_Argument,
832 First => Parser.Current_Index,
833 Last => Arg'Last);
834 Parser.Current_Index := Arg'Last + 1;
835 raise Invalid_Switch;
836 end if;
837 end case;
839 return Switches (Index_Switches);
840 end;
841 end Getopt;
843 -----------------------------------
844 -- Goto_Next_Argument_In_Section --
845 -----------------------------------
847 function Goto_Next_Argument_In_Section
848 (Parser : Opt_Parser) return Boolean
850 begin
851 Parser.Current_Argument := Parser.Current_Argument + 1;
853 if Parser.Current_Argument > Parser.Arg_Count
854 or else Parser.Section (Parser.Current_Argument) = 0
855 then
856 loop
857 Parser.Current_Argument := Parser.Current_Argument + 1;
859 if Parser.Current_Argument > Parser.Arg_Count then
860 Parser.Current_Index := 1;
861 return False;
862 end if;
864 exit when Parser.Section (Parser.Current_Argument) =
865 Parser.Current_Section;
866 end loop;
867 end if;
869 Parser.Current_Index :=
870 Argument (Parser, Parser.Current_Argument)'First;
872 return True;
873 end Goto_Next_Argument_In_Section;
875 ------------------
876 -- Goto_Section --
877 ------------------
879 procedure Goto_Section
880 (Name : String := "";
881 Parser : Opt_Parser := Command_Line_Parser)
883 Index : Integer;
885 begin
886 Parser.In_Expansion := False;
888 if Name = "" then
889 Parser.Current_Argument := 1;
890 Parser.Current_Index := 1;
891 Parser.Current_Section := 1;
892 return;
893 end if;
895 Index := 1;
896 while Index <= Parser.Arg_Count loop
897 if Parser.Section (Index) = 0
898 and then Argument (Parser, Index) = Parser.Switch_Character & Name
899 then
900 Parser.Current_Argument := Index + 1;
901 Parser.Current_Index := 1;
903 if Parser.Current_Argument <= Parser.Arg_Count then
904 Parser.Current_Section :=
905 Parser.Section (Parser.Current_Argument);
906 end if;
908 -- Exit from loop if we have the start of another section
910 if Index = Parser.Section'Last
911 or else Parser.Section (Index + 1) /= 0
912 then
913 return;
914 end if;
915 end if;
917 Index := Index + 1;
918 end loop;
920 Parser.Current_Argument := Positive'Last;
921 Parser.Current_Index := 2; -- so that Get_Argument returns nothing
922 end Goto_Section;
924 ----------------------------
925 -- Initialize_Option_Scan --
926 ----------------------------
928 procedure Initialize_Option_Scan
929 (Switch_Char : Character := '-';
930 Stop_At_First_Non_Switch : Boolean := False;
931 Section_Delimiters : String := "")
933 begin
934 Internal_Initialize_Option_Scan
935 (Parser => Command_Line_Parser,
936 Switch_Char => Switch_Char,
937 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
938 Section_Delimiters => Section_Delimiters);
939 end Initialize_Option_Scan;
941 ----------------------------
942 -- Initialize_Option_Scan --
943 ----------------------------
945 procedure Initialize_Option_Scan
946 (Parser : out Opt_Parser;
947 Command_Line : GNAT.OS_Lib.Argument_List_Access;
948 Switch_Char : Character := '-';
949 Stop_At_First_Non_Switch : Boolean := False;
950 Section_Delimiters : String := "")
952 begin
953 Free (Parser);
955 if Command_Line = null then
956 Parser := new Opt_Parser_Data (CL.Argument_Count);
957 Internal_Initialize_Option_Scan
958 (Parser => Parser,
959 Switch_Char => Switch_Char,
960 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
961 Section_Delimiters => Section_Delimiters);
962 else
963 Parser := new Opt_Parser_Data (Command_Line'Length);
964 Parser.Arguments := Command_Line;
965 Internal_Initialize_Option_Scan
966 (Parser => Parser,
967 Switch_Char => Switch_Char,
968 Stop_At_First_Non_Switch => Stop_At_First_Non_Switch,
969 Section_Delimiters => Section_Delimiters);
970 end if;
971 end Initialize_Option_Scan;
973 -------------------------------------
974 -- Internal_Initialize_Option_Scan --
975 -------------------------------------
977 procedure Internal_Initialize_Option_Scan
978 (Parser : Opt_Parser;
979 Switch_Char : Character;
980 Stop_At_First_Non_Switch : Boolean;
981 Section_Delimiters : String)
983 Section_Num : Section_Number;
984 Section_Index : Integer;
985 Last : Integer;
986 Delimiter_Found : Boolean;
988 Discard : Boolean;
989 pragma Warnings (Off, Discard);
991 begin
992 Parser.Current_Argument := 0;
993 Parser.Current_Index := 0;
994 Parser.In_Expansion := False;
995 Parser.Switch_Character := Switch_Char;
996 Parser.Stop_At_First := Stop_At_First_Non_Switch;
997 Parser.Section := (others => 1);
999 -- If we are using sections, we have to preprocess the command line to
1000 -- delimit them. A section can be repeated, so we just give each item
1001 -- on the command line a section number
1003 Section_Num := 1;
1004 Section_Index := Section_Delimiters'First;
1005 while Section_Index <= Section_Delimiters'Last loop
1006 Last := Section_Index;
1007 while Last <= Section_Delimiters'Last
1008 and then Section_Delimiters (Last) /= ' '
1009 loop
1010 Last := Last + 1;
1011 end loop;
1013 Delimiter_Found := False;
1014 Section_Num := Section_Num + 1;
1016 for Index in 1 .. Parser.Arg_Count loop
1017 if Argument (Parser, Index)(1) = Parser.Switch_Character
1018 and then
1019 Argument (Parser, Index) = Parser.Switch_Character &
1020 Section_Delimiters
1021 (Section_Index .. Last - 1)
1022 then
1023 Parser.Section (Index) := 0;
1024 Delimiter_Found := True;
1026 elsif Parser.Section (Index) = 0 then
1028 -- A previous section delimiter
1030 Delimiter_Found := False;
1032 elsif Delimiter_Found then
1033 Parser.Section (Index) := Section_Num;
1034 end if;
1035 end loop;
1037 Section_Index := Last + 1;
1038 while Section_Index <= Section_Delimiters'Last
1039 and then Section_Delimiters (Section_Index) = ' '
1040 loop
1041 Section_Index := Section_Index + 1;
1042 end loop;
1043 end loop;
1045 Discard := Goto_Next_Argument_In_Section (Parser);
1046 end Internal_Initialize_Option_Scan;
1048 ---------------
1049 -- Parameter --
1050 ---------------
1052 function Parameter
1053 (Parser : Opt_Parser := Command_Line_Parser) return String
1055 begin
1056 if Parser.The_Parameter.First > Parser.The_Parameter.Last then
1057 return String'(1 .. 0 => ' ');
1058 else
1059 return Argument (Parser, Parser.The_Parameter.Arg_Num)
1060 (Parser.The_Parameter.First .. Parser.The_Parameter.Last);
1061 end if;
1062 end Parameter;
1064 ---------------
1065 -- Separator --
1066 ---------------
1068 function Separator
1069 (Parser : Opt_Parser := Command_Line_Parser) return Character
1071 begin
1072 return Parser.The_Separator;
1073 end Separator;
1075 -------------------
1076 -- Set_Parameter --
1077 -------------------
1079 procedure Set_Parameter
1080 (Variable : out Parameter_Type;
1081 Arg_Num : Positive;
1082 First : Positive;
1083 Last : Positive;
1084 Extra : Character := ASCII.NUL)
1086 begin
1087 Variable.Arg_Num := Arg_Num;
1088 Variable.First := First;
1089 Variable.Last := Last;
1090 Variable.Extra := Extra;
1091 end Set_Parameter;
1093 ---------------------
1094 -- Start_Expansion --
1095 ---------------------
1097 procedure Start_Expansion
1098 (Iterator : out Expansion_Iterator;
1099 Pattern : String;
1100 Directory : String := "";
1101 Basic_Regexp : Boolean := True)
1103 Directory_Separator : Character;
1104 pragma Import (C, Directory_Separator, "__gnat_dir_separator");
1106 First : Positive := Pattern'First;
1107 Pat : String := Pattern;
1109 begin
1110 Canonical_Case_File_Name (Pat);
1111 Iterator.Current_Depth := 1;
1113 -- If Directory is unspecified, use the current directory ("./" or ".\")
1115 if Directory = "" then
1116 Iterator.Dir_Name (1 .. 2) := "." & Directory_Separator;
1117 Iterator.Start := 3;
1119 else
1120 Iterator.Dir_Name (1 .. Directory'Length) := Directory;
1121 Iterator.Start := Directory'Length + 1;
1122 Canonical_Case_File_Name (Iterator.Dir_Name (1 .. Directory'Length));
1124 -- Make sure that the last character is a directory separator
1126 if Directory (Directory'Last) /= Directory_Separator then
1127 Iterator.Dir_Name (Iterator.Start) := Directory_Separator;
1128 Iterator.Start := Iterator.Start + 1;
1129 end if;
1130 end if;
1132 Iterator.Levels (1).Name_Last := Iterator.Start - 1;
1134 -- Open the initial Directory, at depth 1
1136 GNAT.Directory_Operations.Open
1137 (Iterator.Levels (1).Dir, Iterator.Dir_Name (1 .. Iterator.Start - 1));
1139 -- If in the current directory and the pattern starts with "./" or ".\",
1140 -- drop the "./" or ".\" from the pattern.
1142 if Directory = "" and then Pat'Length > 2
1143 and then Pat (Pat'First) = '.'
1144 and then Pat (Pat'First + 1) = Directory_Separator
1145 then
1146 First := Pat'First + 2;
1147 end if;
1149 Iterator.Regexp :=
1150 GNAT.Regexp.Compile (Pat (First .. Pat'Last), Basic_Regexp, True);
1152 Iterator.Maximum_Depth := 1;
1154 -- Maximum_Depth is equal to 1 plus the number of directory separators
1155 -- in the pattern.
1157 for Index in First .. Pat'Last loop
1158 if Pat (Index) = Directory_Separator then
1159 Iterator.Maximum_Depth := Iterator.Maximum_Depth + 1;
1160 exit when Iterator.Maximum_Depth = Max_Depth;
1161 end if;
1162 end loop;
1163 end Start_Expansion;
1165 ----------
1166 -- Free --
1167 ----------
1169 procedure Free (Parser : in out Opt_Parser) is
1170 procedure Unchecked_Free is new
1171 Ada.Unchecked_Deallocation (Opt_Parser_Data, Opt_Parser);
1172 begin
1173 if Parser /= null
1174 and then Parser /= Command_Line_Parser
1175 then
1176 Free (Parser.Arguments);
1177 Unchecked_Free (Parser);
1178 end if;
1179 end Free;
1181 ------------------
1182 -- Define_Alias --
1183 ------------------
1185 procedure Define_Alias
1186 (Config : in out Command_Line_Configuration;
1187 Switch : String;
1188 Expanded : String;
1189 Section : String := "")
1191 Def : Alias_Definition;
1192 begin
1193 if Config = null then
1194 Config := new Command_Line_Configuration_Record;
1195 end if;
1197 Def.Alias := new String'(Switch);
1198 Def.Expansion := new String'(Expanded);
1199 Def.Section := new String'(Section);
1200 Add (Config.Aliases, Def);
1201 end Define_Alias;
1203 -------------------
1204 -- Define_Prefix --
1205 -------------------
1207 procedure Define_Prefix
1208 (Config : in out Command_Line_Configuration;
1209 Prefix : String)
1211 begin
1212 if Config = null then
1213 Config := new Command_Line_Configuration_Record;
1214 end if;
1216 Add (Config.Prefixes, new String'(Prefix));
1217 end Define_Prefix;
1219 ---------
1220 -- Add --
1221 ---------
1223 procedure Add
1224 (Config : in out Command_Line_Configuration;
1225 Switch : Switch_Definition)
1227 procedure Unchecked_Free is new Ada.Unchecked_Deallocation
1228 (Switch_Definitions, Switch_Definitions_List);
1230 Tmp : Switch_Definitions_List;
1232 begin
1233 if Config = null then
1234 Config := new Command_Line_Configuration_Record;
1235 end if;
1237 Tmp := Config.Switches;
1239 if Tmp = null then
1240 Config.Switches := new Switch_Definitions (1 .. 1);
1241 else
1242 Config.Switches := new Switch_Definitions (1 .. Tmp'Length + 1);
1243 Config.Switches (1 .. Tmp'Length) := Tmp.all;
1244 Unchecked_Free (Tmp);
1245 end if;
1247 if Switch.Switch /= null and then Switch.Switch.all = "*" then
1248 Config.Star_Switch := True;
1249 end if;
1251 Config.Switches (Config.Switches'Last) := Switch;
1252 end Add;
1254 ---------
1255 -- Add --
1256 ---------
1258 procedure Add (Def : in out Alias_Definitions_List;
1259 Alias : Alias_Definition)
1261 procedure Unchecked_Free is new
1262 Ada.Unchecked_Deallocation
1263 (Alias_Definitions, Alias_Definitions_List);
1265 Tmp : Alias_Definitions_List := Def;
1267 begin
1268 if Tmp = null then
1269 Def := new Alias_Definitions (1 .. 1);
1270 else
1271 Def := new Alias_Definitions (1 .. Tmp'Length + 1);
1272 Def (1 .. Tmp'Length) := Tmp.all;
1273 Unchecked_Free (Tmp);
1274 end if;
1276 Def (Def'Last) := Alias;
1277 end Add;
1279 ---------------------------
1280 -- Initialize_Switch_Def --
1281 ---------------------------
1283 procedure Initialize_Switch_Def
1284 (Def : out Switch_Definition;
1285 Switch : String := "";
1286 Long_Switch : String := "";
1287 Help : String := "";
1288 Section : String := "";
1289 Argument : String := "ARG")
1291 P1, P2 : Switch_Parameter_Type := Parameter_None;
1292 Last1, Last2 : Integer;
1294 begin
1295 if Switch /= "" then
1296 Def.Switch := new String'(Switch);
1297 Decompose_Switch (Switch, P1, Last1);
1298 end if;
1300 if Long_Switch /= "" then
1301 Def.Long_Switch := new String'(Long_Switch);
1302 Decompose_Switch (Long_Switch, P2, Last2);
1303 end if;
1305 if Switch /= "" and then Long_Switch /= "" then
1306 if (P1 = Parameter_None and then P2 /= P1)
1307 or else (P2 = Parameter_None and then P1 /= P2)
1308 or else (P1 = Parameter_Optional and then P2 /= P1)
1309 or else (P2 = Parameter_Optional and then P2 /= P1)
1310 then
1311 raise Invalid_Switch
1312 with "Inconsistent parameter types for "
1313 & Switch & " and " & Long_Switch;
1314 end if;
1315 end if;
1317 if Section /= "" then
1318 Def.Section := new String'(Section);
1319 end if;
1321 if Argument /= "ARG" then
1322 Def.Argument := new String'(Argument);
1323 end if;
1325 if Help /= "" then
1326 Def.Help := new String'(Help);
1327 end if;
1328 end Initialize_Switch_Def;
1330 -------------------
1331 -- Define_Switch --
1332 -------------------
1334 procedure Define_Switch
1335 (Config : in out Command_Line_Configuration;
1336 Switch : String := "";
1337 Long_Switch : String := "";
1338 Help : String := "";
1339 Section : String := "";
1340 Argument : String := "ARG")
1342 Def : Switch_Definition;
1343 begin
1344 if Switch /= "" or else Long_Switch /= "" then
1345 Initialize_Switch_Def
1346 (Def, Switch, Long_Switch, Help, Section, Argument);
1347 Add (Config, Def);
1348 end if;
1349 end Define_Switch;
1351 -------------------
1352 -- Define_Switch --
1353 -------------------
1355 procedure Define_Switch
1356 (Config : in out Command_Line_Configuration;
1357 Output : access Boolean;
1358 Switch : String := "";
1359 Long_Switch : String := "";
1360 Help : String := "";
1361 Section : String := "";
1362 Value : Boolean := True)
1364 Def : Switch_Definition (Switch_Boolean);
1365 begin
1366 if Switch /= "" or else Long_Switch /= "" then
1367 Initialize_Switch_Def (Def, Switch, Long_Switch, Help, Section);
1368 Def.Boolean_Output := Output.all'Unchecked_Access;
1369 Def.Boolean_Value := Value;
1370 Add (Config, Def);
1371 end if;
1372 end Define_Switch;
1374 -------------------
1375 -- Define_Switch --
1376 -------------------
1378 procedure Define_Switch
1379 (Config : in out Command_Line_Configuration;
1380 Output : access Integer;
1381 Switch : String := "";
1382 Long_Switch : String := "";
1383 Help : String := "";
1384 Section : String := "";
1385 Initial : Integer := 0;
1386 Default : Integer := 1;
1387 Argument : String := "ARG")
1389 Def : Switch_Definition (Switch_Integer);
1390 begin
1391 if Switch /= "" or else Long_Switch /= "" then
1392 Initialize_Switch_Def
1393 (Def, Switch, Long_Switch, Help, Section, Argument);
1394 Def.Integer_Output := Output.all'Unchecked_Access;
1395 Def.Integer_Default := Default;
1396 Def.Integer_Initial := Initial;
1397 Add (Config, Def);
1398 end if;
1399 end Define_Switch;
1401 -------------------
1402 -- Define_Switch --
1403 -------------------
1405 procedure Define_Switch
1406 (Config : in out Command_Line_Configuration;
1407 Output : access GNAT.Strings.String_Access;
1408 Switch : String := "";
1409 Long_Switch : String := "";
1410 Help : String := "";
1411 Section : String := "";
1412 Argument : String := "ARG")
1414 Def : Switch_Definition (Switch_String);
1415 begin
1416 if Switch /= "" or else Long_Switch /= "" then
1417 Initialize_Switch_Def
1418 (Def, Switch, Long_Switch, Help, Section, Argument);
1419 Def.String_Output := Output.all'Unchecked_Access;
1420 Add (Config, Def);
1421 end if;
1422 end Define_Switch;
1424 --------------------
1425 -- Define_Section --
1426 --------------------
1428 procedure Define_Section
1429 (Config : in out Command_Line_Configuration;
1430 Section : String)
1432 begin
1433 if Config = null then
1434 Config := new Command_Line_Configuration_Record;
1435 end if;
1437 Add (Config.Sections, new String'(Section));
1438 end Define_Section;
1440 --------------------
1441 -- Foreach_Switch --
1442 --------------------
1444 procedure Foreach_Switch
1445 (Config : Command_Line_Configuration;
1446 Section : String)
1448 begin
1449 if Config /= null and then Config.Switches /= null then
1450 for J in Config.Switches'Range loop
1451 if (Section = "" and then Config.Switches (J).Section = null)
1452 or else
1453 (Config.Switches (J).Section /= null
1454 and then Config.Switches (J).Section.all = Section)
1455 then
1456 exit when Config.Switches (J).Switch /= null
1457 and then not Callback (Config.Switches (J).Switch.all, J);
1459 exit when Config.Switches (J).Long_Switch /= null
1460 and then
1461 not Callback (Config.Switches (J).Long_Switch.all, J);
1462 end if;
1463 end loop;
1464 end if;
1465 end Foreach_Switch;
1467 ------------------
1468 -- Get_Switches --
1469 ------------------
1471 function Get_Switches
1472 (Config : Command_Line_Configuration;
1473 Switch_Char : Character := '-';
1474 Section : String := "") return String
1476 Ret : Ada.Strings.Unbounded.Unbounded_String;
1477 use Ada.Strings.Unbounded;
1479 function Add_Switch (S : String; Index : Integer) return Boolean;
1480 -- Add a switch to Ret
1482 ----------------
1483 -- Add_Switch --
1484 ----------------
1486 function Add_Switch (S : String; Index : Integer) return Boolean is
1487 pragma Unreferenced (Index);
1488 begin
1489 if S = "*" then
1490 Ret := "*" & Ret; -- Always first
1491 elsif S (S'First) = Switch_Char then
1492 Append (Ret, " " & S (S'First + 1 .. S'Last));
1493 else
1494 Append (Ret, " " & S);
1495 end if;
1497 return True;
1498 end Add_Switch;
1500 Tmp : Boolean;
1501 pragma Unreferenced (Tmp);
1503 procedure Foreach is new Foreach_Switch (Add_Switch);
1505 -- Start of processing for Get_Switches
1507 begin
1508 if Config = null then
1509 return "";
1510 end if;
1512 Foreach (Config, Section => Section);
1514 -- Adding relevant aliases
1516 if Config.Aliases /= null then
1517 for A in Config.Aliases'Range loop
1518 if Config.Aliases (A).Section.all = Section then
1519 Tmp := Add_Switch (Config.Aliases (A).Alias.all, -1);
1520 end if;
1521 end loop;
1522 end if;
1524 return To_String (Ret);
1525 end Get_Switches;
1527 ------------------------
1528 -- Section_Delimiters --
1529 ------------------------
1531 function Section_Delimiters
1532 (Config : Command_Line_Configuration) return String
1534 use Ada.Strings.Unbounded;
1535 Result : Unbounded_String;
1537 begin
1538 if Config /= null and then Config.Sections /= null then
1539 for S in Config.Sections'Range loop
1540 Append (Result, " " & Config.Sections (S).all);
1541 end loop;
1542 end if;
1544 return To_String (Result);
1545 end Section_Delimiters;
1547 -----------------------
1548 -- Set_Configuration --
1549 -----------------------
1551 procedure Set_Configuration
1552 (Cmd : in out Command_Line;
1553 Config : Command_Line_Configuration)
1555 begin
1556 Cmd.Config := Config;
1557 end Set_Configuration;
1559 -----------------------
1560 -- Get_Configuration --
1561 -----------------------
1563 function Get_Configuration
1564 (Cmd : Command_Line) return Command_Line_Configuration
1566 begin
1567 return Cmd.Config;
1568 end Get_Configuration;
1570 ----------------------
1571 -- Set_Command_Line --
1572 ----------------------
1574 procedure Set_Command_Line
1575 (Cmd : in out Command_Line;
1576 Switches : String;
1577 Getopt_Description : String := "";
1578 Switch_Char : Character := '-')
1580 Tmp : Argument_List_Access;
1581 Parser : Opt_Parser;
1582 S : Character;
1583 Section : String_Access := null;
1585 function Real_Full_Switch
1586 (S : Character;
1587 Parser : Opt_Parser) return String;
1588 -- Ensure that the returned switch value contains the
1589 -- Switch_Char prefix if needed.
1591 ----------------------
1592 -- Real_Full_Switch --
1593 ----------------------
1595 function Real_Full_Switch
1596 (S : Character;
1597 Parser : Opt_Parser) return String
1599 begin
1600 if S = '*' then
1601 return Full_Switch (Parser);
1602 else
1603 return Switch_Char & Full_Switch (Parser);
1604 end if;
1605 end Real_Full_Switch;
1607 -- Start of processing for Set_Command_Line
1609 begin
1610 Free (Cmd.Expanded);
1611 Free (Cmd.Params);
1613 if Switches /= "" then
1614 Tmp := Argument_String_To_List (Switches);
1615 Initialize_Option_Scan (Parser, Tmp, Switch_Char);
1617 loop
1618 begin
1619 if Cmd.Config /= null then
1621 -- Do not use Getopt_Description in this case. Otherwise,
1622 -- if we have defined a prefix -gnaty, and two switches
1623 -- -gnatya and -gnatyL!, we would have a different behavior
1624 -- depending on the order of switches:
1626 -- -gnatyL1a => -gnatyL with argument "1a"
1627 -- -gnatyaL1 => -gnatya and -gnatyL with argument "1"
1629 -- This is because the call to Getopt below knows nothing
1630 -- about prefixes, and in the first case finds a valid
1631 -- switch with arguments, so returns it without analyzing
1632 -- the argument. In the second case, the switch matches "*",
1633 -- and is then decomposed below.
1635 S := Getopt (Switches => "*",
1636 Concatenate => False,
1637 Parser => Parser);
1639 else
1640 S := Getopt (Switches => "* " & Getopt_Description,
1641 Concatenate => False,
1642 Parser => Parser);
1643 end if;
1645 exit when S = ASCII.NUL;
1647 declare
1648 Sw : constant String := Real_Full_Switch (S, Parser);
1649 Is_Section : Boolean := False;
1651 begin
1652 if Cmd.Config /= null
1653 and then Cmd.Config.Sections /= null
1654 then
1655 Section_Search :
1656 for S in Cmd.Config.Sections'Range loop
1657 if Sw = Cmd.Config.Sections (S).all then
1658 Section := Cmd.Config.Sections (S);
1659 Is_Section := True;
1661 exit Section_Search;
1662 end if;
1663 end loop Section_Search;
1664 end if;
1666 if not Is_Section then
1667 if Section = null then
1668 Add_Switch (Cmd, Sw, Parameter (Parser));
1669 else
1670 Add_Switch
1671 (Cmd, Sw, Parameter (Parser),
1672 Section => Section.all);
1673 end if;
1674 end if;
1675 end;
1677 exception
1678 when Invalid_Parameter =>
1680 -- Add it with no parameter, if that's the way the user
1681 -- wants it.
1683 -- Specify the separator in all cases, as the switch might
1684 -- need to be unaliased, and the alias might contain
1685 -- switches with parameters.
1687 if Section = null then
1688 Add_Switch
1689 (Cmd, Switch_Char & Full_Switch (Parser));
1690 else
1691 Add_Switch
1692 (Cmd, Switch_Char & Full_Switch (Parser),
1693 Section => Section.all);
1694 end if;
1695 end;
1696 end loop;
1698 Free (Parser);
1699 end if;
1700 end Set_Command_Line;
1702 ----------------
1703 -- Looking_At --
1704 ----------------
1706 function Looking_At
1707 (Type_Str : String;
1708 Index : Natural;
1709 Substring : String) return Boolean
1711 begin
1712 return Index + Substring'Length - 1 <= Type_Str'Last
1713 and then Type_Str (Index .. Index + Substring'Length - 1) = Substring;
1714 end Looking_At;
1716 ------------------------
1717 -- Can_Have_Parameter --
1718 ------------------------
1720 function Can_Have_Parameter (S : String) return Boolean is
1721 begin
1722 if S'Length <= 1 then
1723 return False;
1724 end if;
1726 case S (S'Last) is
1727 when '!' | ':' | '?' | '=' =>
1728 return True;
1729 when others =>
1730 return False;
1731 end case;
1732 end Can_Have_Parameter;
1734 -----------------------
1735 -- Require_Parameter --
1736 -----------------------
1738 function Require_Parameter (S : String) return Boolean is
1739 begin
1740 if S'Length <= 1 then
1741 return False;
1742 end if;
1744 case S (S'Last) is
1745 when '!' | ':' | '=' =>
1746 return True;
1747 when others =>
1748 return False;
1749 end case;
1750 end Require_Parameter;
1752 -------------------
1753 -- Actual_Switch --
1754 -------------------
1756 function Actual_Switch (S : String) return String is
1757 begin
1758 if S'Length <= 1 then
1759 return S;
1760 end if;
1762 case S (S'Last) is
1763 when '!' | ':' | '?' | '=' =>
1764 return S (S'First .. S'Last - 1);
1765 when others =>
1766 return S;
1767 end case;
1768 end Actual_Switch;
1770 ----------------------------
1771 -- For_Each_Simple_Switch --
1772 ----------------------------
1774 procedure For_Each_Simple_Switch
1775 (Config : Command_Line_Configuration;
1776 Section : String;
1777 Switch : String;
1778 Parameter : String := "";
1779 Unalias : Boolean := True)
1781 function Group_Analysis
1782 (Prefix : String;
1783 Group : String) return Boolean;
1784 -- Perform the analysis of a group of switches
1786 Found_In_Config : Boolean := False;
1787 function Is_In_Config
1788 (Config_Switch : String; Index : Integer) return Boolean;
1789 -- If Switch is the same as Config_Switch, run the callback and sets
1790 -- Found_In_Config to True.
1792 function Starts_With
1793 (Config_Switch : String; Index : Integer) return Boolean;
1794 -- if Switch starts with Config_Switch, sets Found_In_Config to True.
1795 -- The return value is for the Foreach_Switch iterator.
1797 --------------------
1798 -- Group_Analysis --
1799 --------------------
1801 function Group_Analysis
1802 (Prefix : String;
1803 Group : String) return Boolean
1805 Idx : Natural;
1806 Found : Boolean;
1808 function Analyze_Simple_Switch
1809 (Switch : String; Index : Integer) return Boolean;
1810 -- "Switches" is one of the switch definitions passed to the
1811 -- configuration, not one of the switches found on the command line.
1813 ---------------------------
1814 -- Analyze_Simple_Switch --
1815 ---------------------------
1817 function Analyze_Simple_Switch
1818 (Switch : String; Index : Integer) return Boolean
1820 pragma Unreferenced (Index);
1822 Full : constant String := Prefix & Group (Idx .. Group'Last);
1824 Sw : constant String := Actual_Switch (Switch);
1825 -- Switches definition minus argument definition
1827 Last : Natural;
1828 Param : Natural;
1830 begin
1831 -- Verify that sw starts with Prefix
1833 if Looking_At (Sw, Sw'First, Prefix)
1835 -- Verify that the group starts with sw
1837 and then Looking_At (Full, Full'First, Sw)
1838 then
1839 Last := Idx + Sw'Length - Prefix'Length - 1;
1840 Param := Last + 1;
1842 if Can_Have_Parameter (Switch) then
1844 -- Include potential parameter to the recursive call. Only
1845 -- numbers are allowed.
1847 while Last < Group'Last
1848 and then Group (Last + 1) in '0' .. '9'
1849 loop
1850 Last := Last + 1;
1851 end loop;
1852 end if;
1854 if not Require_Parameter (Switch) or else Last >= Param then
1855 if Idx = Group'First
1856 and then Last = Group'Last
1857 and then Last < Param
1858 then
1859 -- The group only concerns a single switch. Do not
1860 -- perform recursive call.
1862 -- Note that we still perform a recursive call if
1863 -- a parameter is detected in the switch, as this
1864 -- is a way to correctly identify such a parameter
1865 -- in aliases.
1867 return False;
1868 end if;
1870 Found := True;
1872 -- Recursive call, using the detected parameter if any
1874 if Last >= Param then
1875 For_Each_Simple_Switch
1876 (Config,
1877 Section,
1878 Prefix & Group (Idx .. Param - 1),
1879 Group (Param .. Last));
1881 else
1882 For_Each_Simple_Switch
1883 (Config, Section, Prefix & Group (Idx .. Last), "");
1884 end if;
1886 Idx := Last + 1;
1887 return False;
1888 end if;
1889 end if;
1891 return True;
1892 end Analyze_Simple_Switch;
1894 procedure Foreach is new Foreach_Switch (Analyze_Simple_Switch);
1896 -- Start of processing for Group_Analysis
1898 begin
1899 Idx := Group'First;
1900 while Idx <= Group'Last loop
1901 Found := False;
1902 Foreach (Config, Section);
1904 if not Found then
1905 For_Each_Simple_Switch
1906 (Config, Section, Prefix & Group (Idx), "");
1907 Idx := Idx + 1;
1908 end if;
1909 end loop;
1911 return True;
1912 end Group_Analysis;
1914 ------------------
1915 -- Is_In_Config --
1916 ------------------
1918 function Is_In_Config
1919 (Config_Switch : String; Index : Integer) return Boolean
1921 Last : Natural;
1922 P : Switch_Parameter_Type;
1924 begin
1925 Decompose_Switch (Config_Switch, P, Last);
1927 if Config_Switch (Config_Switch'First .. Last) = Switch then
1928 case P is
1929 when Parameter_None =>
1930 if Parameter = "" then
1931 Callback (Switch, "", "", Index => Index);
1932 Found_In_Config := True;
1933 return False;
1934 end if;
1936 when Parameter_With_Optional_Space =>
1937 Callback (Switch, " ", Parameter, Index => Index);
1938 Found_In_Config := True;
1939 return False;
1941 when Parameter_With_Space_Or_Equal =>
1942 Callback (Switch, "=", Parameter, Index => Index);
1943 Found_In_Config := True;
1944 return False;
1946 when Parameter_No_Space =>
1947 Callback (Switch, "", Parameter, Index);
1948 Found_In_Config := True;
1949 return False;
1951 when Parameter_Optional =>
1952 Callback (Switch, "", Parameter, Index);
1953 Found_In_Config := True;
1954 return False;
1955 end case;
1956 end if;
1958 return True;
1959 end Is_In_Config;
1961 -----------------
1962 -- Starts_With --
1963 -----------------
1965 function Starts_With
1966 (Config_Switch : String; Index : Integer) return Boolean
1968 Last : Natural;
1969 Param : Natural;
1970 P : Switch_Parameter_Type;
1972 begin
1973 -- This function is called when we believe the parameter was
1974 -- specified as part of the switch, instead of separately. Thus we
1975 -- look in the config to find all possible switches.
1977 Decompose_Switch (Config_Switch, P, Last);
1979 if Looking_At
1980 (Switch, Switch'First,
1981 Config_Switch (Config_Switch'First .. Last))
1982 then
1983 -- Set first char of Param, and last char of Switch
1985 Param := Switch'First + Last;
1986 Last := Switch'First + Last - Config_Switch'First;
1988 case P is
1990 -- None is already handled in Is_In_Config
1992 when Parameter_None =>
1993 null;
1995 when Parameter_With_Space_Or_Equal =>
1996 if Param <= Switch'Last
1997 and then
1998 (Switch (Param) = ' ' or else Switch (Param) = '=')
1999 then
2000 Callback (Switch (Switch'First .. Last),
2001 "=", Switch (Param + 1 .. Switch'Last), Index);
2002 Found_In_Config := True;
2003 return False;
2004 end if;
2006 when Parameter_With_Optional_Space =>
2007 if Param <= Switch'Last and then Switch (Param) = ' ' then
2008 Param := Param + 1;
2009 end if;
2011 Callback (Switch (Switch'First .. Last),
2012 " ", Switch (Param .. Switch'Last), Index);
2013 Found_In_Config := True;
2014 return False;
2016 when Parameter_No_Space | Parameter_Optional =>
2017 Callback (Switch (Switch'First .. Last),
2018 "", Switch (Param .. Switch'Last), Index);
2019 Found_In_Config := True;
2020 return False;
2021 end case;
2022 end if;
2023 return True;
2024 end Starts_With;
2026 procedure Foreach_In_Config is new Foreach_Switch (Is_In_Config);
2027 procedure Foreach_Starts_With is new Foreach_Switch (Starts_With);
2029 -- Start of processing for For_Each_Simple_Switch
2031 begin
2032 -- First determine if the switch corresponds to one belonging to the
2033 -- configuration. If so, run callback and exit.
2035 -- ??? Is this necessary. On simple tests, we seem to have the same
2036 -- results with or without this call.
2038 Foreach_In_Config (Config, Section);
2040 if Found_In_Config then
2041 return;
2042 end if;
2044 -- If adding a switch that can in fact be expanded through aliases,
2045 -- add separately each of its expansions.
2047 -- This takes care of expansions like "-T" -> "-gnatwrs", where the
2048 -- alias and its expansion do not have the same prefix. Given the order
2049 -- in which we do things here, the expansion of the alias will itself
2050 -- be checked for a common prefix and split into simple switches.
2052 if Unalias
2053 and then Config /= null
2054 and then Config.Aliases /= null
2055 then
2056 for A in Config.Aliases'Range loop
2057 if Config.Aliases (A).Section.all = Section
2058 and then Config.Aliases (A).Alias.all = Switch
2059 and then Parameter = ""
2060 then
2061 For_Each_Simple_Switch
2062 (Config, Section, Config.Aliases (A).Expansion.all, "");
2063 return;
2064 end if;
2065 end loop;
2066 end if;
2068 -- If adding a switch grouping several switches, add each of the simple
2069 -- switches instead.
2071 if Config /= null and then Config.Prefixes /= null then
2072 for P in Config.Prefixes'Range loop
2073 if Switch'Length > Config.Prefixes (P)'Length + 1
2074 and then
2075 Looking_At (Switch, Switch'First, Config.Prefixes (P).all)
2076 then
2077 -- Alias expansion will be done recursively
2079 if Config.Switches = null then
2080 for S in Switch'First + Config.Prefixes (P)'Length
2081 .. Switch'Last
2082 loop
2083 For_Each_Simple_Switch
2084 (Config, Section,
2085 Config.Prefixes (P).all & Switch (S), "");
2086 end loop;
2088 return;
2090 elsif Group_Analysis
2091 (Config.Prefixes (P).all,
2092 Switch
2093 (Switch'First + Config.Prefixes (P)'Length .. Switch'Last))
2094 then
2095 -- Recursive calls already done on each switch of the group:
2096 -- Return without executing Callback.
2098 return;
2099 end if;
2100 end if;
2101 end loop;
2102 end if;
2104 -- Test if added switch is a known switch with parameter attached
2105 -- instead of being specified separately
2107 if Parameter = ""
2108 and then Config /= null
2109 and then Config.Switches /= null
2110 then
2111 Found_In_Config := False;
2112 Foreach_Starts_With (Config, Section);
2114 if Found_In_Config then
2115 return;
2116 end if;
2117 end if;
2119 -- The switch is invalid in the config, but we still want to report it.
2120 -- The config could, for instance, include "*" to specify it accepts
2121 -- all switches.
2123 Callback (Switch, " ", Parameter, Index => -1);
2124 end For_Each_Simple_Switch;
2126 ----------------
2127 -- Add_Switch --
2128 ----------------
2130 procedure Add_Switch
2131 (Cmd : in out Command_Line;
2132 Switch : String;
2133 Parameter : String := "";
2134 Separator : Character := ASCII.NUL;
2135 Section : String := "";
2136 Add_Before : Boolean := False)
2138 Success : Boolean;
2139 pragma Unreferenced (Success);
2140 begin
2141 Add_Switch (Cmd, Switch, Parameter, Separator,
2142 Section, Add_Before, Success);
2143 end Add_Switch;
2145 ----------------
2146 -- Add_Switch --
2147 ----------------
2149 procedure Add_Switch
2150 (Cmd : in out Command_Line;
2151 Switch : String;
2152 Parameter : String := "";
2153 Separator : Character := ASCII.NUL;
2154 Section : String := "";
2155 Add_Before : Boolean := False;
2156 Success : out Boolean)
2158 procedure Add_Simple_Switch
2159 (Simple : String;
2160 Sepa : String;
2161 Param : String;
2162 Index : Integer);
2163 -- Add a new switch that has had all its aliases expanded, and switches
2164 -- ungrouped. We know there are no more aliases in Switches.
2166 -----------------------
2167 -- Add_Simple_Switch --
2168 -----------------------
2170 procedure Add_Simple_Switch
2171 (Simple : String;
2172 Sepa : String;
2173 Param : String;
2174 Index : Integer)
2176 Sep : Character;
2178 begin
2179 if Index = -1
2180 and then Cmd.Config /= null
2181 and then not Cmd.Config.Star_Switch
2182 then
2183 raise Invalid_Switch
2184 with "Invalid switch " & Simple;
2185 end if;
2187 if Separator /= ASCII.NUL then
2188 Sep := Separator;
2190 elsif Sepa = "" then
2191 Sep := ASCII.NUL;
2192 else
2193 Sep := Sepa (Sepa'First);
2194 end if;
2196 if Cmd.Expanded = null then
2197 Cmd.Expanded := new Argument_List'(1 .. 1 => new String'(Simple));
2199 if Param /= "" then
2200 Cmd.Params :=
2201 new Argument_List'(1 .. 1 => new String'(Sep & Param));
2202 else
2203 Cmd.Params := new Argument_List'(1 .. 1 => null);
2204 end if;
2206 if Section = "" then
2207 Cmd.Sections := new Argument_List'(1 .. 1 => null);
2208 else
2209 Cmd.Sections :=
2210 new Argument_List'(1 .. 1 => new String'(Section));
2211 end if;
2213 else
2214 -- Do we already have this switch?
2216 for C in Cmd.Expanded'Range loop
2217 if Cmd.Expanded (C).all = Simple
2218 and then
2219 ((Cmd.Params (C) = null and then Param = "")
2220 or else
2221 (Cmd.Params (C) /= null
2222 and then Cmd.Params (C).all = Sep & Param))
2223 and then
2224 ((Cmd.Sections (C) = null and then Section = "")
2225 or else
2226 (Cmd.Sections (C) /= null
2227 and then Cmd.Sections (C).all = Section))
2228 then
2229 return;
2230 end if;
2231 end loop;
2233 -- Inserting at least one switch
2235 Success := True;
2236 Add (Cmd.Expanded, new String'(Simple), Add_Before);
2238 if Param /= "" then
2240 (Cmd.Params,
2241 new String'(Sep & Param),
2242 Add_Before);
2243 else
2245 (Cmd.Params,
2246 null,
2247 Add_Before);
2248 end if;
2250 if Section = "" then
2252 (Cmd.Sections,
2253 null,
2254 Add_Before);
2255 else
2257 (Cmd.Sections,
2258 new String'(Section),
2259 Add_Before);
2260 end if;
2261 end if;
2262 end Add_Simple_Switch;
2264 procedure Add_Simple_Switches is
2265 new For_Each_Simple_Switch (Add_Simple_Switch);
2267 -- Local Variables
2269 Section_Valid : Boolean := False;
2271 -- Start of processing for Add_Switch
2273 begin
2274 if Section /= "" and then Cmd.Config /= null then
2275 for S in Cmd.Config.Sections'Range loop
2276 if Section = Cmd.Config.Sections (S).all then
2277 Section_Valid := True;
2278 exit;
2279 end if;
2280 end loop;
2282 if not Section_Valid then
2283 raise Invalid_Section;
2284 end if;
2285 end if;
2287 Success := False;
2288 Add_Simple_Switches (Cmd.Config, Section, Switch, Parameter);
2289 Free (Cmd.Coalesce);
2290 end Add_Switch;
2292 ------------
2293 -- Remove --
2294 ------------
2296 procedure Remove (Line : in out Argument_List_Access; Index : Integer) is
2297 Tmp : Argument_List_Access := Line;
2299 begin
2300 Line := new Argument_List (Tmp'First .. Tmp'Last - 1);
2302 if Index /= Tmp'First then
2303 Line (Tmp'First .. Index - 1) := Tmp (Tmp'First .. Index - 1);
2304 end if;
2306 Free (Tmp (Index));
2308 if Index /= Tmp'Last then
2309 Line (Index .. Tmp'Last - 1) := Tmp (Index + 1 .. Tmp'Last);
2310 end if;
2312 Unchecked_Free (Tmp);
2313 end Remove;
2315 ---------
2316 -- Add --
2317 ---------
2319 procedure Add
2320 (Line : in out Argument_List_Access;
2321 Str : String_Access;
2322 Before : Boolean := False)
2324 Tmp : Argument_List_Access := Line;
2326 begin
2327 if Tmp /= null then
2328 Line := new Argument_List (Tmp'First .. Tmp'Last + 1);
2330 if Before then
2331 Line (Tmp'First) := Str;
2332 Line (Tmp'First + 1 .. Tmp'Last + 1) := Tmp.all;
2333 else
2334 Line (Tmp'Range) := Tmp.all;
2335 Line (Tmp'Last + 1) := Str;
2336 end if;
2338 Unchecked_Free (Tmp);
2340 else
2341 Line := new Argument_List'(1 .. 1 => Str);
2342 end if;
2343 end Add;
2345 -------------------
2346 -- Remove_Switch --
2347 -------------------
2349 procedure Remove_Switch
2350 (Cmd : in out Command_Line;
2351 Switch : String;
2352 Remove_All : Boolean := False;
2353 Has_Parameter : Boolean := False;
2354 Section : String := "")
2356 Success : Boolean;
2357 pragma Unreferenced (Success);
2358 begin
2359 Remove_Switch (Cmd, Switch, Remove_All, Has_Parameter, Section, Success);
2360 end Remove_Switch;
2362 -------------------
2363 -- Remove_Switch --
2364 -------------------
2366 procedure Remove_Switch
2367 (Cmd : in out Command_Line;
2368 Switch : String;
2369 Remove_All : Boolean := False;
2370 Has_Parameter : Boolean := False;
2371 Section : String := "";
2372 Success : out Boolean)
2374 procedure Remove_Simple_Switch
2375 (Simple, Separator, Param : String; Index : Integer);
2376 -- Removes a simple switch, with no aliasing or grouping
2378 --------------------------
2379 -- Remove_Simple_Switch --
2380 --------------------------
2382 procedure Remove_Simple_Switch
2383 (Simple, Separator, Param : String; Index : Integer)
2385 C : Integer;
2386 pragma Unreferenced (Param, Separator, Index);
2388 begin
2389 if Cmd.Expanded /= null then
2390 C := Cmd.Expanded'First;
2391 while C <= Cmd.Expanded'Last loop
2392 if Cmd.Expanded (C).all = Simple
2393 and then
2394 (Remove_All
2395 or else (Cmd.Sections (C) = null
2396 and then Section = "")
2397 or else (Cmd.Sections (C) /= null
2398 and then Section = Cmd.Sections (C).all))
2399 and then (not Has_Parameter or else Cmd.Params (C) /= null)
2400 then
2401 Remove (Cmd.Expanded, C);
2402 Remove (Cmd.Params, C);
2403 Remove (Cmd.Sections, C);
2404 Success := True;
2406 if not Remove_All then
2407 return;
2408 end if;
2410 else
2411 C := C + 1;
2412 end if;
2413 end loop;
2414 end if;
2415 end Remove_Simple_Switch;
2417 procedure Remove_Simple_Switches is
2418 new For_Each_Simple_Switch (Remove_Simple_Switch);
2420 -- Start of processing for Remove_Switch
2422 begin
2423 Success := False;
2424 Remove_Simple_Switches
2425 (Cmd.Config, Section, Switch, "", Unalias => not Has_Parameter);
2426 Free (Cmd.Coalesce);
2427 end Remove_Switch;
2429 -------------------
2430 -- Remove_Switch --
2431 -------------------
2433 procedure Remove_Switch
2434 (Cmd : in out Command_Line;
2435 Switch : String;
2436 Parameter : String;
2437 Section : String := "")
2439 procedure Remove_Simple_Switch
2440 (Simple, Separator, Param : String; Index : Integer);
2441 -- Removes a simple switch, with no aliasing or grouping
2443 --------------------------
2444 -- Remove_Simple_Switch --
2445 --------------------------
2447 procedure Remove_Simple_Switch
2448 (Simple, Separator, Param : String; Index : Integer)
2450 pragma Unreferenced (Separator, Index);
2451 C : Integer;
2453 begin
2454 if Cmd.Expanded /= null then
2455 C := Cmd.Expanded'First;
2456 while C <= Cmd.Expanded'Last loop
2457 if Cmd.Expanded (C).all = Simple
2458 and then
2459 ((Cmd.Sections (C) = null
2460 and then Section = "")
2461 or else
2462 (Cmd.Sections (C) /= null
2463 and then Section = Cmd.Sections (C).all))
2464 and then
2465 ((Cmd.Params (C) = null and then Param = "")
2466 or else
2467 (Cmd.Params (C) /= null
2468 and then
2470 -- Ignore the separator stored in Parameter
2472 Cmd.Params (C) (Cmd.Params (C)'First + 1
2473 .. Cmd.Params (C)'Last) =
2474 Param))
2475 then
2476 Remove (Cmd.Expanded, C);
2477 Remove (Cmd.Params, C);
2478 Remove (Cmd.Sections, C);
2480 -- The switch is necessarily unique by construction of
2481 -- Add_Switch.
2483 return;
2485 else
2486 C := C + 1;
2487 end if;
2488 end loop;
2489 end if;
2490 end Remove_Simple_Switch;
2492 procedure Remove_Simple_Switches is
2493 new For_Each_Simple_Switch (Remove_Simple_Switch);
2495 -- Start of processing for Remove_Switch
2497 begin
2498 Remove_Simple_Switches (Cmd.Config, Section, Switch, Parameter);
2499 Free (Cmd.Coalesce);
2500 end Remove_Switch;
2502 --------------------
2503 -- Group_Switches --
2504 --------------------
2506 procedure Group_Switches
2507 (Cmd : Command_Line;
2508 Result : Argument_List_Access;
2509 Sections : Argument_List_Access;
2510 Params : Argument_List_Access)
2512 function Compatible_Parameter (Param : String_Access) return Boolean;
2513 -- True when the parameter can be part of a group
2515 --------------------------
2516 -- Compatible_Parameter --
2517 --------------------------
2519 function Compatible_Parameter (Param : String_Access) return Boolean is
2520 begin
2521 -- No parameter OK
2523 if Param = null then
2524 return True;
2526 -- We need parameters without separators
2528 elsif Param (Param'First) /= ASCII.NUL then
2529 return False;
2531 -- Parameters must be all digits
2533 else
2534 for J in Param'First + 1 .. Param'Last loop
2535 if Param (J) not in '0' .. '9' then
2536 return False;
2537 end if;
2538 end loop;
2540 return True;
2541 end if;
2542 end Compatible_Parameter;
2544 -- Local declarations
2546 Group : Ada.Strings.Unbounded.Unbounded_String;
2547 First : Natural;
2548 use type Ada.Strings.Unbounded.Unbounded_String;
2550 -- Start of processing for Group_Switches
2552 begin
2553 if Cmd.Config = null
2554 or else Cmd.Config.Prefixes = null
2555 then
2556 return;
2557 end if;
2559 for P in Cmd.Config.Prefixes'Range loop
2560 Group := Ada.Strings.Unbounded.Null_Unbounded_String;
2561 First := 0;
2563 for C in Result'Range loop
2564 if Result (C) /= null
2565 and then Compatible_Parameter (Params (C))
2566 and then Looking_At
2567 (Result (C).all,
2568 Result (C)'First,
2569 Cmd.Config.Prefixes (P).all)
2570 then
2571 -- If we are still in the same section, group the switches
2573 if First = 0
2574 or else
2575 (Sections (C) = null
2576 and then Sections (First) = null)
2577 or else
2578 (Sections (C) /= null
2579 and then Sections (First) /= null
2580 and then Sections (C).all = Sections (First).all)
2581 then
2582 Group :=
2583 Group &
2584 Result (C)
2585 (Result (C)'First + Cmd.Config.Prefixes (P)'Length ..
2586 Result (C)'Last);
2588 if Params (C) /= null then
2589 Group :=
2590 Group &
2591 Params (C) (Params (C)'First + 1 .. Params (C)'Last);
2592 Free (Params (C));
2593 end if;
2595 if First = 0 then
2596 First := C;
2597 end if;
2599 Free (Result (C));
2601 -- We changed section: we put the grouped switches to the first
2602 -- place, on continue with the new section.
2604 else
2605 Result (First) :=
2606 new String'
2607 (Cmd.Config.Prefixes (P).all &
2608 Ada.Strings.Unbounded.To_String (Group));
2609 Group :=
2610 Ada.Strings.Unbounded.To_Unbounded_String
2611 (Result (C)
2612 (Result (C)'First + Cmd.Config.Prefixes (P)'Length ..
2613 Result (C)'Last));
2614 First := C;
2615 end if;
2616 end if;
2617 end loop;
2619 if First > 0 then
2620 Result (First) :=
2621 new String'
2622 (Cmd.Config.Prefixes (P).all &
2623 Ada.Strings.Unbounded.To_String (Group));
2624 end if;
2625 end loop;
2626 end Group_Switches;
2628 --------------------
2629 -- Alias_Switches --
2630 --------------------
2632 procedure Alias_Switches
2633 (Cmd : Command_Line;
2634 Result : Argument_List_Access;
2635 Params : Argument_List_Access)
2637 Found : Boolean;
2638 First : Natural;
2640 procedure Check_Cb (Switch, Separator, Param : String; Index : Integer);
2641 -- Checks whether the command line contains [Switch].
2642 -- Sets the global variable [Found] appropriately.
2643 -- This will be called for each simple switch that make up an alias, to
2644 -- know whether the alias should be applied.
2646 procedure Remove_Cb (Switch, Separator, Param : String; Index : Integer);
2647 -- Remove the simple switch [Switch] from the command line, since it is
2648 -- part of a simpler alias
2650 --------------
2651 -- Check_Cb --
2652 --------------
2654 procedure Check_Cb
2655 (Switch, Separator, Param : String; Index : Integer)
2657 pragma Unreferenced (Separator, Index);
2659 begin
2660 if Found then
2661 for E in Result'Range loop
2662 if Result (E) /= null
2663 and then
2664 (Params (E) = null
2665 or else Params (E) (Params (E)'First + 1 ..
2666 Params (E)'Last) = Param)
2667 and then Result (E).all = Switch
2668 then
2669 return;
2670 end if;
2671 end loop;
2673 Found := False;
2674 end if;
2675 end Check_Cb;
2677 ---------------
2678 -- Remove_Cb --
2679 ---------------
2681 procedure Remove_Cb (Switch, Separator, Param : String; Index : Integer)
2683 pragma Unreferenced (Separator, Index);
2685 begin
2686 for E in Result'Range loop
2687 if Result (E) /= null
2688 and then
2689 (Params (E) = null
2690 or else Params (E) (Params (E)'First + 1
2691 .. Params (E)'Last) = Param)
2692 and then Result (E).all = Switch
2693 then
2694 if First > E then
2695 First := E;
2696 end if;
2698 Free (Result (E));
2699 Free (Params (E));
2700 return;
2701 end if;
2702 end loop;
2703 end Remove_Cb;
2705 procedure Check_All is new For_Each_Simple_Switch (Check_Cb);
2706 procedure Remove_All is new For_Each_Simple_Switch (Remove_Cb);
2708 -- Start of processing for Alias_Switches
2710 begin
2711 if Cmd.Config = null
2712 or else Cmd.Config.Aliases = null
2713 then
2714 return;
2715 end if;
2717 for A in Cmd.Config.Aliases'Range loop
2719 -- Compute the various simple switches that make up the alias. We
2720 -- split the expansion into as many simple switches as possible, and
2721 -- then check whether the expanded command line has all of them.
2723 Found := True;
2724 Check_All (Cmd.Config,
2725 Switch => Cmd.Config.Aliases (A).Expansion.all,
2726 Section => Cmd.Config.Aliases (A).Section.all);
2728 if Found then
2729 First := Integer'Last;
2730 Remove_All (Cmd.Config,
2731 Switch => Cmd.Config.Aliases (A).Expansion.all,
2732 Section => Cmd.Config.Aliases (A).Section.all);
2733 Result (First) := new String'(Cmd.Config.Aliases (A).Alias.all);
2734 end if;
2735 end loop;
2736 end Alias_Switches;
2738 -------------------
2739 -- Sort_Sections --
2740 -------------------
2742 procedure Sort_Sections
2743 (Line : GNAT.OS_Lib.Argument_List_Access;
2744 Sections : GNAT.OS_Lib.Argument_List_Access;
2745 Params : GNAT.OS_Lib.Argument_List_Access)
2747 Sections_List : Argument_List_Access :=
2748 new Argument_List'(1 .. 1 => null);
2749 Found : Boolean;
2750 Old_Line : constant Argument_List := Line.all;
2751 Old_Sections : constant Argument_List := Sections.all;
2752 Old_Params : constant Argument_List := Params.all;
2753 Index : Natural;
2755 begin
2756 if Line = null then
2757 return;
2758 end if;
2760 -- First construct a list of all sections
2762 for E in Line'Range loop
2763 if Sections (E) /= null then
2764 Found := False;
2765 for S in Sections_List'Range loop
2766 if (Sections_List (S) = null and then Sections (E) = null)
2767 or else
2768 (Sections_List (S) /= null
2769 and then Sections (E) /= null
2770 and then Sections_List (S).all = Sections (E).all)
2771 then
2772 Found := True;
2773 exit;
2774 end if;
2775 end loop;
2777 if not Found then
2778 Add (Sections_List, Sections (E));
2779 end if;
2780 end if;
2781 end loop;
2783 Index := Line'First;
2785 for S in Sections_List'Range loop
2786 for E in Old_Line'Range loop
2787 if (Sections_List (S) = null and then Old_Sections (E) = null)
2788 or else
2789 (Sections_List (S) /= null
2790 and then Old_Sections (E) /= null
2791 and then Sections_List (S).all = Old_Sections (E).all)
2792 then
2793 Line (Index) := Old_Line (E);
2794 Sections (Index) := Old_Sections (E);
2795 Params (Index) := Old_Params (E);
2796 Index := Index + 1;
2797 end if;
2798 end loop;
2799 end loop;
2801 Unchecked_Free (Sections_List);
2802 end Sort_Sections;
2804 -----------
2805 -- Start --
2806 -----------
2808 procedure Start
2809 (Cmd : in out Command_Line;
2810 Iter : in out Command_Line_Iterator;
2811 Expanded : Boolean := False)
2813 begin
2814 if Cmd.Expanded = null then
2815 Iter.List := null;
2816 return;
2817 end if;
2819 -- Reorder the expanded line so that sections are grouped
2821 Sort_Sections (Cmd.Expanded, Cmd.Sections, Cmd.Params);
2823 -- Coalesce the switches as much as possible
2825 if not Expanded
2826 and then Cmd.Coalesce = null
2827 then
2828 Cmd.Coalesce := new Argument_List (Cmd.Expanded'Range);
2829 for E in Cmd.Expanded'Range loop
2830 Cmd.Coalesce (E) := new String'(Cmd.Expanded (E).all);
2831 end loop;
2833 Free (Cmd.Coalesce_Sections);
2834 Cmd.Coalesce_Sections := new Argument_List (Cmd.Sections'Range);
2835 for E in Cmd.Sections'Range loop
2836 Cmd.Coalesce_Sections (E) :=
2837 (if Cmd.Sections (E) = null then null
2838 else new String'(Cmd.Sections (E).all));
2839 end loop;
2841 Free (Cmd.Coalesce_Params);
2842 Cmd.Coalesce_Params := new Argument_List (Cmd.Params'Range);
2843 for E in Cmd.Params'Range loop
2844 Cmd.Coalesce_Params (E) :=
2845 (if Cmd.Params (E) = null then null
2846 else new String'(Cmd.Params (E).all));
2847 end loop;
2849 -- Not a clone, since we will not modify the parameters anyway
2851 Alias_Switches (Cmd, Cmd.Coalesce, Cmd.Coalesce_Params);
2852 Group_Switches
2853 (Cmd, Cmd.Coalesce, Cmd.Coalesce_Sections, Cmd.Coalesce_Params);
2854 end if;
2856 if Expanded then
2857 Iter.List := Cmd.Expanded;
2858 Iter.Params := Cmd.Params;
2859 Iter.Sections := Cmd.Sections;
2860 else
2861 Iter.List := Cmd.Coalesce;
2862 Iter.Params := Cmd.Coalesce_Params;
2863 Iter.Sections := Cmd.Coalesce_Sections;
2864 end if;
2866 if Iter.List = null then
2867 Iter.Current := Integer'Last;
2868 else
2869 Iter.Current := Iter.List'First - 1;
2870 Next (Iter);
2871 end if;
2872 end Start;
2874 --------------------
2875 -- Current_Switch --
2876 --------------------
2878 function Current_Switch (Iter : Command_Line_Iterator) return String is
2879 begin
2880 return Iter.List (Iter.Current).all;
2881 end Current_Switch;
2883 --------------------
2884 -- Is_New_Section --
2885 --------------------
2887 function Is_New_Section (Iter : Command_Line_Iterator) return Boolean is
2888 Section : constant String := Current_Section (Iter);
2890 begin
2891 if Iter.Sections = null then
2892 return False;
2894 elsif Iter.Current = Iter.Sections'First
2895 or else Iter.Sections (Iter.Current - 1) = null
2896 then
2897 return Section /= "";
2899 else
2900 return Section /= Iter.Sections (Iter.Current - 1).all;
2901 end if;
2902 end Is_New_Section;
2904 ---------------------
2905 -- Current_Section --
2906 ---------------------
2908 function Current_Section (Iter : Command_Line_Iterator) return String is
2909 begin
2910 if Iter.Sections = null
2911 or else Iter.Current > Iter.Sections'Last
2912 or else Iter.Sections (Iter.Current) = null
2913 then
2914 return "";
2915 end if;
2917 return Iter.Sections (Iter.Current).all;
2918 end Current_Section;
2920 -----------------------
2921 -- Current_Separator --
2922 -----------------------
2924 function Current_Separator (Iter : Command_Line_Iterator) return String is
2925 begin
2926 if Iter.Params = null
2927 or else Iter.Current > Iter.Params'Last
2928 or else Iter.Params (Iter.Current) = null
2929 then
2930 return "";
2932 else
2933 declare
2934 Sep : constant Character :=
2935 Iter.Params (Iter.Current) (Iter.Params (Iter.Current)'First);
2936 begin
2937 if Sep = ASCII.NUL then
2938 return "";
2939 else
2940 return "" & Sep;
2941 end if;
2942 end;
2943 end if;
2944 end Current_Separator;
2946 -----------------------
2947 -- Current_Parameter --
2948 -----------------------
2950 function Current_Parameter (Iter : Command_Line_Iterator) return String is
2951 begin
2952 if Iter.Params = null
2953 or else Iter.Current > Iter.Params'Last
2954 or else Iter.Params (Iter.Current) = null
2955 then
2956 return "";
2958 else
2959 -- Return result, skipping separator
2961 declare
2962 P : constant String := Iter.Params (Iter.Current).all;
2963 begin
2964 return P (P'First + 1 .. P'Last);
2965 end;
2966 end if;
2967 end Current_Parameter;
2969 --------------
2970 -- Has_More --
2971 --------------
2973 function Has_More (Iter : Command_Line_Iterator) return Boolean is
2974 begin
2975 return Iter.List /= null and then Iter.Current <= Iter.List'Last;
2976 end Has_More;
2978 ----------
2979 -- Next --
2980 ----------
2982 procedure Next (Iter : in out Command_Line_Iterator) is
2983 begin
2984 Iter.Current := Iter.Current + 1;
2985 while Iter.Current <= Iter.List'Last
2986 and then Iter.List (Iter.Current) = null
2987 loop
2988 Iter.Current := Iter.Current + 1;
2989 end loop;
2990 end Next;
2992 ----------
2993 -- Free --
2994 ----------
2996 procedure Free (Config : in out Command_Line_Configuration) is
2997 procedure Unchecked_Free is new
2998 Ada.Unchecked_Deallocation
2999 (Switch_Definitions, Switch_Definitions_List);
3001 procedure Unchecked_Free is new
3002 Ada.Unchecked_Deallocation
3003 (Alias_Definitions, Alias_Definitions_List);
3005 begin
3006 if Config /= null then
3007 Free (Config.Prefixes);
3008 Free (Config.Sections);
3009 Free (Config.Usage);
3010 Free (Config.Help);
3011 Free (Config.Help_Msg);
3013 if Config.Aliases /= null then
3014 for A in Config.Aliases'Range loop
3015 Free (Config.Aliases (A).Alias);
3016 Free (Config.Aliases (A).Expansion);
3017 Free (Config.Aliases (A).Section);
3018 end loop;
3020 Unchecked_Free (Config.Aliases);
3021 end if;
3023 if Config.Switches /= null then
3024 for S in Config.Switches'Range loop
3025 Free (Config.Switches (S).Switch);
3026 Free (Config.Switches (S).Long_Switch);
3027 Free (Config.Switches (S).Help);
3028 Free (Config.Switches (S).Section);
3029 end loop;
3031 Unchecked_Free (Config.Switches);
3032 end if;
3034 Unchecked_Free (Config);
3035 end if;
3036 end Free;
3038 ----------
3039 -- Free --
3040 ----------
3042 procedure Free (Cmd : in out Command_Line) is
3043 begin
3044 Free (Cmd.Expanded);
3045 Free (Cmd.Coalesce);
3046 Free (Cmd.Coalesce_Sections);
3047 Free (Cmd.Coalesce_Params);
3048 Free (Cmd.Params);
3049 Free (Cmd.Sections);
3050 end Free;
3052 ---------------
3053 -- Set_Usage --
3054 ---------------
3056 procedure Set_Usage
3057 (Config : in out Command_Line_Configuration;
3058 Usage : String := "[switches] [arguments]";
3059 Help : String := "";
3060 Help_Msg : String := "")
3062 begin
3063 if Config = null then
3064 Config := new Command_Line_Configuration_Record;
3065 end if;
3067 Free (Config.Usage);
3068 Free (Config.Help);
3069 Free (Config.Help_Msg);
3071 Config.Usage := new String'(Usage);
3072 Config.Help := new String'(Help);
3073 Config.Help_Msg := new String'(Help_Msg);
3074 end Set_Usage;
3076 ------------------
3077 -- Display_Help --
3078 ------------------
3080 procedure Display_Help (Config : Command_Line_Configuration) is
3081 function Switch_Name
3082 (Def : Switch_Definition;
3083 Section : String) return String;
3084 -- Return the "-short, --long=ARG" string for Def.
3085 -- Returns "" if the switch is not in the section.
3087 function Param_Name
3088 (P : Switch_Parameter_Type;
3089 Name : String := "ARG") return String;
3090 -- Return the display for a switch parameter
3092 procedure Display_Section_Help (Section : String);
3093 -- Display the help for a specific section ("" is the default section)
3095 --------------------------
3096 -- Display_Section_Help --
3097 --------------------------
3099 procedure Display_Section_Help (Section : String) is
3100 Max_Len : Natural := 0;
3102 begin
3103 -- ??? Special display for "*"
3105 New_Line;
3107 if Section /= "" then
3108 Put_Line ("Switches after " & Section);
3109 end if;
3111 -- Compute size of the switches column
3113 for S in Config.Switches'Range loop
3114 Max_Len := Natural'Max
3115 (Max_Len, Switch_Name (Config.Switches (S), Section)'Length);
3116 end loop;
3118 if Config.Aliases /= null then
3119 for A in Config.Aliases'Range loop
3120 if Config.Aliases (A).Section.all = Section then
3121 Max_Len := Natural'Max
3122 (Max_Len, Config.Aliases (A).Alias'Length);
3123 end if;
3124 end loop;
3125 end if;
3127 -- Display the switches
3129 for S in Config.Switches'Range loop
3130 declare
3131 N : constant String :=
3132 Switch_Name (Config.Switches (S), Section);
3134 begin
3135 if N /= "" then
3136 Put (" ");
3137 Put (N);
3138 Put ((1 .. Max_Len - N'Length + 1 => ' '));
3140 if Config.Switches (S).Help /= null then
3141 Put (Config.Switches (S).Help.all);
3142 end if;
3144 New_Line;
3145 end if;
3146 end;
3147 end loop;
3149 -- Display the aliases
3151 if Config.Aliases /= null then
3152 for A in Config.Aliases'Range loop
3153 if Config.Aliases (A).Section.all = Section then
3154 Put (" ");
3155 Put (Config.Aliases (A).Alias.all);
3156 Put ((1 .. Max_Len - Config.Aliases (A).Alias'Length + 1
3157 => ' '));
3158 Put ("Equivalent to " & Config.Aliases (A).Expansion.all);
3159 New_Line;
3160 end if;
3161 end loop;
3162 end if;
3163 end Display_Section_Help;
3165 ----------------
3166 -- Param_Name --
3167 ----------------
3169 function Param_Name
3170 (P : Switch_Parameter_Type;
3171 Name : String := "ARG") return String
3173 begin
3174 case P is
3175 when Parameter_None =>
3176 return "";
3178 when Parameter_With_Optional_Space =>
3179 return " " & To_Upper (Name);
3181 when Parameter_With_Space_Or_Equal =>
3182 return "=" & To_Upper (Name);
3184 when Parameter_No_Space =>
3185 return To_Upper (Name);
3187 when Parameter_Optional =>
3188 return '[' & To_Upper (Name) & ']';
3189 end case;
3190 end Param_Name;
3192 -----------------
3193 -- Switch_Name --
3194 -----------------
3196 function Switch_Name
3197 (Def : Switch_Definition;
3198 Section : String) return String
3200 use Ada.Strings.Unbounded;
3201 Result : Unbounded_String;
3202 P1, P2 : Switch_Parameter_Type;
3203 Last1, Last2 : Integer := 0;
3205 begin
3206 if (Section = "" and then Def.Section = null)
3207 or else (Def.Section /= null and then Def.Section.all = Section)
3208 then
3209 if Def.Switch /= null and then Def.Switch.all = "*" then
3210 return "[any switch]";
3211 end if;
3213 if Def.Switch /= null then
3214 Decompose_Switch (Def.Switch.all, P1, Last1);
3215 Append (Result, Def.Switch (Def.Switch'First .. Last1));
3217 if Def.Long_Switch /= null then
3218 Decompose_Switch (Def.Long_Switch.all, P2, Last2);
3219 Append (Result, ", "
3220 & Def.Long_Switch (Def.Long_Switch'First .. Last2));
3222 if Def.Argument = null then
3223 Append (Result, Param_Name (P2, "ARG"));
3224 else
3225 Append (Result, Param_Name (P2, Def.Argument.all));
3226 end if;
3228 else
3229 if Def.Argument = null then
3230 Append (Result, Param_Name (P1, "ARG"));
3231 else
3232 Append (Result, Param_Name (P1, Def.Argument.all));
3233 end if;
3234 end if;
3236 -- Def.Switch is null (Long_Switch must be non-null)
3238 else
3239 Decompose_Switch (Def.Long_Switch.all, P2, Last2);
3240 Append (Result,
3241 Def.Long_Switch (Def.Long_Switch'First .. Last2));
3243 if Def.Argument = null then
3244 Append (Result, Param_Name (P2, "ARG"));
3245 else
3246 Append (Result, Param_Name (P2, Def.Argument.all));
3247 end if;
3248 end if;
3249 end if;
3251 return To_String (Result);
3252 end Switch_Name;
3254 -- Start of processing for Display_Help
3256 begin
3257 if Config = null then
3258 return;
3259 end if;
3261 if Config.Help /= null and then Config.Help.all /= "" then
3262 Put_Line (Config.Help.all);
3263 end if;
3265 if Config.Usage /= null then
3266 Put_Line ("Usage: "
3267 & Base_Name
3268 (Ada.Command_Line.Command_Name) & " " & Config.Usage.all);
3269 else
3270 Put_Line ("Usage: " & Base_Name (Ada.Command_Line.Command_Name)
3271 & " [switches] [arguments]");
3272 end if;
3274 if Config.Help_Msg /= null and then Config.Help_Msg.all /= "" then
3275 Put_Line (Config.Help_Msg.all);
3277 else
3278 Display_Section_Help ("");
3280 if Config.Sections /= null and then Config.Switches /= null then
3281 for S in Config.Sections'Range loop
3282 Display_Section_Help (Config.Sections (S).all);
3283 end loop;
3284 end if;
3285 end if;
3286 end Display_Help;
3288 ------------
3289 -- Getopt --
3290 ------------
3292 procedure Getopt
3293 (Config : Command_Line_Configuration;
3294 Callback : Switch_Handler := null;
3295 Parser : Opt_Parser := Command_Line_Parser;
3296 Concatenate : Boolean := True)
3298 Getopt_Switches : String_Access;
3299 C : Character := ASCII.NUL;
3301 Empty_Name : aliased constant String := "";
3302 Current_Section : Integer := -1;
3303 Section_Name : not null access constant String := Empty_Name'Access;
3305 procedure Simple_Callback
3306 (Simple_Switch : String;
3307 Separator : String;
3308 Parameter : String;
3309 Index : Integer);
3310 -- Needs comments ???
3312 procedure Do_Callback (Switch, Parameter : String; Index : Integer);
3314 -----------------
3315 -- Do_Callback --
3316 -----------------
3318 procedure Do_Callback (Switch, Parameter : String; Index : Integer) is
3319 begin
3320 -- Do automatic handling when possible
3322 if Index /= -1 then
3323 case Config.Switches (Index).Typ is
3324 when Switch_Untyped =>
3325 null; -- no automatic handling
3327 when Switch_Boolean =>
3328 Config.Switches (Index).Boolean_Output.all :=
3329 Config.Switches (Index).Boolean_Value;
3330 return;
3332 when Switch_Integer =>
3333 begin
3334 if Parameter = "" then
3335 Config.Switches (Index).Integer_Output.all :=
3336 Config.Switches (Index).Integer_Default;
3337 else
3338 Config.Switches (Index).Integer_Output.all :=
3339 Integer'Value (Parameter);
3340 end if;
3342 exception
3343 when Constraint_Error =>
3344 raise Invalid_Parameter
3345 with "Expected integer parameter for '"
3346 & Switch & "'";
3347 end;
3349 return;
3351 when Switch_String =>
3352 Free (Config.Switches (Index).String_Output.all);
3353 Config.Switches (Index).String_Output.all :=
3354 new String'(Parameter);
3355 return;
3357 end case;
3358 end if;
3360 -- Otherwise calls the user callback if one was defined
3362 if Callback /= null then
3363 Callback (Switch => Switch,
3364 Parameter => Parameter,
3365 Section => Section_Name.all);
3366 end if;
3367 end Do_Callback;
3369 procedure For_Each_Simple
3370 is new For_Each_Simple_Switch (Simple_Callback);
3372 ---------------------
3373 -- Simple_Callback --
3374 ---------------------
3376 procedure Simple_Callback
3377 (Simple_Switch : String;
3378 Separator : String;
3379 Parameter : String;
3380 Index : Integer)
3382 pragma Unreferenced (Separator);
3383 begin
3384 Do_Callback (Switch => Simple_Switch,
3385 Parameter => Parameter,
3386 Index => Index);
3387 end Simple_Callback;
3389 -- Start of processing for Getopt
3391 begin
3392 -- Initialize sections
3394 if Config.Sections = null then
3395 Config.Sections := new Argument_List'(1 .. 0 => null);
3396 end if;
3398 Internal_Initialize_Option_Scan
3399 (Parser => Parser,
3400 Switch_Char => Parser.Switch_Character,
3401 Stop_At_First_Non_Switch => Parser.Stop_At_First,
3402 Section_Delimiters => Section_Delimiters (Config));
3404 Getopt_Switches := new String'
3405 (Get_Switches (Config, Parser.Switch_Character, Section_Name.all)
3406 & " h -help");
3408 -- Initialize output values for automatically handled switches
3410 for S in Config.Switches'Range loop
3411 case Config.Switches (S).Typ is
3412 when Switch_Untyped =>
3413 null; -- Nothing to do
3415 when Switch_Boolean =>
3416 Config.Switches (S).Boolean_Output.all :=
3417 not Config.Switches (S).Boolean_Value;
3419 when Switch_Integer =>
3420 Config.Switches (S).Integer_Output.all :=
3421 Config.Switches (S).Integer_Initial;
3423 when Switch_String =>
3424 if Config.Switches (S).String_Output.all = null then
3425 Config.Switches (S).String_Output.all := new String'("");
3426 end if;
3427 end case;
3428 end loop;
3430 -- For all sections, and all switches within those sections
3432 loop
3433 C := Getopt (Switches => Getopt_Switches.all,
3434 Concatenate => Concatenate,
3435 Parser => Parser);
3437 if C = '*' then
3438 -- Full_Switch already includes the leading '-'
3440 Do_Callback (Switch => Full_Switch (Parser),
3441 Parameter => Parameter (Parser),
3442 Index => -1);
3444 elsif C /= ASCII.NUL then
3445 if Full_Switch (Parser) = "h"
3446 or else
3447 Full_Switch (Parser) = "-help"
3448 then
3449 Display_Help (Config);
3450 raise Exit_From_Command_Line;
3451 end if;
3453 -- Do switch expansion if needed
3455 For_Each_Simple
3456 (Config,
3457 Section => Section_Name.all,
3458 Switch => Parser.Switch_Character & Full_Switch (Parser),
3459 Parameter => Parameter (Parser));
3461 else
3462 if Current_Section = -1 then
3463 Current_Section := Config.Sections'First;
3464 else
3465 Current_Section := Current_Section + 1;
3466 end if;
3468 exit when Current_Section > Config.Sections'Last;
3470 Section_Name := Config.Sections (Current_Section);
3471 Goto_Section (Section_Name.all, Parser);
3473 Free (Getopt_Switches);
3474 Getopt_Switches := new String'
3475 (Get_Switches
3476 (Config, Parser.Switch_Character, Section_Name.all));
3477 end if;
3478 end loop;
3480 Free (Getopt_Switches);
3482 exception
3483 when Invalid_Switch =>
3484 Free (Getopt_Switches);
3486 -- Message inspired by "ls" on Unix
3488 Put_Line (Standard_Error,
3489 Base_Name (Ada.Command_Line.Command_Name)
3490 & ": unrecognized option '"
3491 & Parser.Switch_Character & Full_Switch (Parser)
3492 & "'");
3493 Put_Line (Standard_Error,
3494 "Try `"
3495 & Base_Name (Ada.Command_Line.Command_Name)
3496 & " --help` for more information.");
3498 raise;
3500 when others =>
3501 Free (Getopt_Switches);
3502 raise;
3503 end Getopt;
3505 -----------
3506 -- Build --
3507 -----------
3509 procedure Build
3510 (Line : in out Command_Line;
3511 Args : out GNAT.OS_Lib.Argument_List_Access;
3512 Expanded : Boolean := False;
3513 Switch_Char : Character := '-')
3515 Iter : Command_Line_Iterator;
3516 Count : Natural := 0;
3518 begin
3519 Start (Line, Iter, Expanded => Expanded);
3520 while Has_More (Iter) loop
3521 if Is_New_Section (Iter) then
3522 Count := Count + 1;
3523 end if;
3525 Count := Count + 1;
3526 Next (Iter);
3527 end loop;
3529 Args := new Argument_List (1 .. Count);
3530 Count := Args'First;
3532 Start (Line, Iter, Expanded => Expanded);
3533 while Has_More (Iter) loop
3534 if Is_New_Section (Iter) then
3535 Args (Count) := new String'(Switch_Char & Current_Section (Iter));
3536 Count := Count + 1;
3537 end if;
3539 Args (Count) := new String'(Current_Switch (Iter)
3540 & Current_Separator (Iter)
3541 & Current_Parameter (Iter));
3542 Count := Count + 1;
3543 Next (Iter);
3544 end loop;
3545 end Build;
3547 end GNAT.Command_Line;