1 ------------------------------------------------------------------------------
3 -- GNAT RUN-TIME COMPONENTS --
5 -- A D A . C O M M A N D _ L I N E . R E S P O N S E _ F I L E --
9 -- Copyright (C) 2007-2015, Free Software Foundation, Inc. --
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. --
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. --
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/>. --
27 -- GNAT was originally developed by the GNAT team at New York University. --
28 -- Extensive contributions were provided by Ada Core Technologies Inc. --
30 ------------------------------------------------------------------------------
32 pragma Compiler_Unit_Warning
;
34 with Ada
.Unchecked_Deallocation
;
36 with System
.OS_Lib
; use System
.OS_Lib
;
38 package body Ada
.Command_Line
.Response_File
is
41 type File_Ptr
is access File_Rec
;
42 type File_Rec
is record
47 -- To build a stack of response file names
49 procedure Free
is new Ada
.Unchecked_Deallocation
(File_Rec
, File_Ptr
);
51 type Argument_List_Access
is access Argument_List
;
52 procedure Free
is new Ada
.Unchecked_Deallocation
53 (Argument_List
, Argument_List_Access
);
54 -- Free only the allocated Argument_List, not allocated String components
60 function Arguments_From
61 (Response_File_Name
: String;
62 Recursive
: Boolean := False;
63 Ignore_Non_Existing_Files
: Boolean := False)
66 First_File
: File_Ptr
:= null;
67 Last_File
: File_Ptr
:= null;
68 -- The stack of response files
70 Arguments
: Argument_List_Access
:= new Argument_List
(1 .. 4);
71 Last_Arg
: Natural := 0;
73 procedure Add_Argument
(Arg
: String);
74 -- Add argument Arg to argument list Arguments, increasing Arguments
77 procedure Recurse
(File_Name
: String);
78 -- Get the arguments from the file and call itself recursively if one of
79 -- the argument starts with character '@'.
85 procedure Add_Argument
(Arg
: String) is
87 if Last_Arg
= Arguments
'Last then
89 New_Arguments
: constant Argument_List_Access
:=
90 new Argument_List
(1 .. Arguments
'Last * 2);
92 New_Arguments
(Arguments
'Range) := Arguments
.all;
93 Arguments
.all := (others => null);
95 Arguments
:= New_Arguments
;
99 Last_Arg
:= Last_Arg
+ 1;
100 Arguments
(Last_Arg
) := new String'(Arg);
107 procedure Recurse (File_Name : String) is
108 FD : File_Descriptor;
110 Buffer_Size : constant := 1500;
111 Buffer : String (1 .. Buffer_Size);
113 Buffer_Length : Natural;
115 Buffer_Cursor : Natural;
117 End_Of_File_Reached : Boolean;
119 Line : String (1 .. Max_Line_Length + 1);
122 First_Char : Positive;
123 -- Index of the first character of an argument in Line
126 -- Index of the last character of an argument in Line
129 -- True when inside a quoted string
133 function End_Of_File return Boolean;
134 -- True when the end of the response file has been reached
136 procedure Get_Buffer;
137 -- Read one buffer from the response file
140 -- Get one line from the response file
146 function End_Of_File return Boolean is
148 return End_Of_File_Reached and then Buffer_Cursor > Buffer_Length;
155 procedure Get_Buffer is
157 Buffer_Length := Read (FD, Buffer (1)'Address, Buffer'Length);
158 End_Of_File_Reached := Buffer_Length < Buffer'Length;
166 procedure Get_Line is
177 Ch := Buffer (Buffer_Cursor);
179 exit when Ch = ASCII.CR or else
180 Ch = ASCII.LF or else
186 if Last = Line'Last then
190 Buffer_Cursor := Buffer_Cursor + 1;
192 if Buffer_Cursor > Buffer_Length then
202 Ch := Buffer (Buffer_Cursor);
204 exit when Ch /= ASCII.HT and then
205 Ch /= ASCII.LF and then
208 Buffer_Cursor := Buffer_Cursor + 1;
210 if Buffer_Cursor > Buffer_Length then
220 -- Start of processing for Recurse
225 -- Open the response file. If not found, fail or report a warning,
226 -- depending on the value of Ignore_Non_Existing_Files.
228 FD := Open_Read (File_Name, Text);
230 if FD = Invalid_FD then
231 if Ignore_Non_Existing_Files then
234 raise File_Does_Not_Exist;
238 -- Put the response file name on the stack
240 if First_File = null then
243 (Name
=> new String'(File_Name),
246 Last_File := First_File;
250 Current : File_Ptr := First_File;
254 if Current.Name.all = File_Name then
255 raise Circularity_Detected;
258 Current := Current.Next;
259 exit when Current = null;
264 (Name
=> new String'(File_Name),
267 Last_File := Last_File.Next;
271 End_Of_File_Reached := False;
274 -- Read the response file line by line
277 while not End_Of_File loop
280 if Last = Line'Last then
286 -- Get each argument on the line
290 -- First, skip any white space
292 while First_Char <= Last loop
293 exit when Line (First_Char) /= ' ' and then
294 Line (First_Char) /= ASCII.HT;
295 First_Char := First_Char + 1;
298 exit Arg_Loop when First_Char > Last;
300 Last_Char := First_Char;
303 -- Get the character one by one
306 while Last_Char <= Last loop
308 -- Inside a string, check only for '"'
311 if Line (Last_Char) = '"' then
315 Line (Last_Char .. Last - 1) :=
316 Line (Last_Char + 1 .. Last);
319 -- End of string is end of argument
321 if Last_Char > Last or else
322 Line (Last_Char) = ' ' or else
323 Line (Last_Char) = ASCII.HT
327 Last_Char := Last_Char - 1;
331 -- If there are two consecutive '"', the quoted
332 -- string is not closed
334 In_String := Line (Last_Char) = '"';
337 Last_Char := Last_Char + 1;
342 Last_Char := Last_Char + 1;
345 elsif Last_Char = Last then
347 -- An opening '"' at the end of the line is an error
349 if Line (Last) = '"' then
350 raise No_Closing_Quote;
353 -- The argument ends with the line
358 elsif Line (Last_Char) = '"' then
360 -- Entering a quoted string: remove the '"'
363 Line (Last_Char .. Last - 1) :=
364 Line (Last_Char + 1 .. Last);
368 -- Outside quoted strings, white space ends the argument
371 when Line (Last_Char + 1) = ' ' or else
372 Line (Last_Char + 1) = ASCII.HT;
374 Last_Char := Last_Char + 1;
376 end loop Character_Loop;
378 -- It is an error to not close a quoted string before the end
382 raise No_Closing_Quote;
385 -- Add the argument to the list
388 Arg : String (1 .. Last_Char - First_Char + 1);
390 Arg := Line (First_Char .. Last_Char);
394 -- Next argument, if line is not finished
396 First_Char := Last_Char + 1;
402 -- If Recursive is True, check for any argument starting with '@'
406 while Arg <= Last_Arg loop
408 if Arguments (Arg)'Length > 0 and then
409 Arguments (Arg) (1) = '@'
411 -- Ignore argument "@
" with no file name
413 if Arguments (Arg)'Length = 1 then
414 Arguments (Arg .. Last_Arg - 1) :=
415 Arguments (Arg + 1 .. Last_Arg);
416 Last_Arg := Last_Arg - 1;
419 -- Save the current arguments and get those in the new
423 Inc_File_Name : constant String :=
424 Arguments (Arg) (2 .. Arguments (Arg)'Last);
425 Current_Arguments : constant Argument_List :=
426 Arguments (1 .. Last_Arg);
428 Recurse (Inc_File_Name);
430 -- Insert the new arguments where the new response
431 -- file was imported.
434 New_Arguments : constant Argument_List :=
435 Arguments (1 .. Last_Arg);
436 New_Last_Arg : constant Positive :=
437 Current_Arguments'Length +
438 New_Arguments'Length - 1;
441 -- Grow Arguments if it is not large enough
443 if Arguments'Last < New_Last_Arg then
444 Last_Arg := Arguments'Last;
447 while Last_Arg < New_Last_Arg loop
448 Last_Arg := Last_Arg * 2;
451 Arguments := new Argument_List (1 .. Last_Arg);
454 Last_Arg := New_Last_Arg;
456 Arguments (1 .. Last_Arg) :=
457 Current_Arguments (1 .. Arg - 1) &
460 (Arg + 1 .. Current_Arguments'Last);
462 Arg := Arg + New_Arguments'Length;
473 -- Remove the response file name from the stack
475 if First_File = Last_File then
476 System.Strings.Free (First_File.Name);
482 System.Strings.Free (Last_File.Name);
483 Last_File := Last_File.Prev;
484 Free (Last_File.Next);
494 -- Start of processing for Arguments_From
497 -- The job is done by procedure Recurse
499 Recurse (Response_File_Name);
501 -- Free Arguments before returning the result
504 Result : constant Argument_List := Arguments (1 .. Last_Arg);
513 -- When an exception occurs, deallocate everything
517 while First_File /= null loop
518 Last_File := First_File.Next;
519 System.Strings.Free (First_File.Name);
521 First_File := Last_File;
527 end Ada.Command_Line.Response_File;