1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
5 -- G N A T . C O M M A N D _ L I N E --
11 -- Copyright (C) 1999-2001 Free Software Foundation, Inc. --
13 -- GNAT is free software; you can redistribute it and/or modify it under --
14 -- terms of the GNU General Public License as published by the Free Soft- --
15 -- ware Foundation; either version 2, or (at your option) any later ver- --
16 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
17 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
18 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
19 -- for more details. You should have received a copy of the GNU General --
20 -- Public License distributed with GNAT; see file COPYING. If not, write --
21 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
22 -- MA 02111-1307, USA. --
24 -- As a special exception, if other files instantiate generics from this --
25 -- unit, or you link this unit with other files to produce an executable, --
26 -- this unit does not by itself cause the resulting executable to be --
27 -- covered by the GNU General Public License. This exception does not --
28 -- however invalidate any other reasons why the executable file might be --
29 -- covered by the GNU Public License. --
31 -- GNAT is maintained by Ada Core Technologies Inc (http://www.gnat.com). --
33 ------------------------------------------------------------------------------
35 with Ada.Command_Line;
37 package body GNAT.Command_Line is
39 package CL renames Ada.Command_Line;
41 type Section_Number is new Natural range 0 .. 65534;
42 for Section_Number'Size use 16;
44 type Parameter_Type is
50 The_Parameter : Parameter_Type;
51 The_Switch : Parameter_Type;
52 -- This type and this variable are provided to store the current switch
55 type Is_Switch_Type is array (1 .. CL.Argument_Count) of Boolean;
56 pragma Pack (Is_Switch_Type);
58 Is_Switch : Is_Switch_Type := (others => False);
59 -- Indicates wich arguments on the command line are considered not be
60 -- switches or parameters to switches (this leaves e.g. the filenames...)
62 type Section_Type is array (1 .. CL.Argument_Count + 1) of Section_Number;
63 pragma Pack (Section_Type);
64 Section : Section_Type := (others => 1);
65 -- Contains the number of the section associated with the current
66 -- switch. If this number is 0, then it is a section delimiter, which
67 -- is never returns by GetOpt.
68 -- The last element of this array is set to 0 to avoid the need to test for
69 -- if we have reached the end of the command line in loops.
71 Current_Argument : Natural := 1;
72 -- Number of the current argument parsed on the command line
74 Current_Index : Natural := 1;
75 -- Index in the current argument of the character to be processed
77 Current_Section : Section_Number := 1;
79 Expansion_It : aliased Expansion_Iterator;
80 -- When Get_Argument is expanding a file name, this is the iterator used
82 In_Expansion : Boolean := False;
83 -- True if we are expanding a file
85 Switch_Character : Character := '-';
86 -- The character at the beginning of the command line arguments,
87 -- indicating the beginning of a switch
89 Stop_At_First : Boolean := False;
90 -- If it is True then Getopt stops at the first non-switch argument
92 procedure Set_Parameter
93 (Variable : out Parameter_Type;
97 pragma Inline (Set_Parameter);
98 -- Set the parameter that will be returned by Parameter below
100 function Goto_Next_Argument_In_Section return Boolean;
101 -- Go to the next argument on the command line. If we are at the end
102 -- of the current section, we want to make sure there is no other
103 -- identical section on the command line (there might be multiple
104 -- instances of -largs).
105 -- Return True if there as another argument, False otherwise
111 function Expansion (Iterator : Expansion_Iterator) return String is
112 use GNAT.Directory_Operations;
113 type Pointer is access all Expansion_Iterator;
115 S : String (1 .. 1024);
117 It : Pointer := Iterator'Unrestricted_Access;
121 Read (It.Dir, S, Last);
125 return String'(1 .. 0 => ' ');
128 if GNAT.Regexp.Match (S (1 .. Last), Iterator.Regexp) then
129 return S (1 .. Last);
134 return String'(1 .. 0 => ' ');
141 function Full_Switch return String is
143 return CL.Argument (The_Switch.Arg_Num)
144 (The_Switch.First .. The_Switch.Last);
151 function Get_Argument (Do_Expansion : Boolean := False) return String is
152 Total : constant Natural := CL.Argument_Count;
157 S : String := Expansion (Expansion_It);
159 if S'Length /= 0 then
162 In_Expansion := False;
168 if Current_Argument > Total then
170 -- If this is the first time this function is called
172 if Current_Index = 1 then
173 Current_Argument := 1;
174 while Current_Argument <= CL.Argument_Count
175 and then Section (Current_Argument) /= Current_Section
177 Current_Argument := Current_Argument + 1;
180 return String'(1 .. 0 => ' ');
183 elsif Section (Current_Argument) = 0 then
184 while Current_Argument <= CL.Argument_Count
185 and then Section (Current_Argument) /= Current_Section
187 Current_Argument := Current_Argument + 1;
193 while Current_Argument <= Total
194 and then Is_Switch (Current_Argument)
196 Current_Argument := Current_Argument + 1;
199 if Current_Argument > Total then
200 return String'(1 .. 0 => ' ');
203 if Section (Current_Argument) = 0 then
204 return Get_Argument (Do_Expansion);
207 Current_Argument := Current_Argument + 1;
209 -- Could it be a file name with wild cards to expand ?
213 Arg : String renames CL.Argument (Current_Argument - 1);
214 Index : Positive := Arg'First;
217 while Index <= Arg'Last loop
220 or else Arg (Index) = '?'
221 or else Arg (Index) = '['
223 In_Expansion := True;
224 Start_Expansion (Expansion_It, Arg);
225 return Get_Argument (Do_Expansion);
233 return CL.Argument (Current_Argument - 1);
240 function Getopt (Switches : String) return Character is
244 -- If we have finished to parse the current command line item (there
245 -- might be multiple switches in a single item), then go to the next
248 if Current_Argument > CL.Argument_Count
249 or else (Current_Index > CL.Argument (Current_Argument)'Last
250 and then not Goto_Next_Argument_In_Section)
255 -- If we are on a new item, test if this might be a switch
257 if Current_Index = 1 then
258 if CL.Argument (Current_Argument)(1) /= Switch_Character then
259 if Switches (Switches'First) = '*' then
260 Set_Parameter (The_Switch,
261 Arg_Num => Current_Argument,
263 Last => CL.Argument (Current_Argument)'Last);
264 Is_Switch (Current_Argument) := True;
265 Dummy := Goto_Next_Argument_In_Section;
269 if Stop_At_First then
270 Current_Argument := Positive'Last;
273 elsif not Goto_Next_Argument_In_Section then
277 return Getopt (Switches);
282 Is_Switch (Current_Argument) := True;
286 Arg : String renames CL.Argument (Current_Argument);
287 Index_Switches : Natural := 0;
288 Max_Length : Natural := 0;
289 Index : Natural := Switches'First;
290 Length : Natural := 1;
294 while Index <= Switches'Last loop
296 -- Search the length of the parameter at this position in Switches
299 while Length <= Switches'Last
300 and then Switches (Length) /= ' '
302 Length := Length + 1;
305 if (Switches (Length - 1) = ':'
306 or else Switches (Length - 1) = '?'
307 or else Switches (Length - 1) = '!')
308 and then Length > Index + 1
310 Length := Length - 1;
313 -- If it is the one we searched, it may be a candidate
315 if Current_Index + Length - 1 - Index <= Arg'Last
317 Switches (Index .. Length - 1) =
318 Arg (Current_Index .. Current_Index + Length - 1 - Index)
319 and then Length - Index > Max_Length
321 Index_Switches := Index;
322 Max_Length := Length - Index;
325 -- Look for the next switch in Switches
326 while Index <= Switches'Last
327 and then Switches (Index) /= ' ' loop
334 End_Index := Current_Index + Max_Length - 1;
336 -- If the switch is not accepted, skip it, unless we had a '*' in
339 if Index_Switches = 0 then
340 if Switches (Switches'First) = '*' then
341 Set_Parameter (The_Switch,
342 Arg_Num => Current_Argument,
344 Last => CL.Argument (Current_Argument)'Last);
345 Is_Switch (Current_Argument) := True;
346 Dummy := Goto_Next_Argument_In_Section;
350 Set_Parameter (The_Switch,
351 Arg_Num => Current_Argument,
352 First => Current_Index,
353 Last => Current_Index);
354 Current_Index := Current_Index + 1;
355 raise Invalid_Switch;
358 Set_Parameter (The_Switch,
359 Arg_Num => Current_Argument,
360 First => Current_Index,
363 -- If switch needs an argument
365 if Index_Switches + Max_Length <= Switches'Last then
367 case Switches (Index_Switches + Max_Length) is
371 if End_Index < Arg'Last then
372 Set_Parameter (The_Parameter,
373 Arg_Num => Current_Argument,
374 First => End_Index + 1,
376 Dummy := Goto_Next_Argument_In_Section;
378 elsif Section (Current_Argument + 1) /= 0 then
381 Arg_Num => Current_Argument + 1,
383 Last => CL.Argument (Current_Argument + 1)'Last);
384 Current_Argument := Current_Argument + 1;
385 Is_Switch (Current_Argument) := True;
386 Dummy := Goto_Next_Argument_In_Section;
389 Current_Index := End_Index + 1;
390 raise Invalid_Parameter;
395 if End_Index < Arg'Last then
396 Set_Parameter (The_Parameter,
397 Arg_Num => Current_Argument,
398 First => End_Index + 1,
400 Dummy := Goto_Next_Argument_In_Section;
403 Current_Index := End_Index + 1;
404 raise Invalid_Parameter;
409 if End_Index < Arg'Last then
410 Set_Parameter (The_Parameter,
411 Arg_Num => Current_Argument,
412 First => End_Index + 1,
416 Set_Parameter (The_Parameter,
417 Arg_Num => Current_Argument,
421 Dummy := Goto_Next_Argument_In_Section;
425 Current_Index := End_Index + 1;
429 Current_Index := End_Index + 1;
432 return Switches (Index_Switches);
436 -----------------------------------
437 -- Goto_Next_Argument_In_Section --
438 -----------------------------------
440 function Goto_Next_Argument_In_Section return Boolean is
443 Current_Argument := Current_Argument + 1;
445 if Section (Current_Argument) = 0 then
447 if Current_Argument > CL.Argument_Count then
450 Current_Argument := Current_Argument + 1;
451 exit when Section (Current_Argument) = Current_Section;
455 end Goto_Next_Argument_In_Section;
461 procedure Goto_Section (Name : String := "") is
462 Index : Integer := 1;
465 In_Expansion := False;
468 Current_Argument := 1;
470 Current_Section := 1;
474 while Index <= CL.Argument_Count loop
476 if Section (Index) = 0
477 and then CL.Argument (Index) = Switch_Character & Name
479 Current_Argument := Index + 1;
481 if Current_Argument <= CL.Argument_Count then
482 Current_Section := Section (Current_Argument);
489 Current_Argument := Positive'Last;
490 Current_Index := 2; -- so that Get_Argument returns nothing
493 ----------------------------
494 -- Initialize_Option_Scan --
495 ----------------------------
497 procedure Initialize_Option_Scan
498 (Switch_Char : Character := '-';
499 Stop_At_First_Non_Switch : Boolean := False;
500 Section_Delimiters : String := "")
502 Section_Num : Section_Number := 1;
503 Section_Index : Integer := Section_Delimiters'First;
505 Delimiter_Found : Boolean;
508 Current_Argument := 0;
510 In_Expansion := False;
511 Switch_Character := Switch_Char;
512 Stop_At_First := Stop_At_First_Non_Switch;
514 -- If we are using sections, we have to preprocess the command line
515 -- to delimit them. A section can be repeated, so we just give each
516 -- item on the command line a section number
518 while Section_Index <= Section_Delimiters'Last loop
520 Last := Section_Index;
521 while Last <= Section_Delimiters'Last
522 and then Section_Delimiters (Last) /= ' '
527 Delimiter_Found := False;
528 Section_Num := Section_Num + 1;
530 for Index in 1 .. CL.Argument_Count loop
531 if CL.Argument (Index)(1) = Switch_Character
532 and then CL.Argument (Index) = Switch_Character
533 & Section_Delimiters (Section_Index .. Last - 1)
535 Section (Index) := 0;
536 Delimiter_Found := True;
538 elsif Section (Index) = 0 then
539 Delimiter_Found := False;
541 elsif Delimiter_Found then
542 Section (Index) := Section_Num;
546 Section_Index := Last + 1;
547 while Section_Index <= Section_Delimiters'Last
548 and then Section_Delimiters (Section_Index) = ' '
550 Section_Index := Section_Index + 1;
554 Delimiter_Found := Goto_Next_Argument_In_Section;
555 end Initialize_Option_Scan;
561 function Parameter return String is
563 if The_Parameter.First > The_Parameter.Last then
564 return String'(1 .. 0 => ' ');
566 return CL.Argument (The_Parameter.Arg_Num)
567 (The_Parameter.First .. The_Parameter.Last);
575 procedure Set_Parameter
576 (Variable : out Parameter_Type;
581 Variable.Arg_Num := Arg_Num;
582 Variable.First := First;
583 Variable.Last := Last;
586 ---------------------
587 -- Start_Expansion --
588 ---------------------
590 procedure Start_Expansion
591 (Iterator : out Expansion_Iterator;
593 Directory : String := "";
594 Basic_Regexp : Boolean := True)
596 Directory_Separator : Character;
597 pragma Import (C, Directory_Separator, "__gnat_dir_separator");
600 if Directory = "" then
601 GNAT.Directory_Operations.Open
602 (Iterator.Dir, "." & Directory_Separator);
604 GNAT.Directory_Operations.Open (Iterator.Dir, Directory);
607 Iterator.Regexp := GNAT.Regexp.Compile (Pattern, Basic_Regexp, True);
611 Section (CL.Argument_Count + 1) := 0;
612 end GNAT.Command_Line;