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