diff options
Diffstat (limited to 'gcc-4.2.1/gcc/ada/gnatchop.adb')
-rw-r--r-- | gcc-4.2.1/gcc/ada/gnatchop.adb | 1810 |
1 files changed, 0 insertions, 1810 deletions
diff --git a/gcc-4.2.1/gcc/ada/gnatchop.adb b/gcc-4.2.1/gcc/ada/gnatchop.adb deleted file mode 100644 index eab706325..000000000 --- a/gcc-4.2.1/gcc/ada/gnatchop.adb +++ /dev/null @@ -1,1810 +0,0 @@ ------------------------------------------------------------------------------- --- -- --- GNAT COMPILER COMPONENTS -- --- -- --- G N A T C H O P -- --- -- --- B o d y -- --- -- --- Copyright (C) 1998-2005, AdaCore -- --- -- --- GNAT is free software; you can redistribute it and/or modify it under -- --- terms of the GNU General Public License as published by the Free Soft- -- --- ware Foundation; either version 2, or (at your option) any later ver- -- --- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- --- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- --- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License -- --- for more details. You should have received a copy of the GNU General -- --- Public License distributed with GNAT; see file COPYING. If not, write -- --- to the Free Software Foundation, 51 Franklin Street, Fifth Floor, -- --- Boston, MA 02110-1301, USA. -- --- -- --- GNAT was originally developed by the GNAT team at New York University. -- --- Extensive contributions were provided by Ada Core Technologies Inc. -- --- -- ------------------------------------------------------------------------------- - -with Ada.Command_Line; use Ada.Command_Line; -with Ada.Text_IO; use Ada.Text_IO; - -with GNAT.Command_Line; use GNAT.Command_Line; -with GNAT.OS_Lib; use GNAT.OS_Lib; -with GNAT.Heap_Sort_G; -with GNAT.Table; - -with Gnatvsn; -with Hostparm; - -with System.CRTL; use System.CRTL; - -procedure Gnatchop is - - Terminate_Program : exception; - -- Used to terminate execution immediately - - Config_File_Name : constant String_Access := new String'("gnat.adc"); - -- The name of the file holding the GNAT configuration pragmas - - Gcc : String_Access := new String'("gcc"); - -- May be modified by switch --GCC= - - Gcc_Set : Boolean := False; - -- True if a switch --GCC= is used - - Gnat_Cmd : String_Access; - -- Command to execute the GNAT compiler - - Gnat_Args : Argument_List_Access := - new Argument_List' - (new String'("-c"), - new String'("-x"), - new String'("ada"), - new String'("-gnats"), - new String'("-gnatu")); - -- Arguments used in Gnat_Cmd call - - EOF : constant Character := Character'Val (26); - -- Special character to signal end of file. Not required in input - -- files, but properly treated if present. Not generated in output - -- files except as a result of copying input file. - - -------------------- - -- File arguments -- - -------------------- - - subtype File_Num is Natural; - subtype File_Offset is Natural; - - type File_Entry is record - Name : String_Access; - -- Name of chop file or directory - - SR_Name : String_Access; - -- Null unless the chop file starts with a source reference pragma - -- in which case this field points to the file name from this pragma. - end record; - - package File is new GNAT.Table - (Table_Component_Type => File_Entry, - Table_Index_Type => File_Num, - Table_Low_Bound => 1, - Table_Initial => 100, - Table_Increment => 100); - - Directory : String_Access; - -- Record name of directory, or a null string if no directory given - - Compilation_Mode : Boolean := False; - Overwrite_Files : Boolean := False; - Preserve_Mode : Boolean := False; - Quiet_Mode : Boolean := False; - Source_References : Boolean := False; - Verbose_Mode : Boolean := False; - Exit_On_Error : Boolean := False; - -- Global options - - Write_gnat_adc : Boolean := False; - -- Gets set true if we append to gnat.adc or create a new gnat.adc. - -- Used to inhibit complaint about no units generated. - - --------------- - -- Unit list -- - --------------- - - type Line_Num is new Natural; - -- Line number (for source reference pragmas) - - type Unit_Count_Type is new Integer; - subtype Unit_Num is Unit_Count_Type range 1 .. Unit_Count_Type'Last; - -- Used to refer to unit number in unit table - - type SUnit_Num is new Integer; - -- Used to refer to entry in sorted units table. Note that entry - -- zero is only for use by Heapsort, and is not otherwise referenced. - - type Unit_Kind is (Unit_Spec, Unit_Body, Config_Pragmas); - - -- Structure to contain all necessary information for one unit. - -- Entries are also temporarily used to record config pragma sequences. - - type Unit_Info is record - File_Name : String_Access; - -- File name from GNAT output line - - Chop_File : File_Num; - -- File number in chop file sequence - - Start_Line : Line_Num; - -- Line number from GNAT output line - - Offset : File_Offset; - -- Offset name from GNAT output line - - SR_Present : Boolean; - -- Set True if SR parameter present - - Length : File_Offset; - -- A length of 0 means that the Unit is the last one in the file - - Kind : Unit_Kind; - -- Indicates kind of unit - - Sorted_Index : SUnit_Num; - -- Index of unit in sorted unit list - - Bufferg : String_Access; - -- Pointer to buffer containing configuration pragmas to be - -- prepended. Null if no pragmas to be prepended. - - end record; - - -- The following table stores the unit offset information - - package Unit is new GNAT.Table - (Table_Component_Type => Unit_Info, - Table_Index_Type => Unit_Count_Type, - Table_Low_Bound => 1, - Table_Initial => 500, - Table_Increment => 100); - - -- The following table is used as a sorted index to the Unit.Table. - -- The entries in Unit.Table are not moved, instead we just shuffle - -- the entries in Sorted_Units. Note that the zeroeth entry in this - -- table is used by GNAT.Heap_Sort_G. - - package Sorted_Units is new GNAT.Table - (Table_Component_Type => Unit_Num, - Table_Index_Type => SUnit_Num, - Table_Low_Bound => 0, - Table_Initial => 500, - Table_Increment => 100); - - function Is_Duplicated (U : SUnit_Num) return Boolean; - -- Returns true if U is duplicated by a later unit. - -- Note that this function returns false for the last entry. - - procedure Sort_Units; - -- Sort units and set up sorted unit table - - ---------------------- - -- File_Descriptors -- - ---------------------- - - function dup (handle : File_Descriptor) return File_Descriptor; - function dup2 (from, to : File_Descriptor) return File_Descriptor; - - --------------------- - -- Local variables -- - --------------------- - - Warning_Count : Natural := 0; - -- Count of warnings issued so far - - ----------------------- - -- Local subprograms -- - ----------------------- - - procedure Error_Msg (Message : String; Warning : Boolean := False); - -- Produce an error message on standard error output - - procedure File_Time_Stamp (Name : C_File_Name; Time : OS_Time); - -- Given the name of a file or directory, Name, set the - -- time stamp. This function must be used for an unopened file. - - function Files_Exist return Boolean; - -- Check Unit.Table for possible file names that already exist - -- in the file system. Returns true if files exist, False otherwise - - function Get_Maximum_File_Name_Length return Integer; - pragma Import (C, Get_Maximum_File_Name_Length, - "__gnat_get_maximum_file_name_length"); - -- Function to get maximum file name length for system - - Maximum_File_Name_Length : constant Integer := Get_Maximum_File_Name_Length; - Maximum_File_Name_Length_String : constant String := - Integer'Image - (Maximum_File_Name_Length); - - function Locate_Executable - (Program_Name : String; - Look_For_Prefix : Boolean := True) - return String_Access; - -- Locate executable for given program name. This takes into account - -- the target-prefix of the current command, if Look_For_Prefix is True. - - subtype EOL_Length is Natural range 0 .. 2; - -- Possible lengths of end of line sequence - - type EOL_String (Len : EOL_Length := 0) is record - Str : String (1 .. Len); - end record; - - function Get_EOL - (Source : access String; - Start : Positive) - return EOL_String; - -- Return the line terminator used in the passed string - - procedure Parse_EOL (Source : access String; Ptr : in out Positive); - -- On return Source (Ptr) is the first character of the next line - -- or EOF. Source.all must be terminated by EOF. - - function Parse_File (Num : File_Num) return Boolean; - -- Calls the GNAT compiler to parse the given source file and parses the - -- output using Parse_Offset_Info. Returns True if parse operation - -- completes, False if some system error (e.g. failure to read the - -- offset information) occurs. - - procedure Parse_Offset_Info (Chop_File : File_Num; Source : access String); - -- Parses the output of the compiler indicating the offsets - -- and names of the compilation units in Chop_File. - - procedure Parse_Token - (Source : access String; - Ptr : in out Positive; - Token_Ptr : out Positive); - -- Skips any separators and stores the start of the token in Token_Ptr. - -- Then stores the position of the next separator in Ptr. - -- On return Source (Token_Ptr .. Ptr - 1) is the token. - - procedure Read_File - (FD : File_Descriptor; - Contents : out String_Access; - Success : out Boolean); - -- Reads file associated with FS into the newly allocated - -- string Contents. - -- [VMS] Success is true iff the number of bytes read is less than or - -- equal to the file size. - -- [Other] Success is true iff the number of bytes read is equal to - -- the file size. - - function Report_Duplicate_Units return Boolean; - -- Output messages about duplicate units in the input files in Unit.Table - -- Returns True if any duplicates found, Fals if no duplicates found. - - function Scan_Arguments return Boolean; - -- Scan command line options and set global variables accordingly. - -- Also scan out file and directory arguments. Returns True if scan - -- was successful, and False if the scan fails for any reason. - - procedure Usage; - -- Output message on standard output describing syntax of gnatchop command - - procedure Warning_Msg (Message : String); - -- Output a warning message on standard error and update warning count - - function Write_Chopped_Files (Input : File_Num) return Boolean; - -- Write all units that result from chopping the Input file - - procedure Write_Config_File (Input : File_Num; U : Unit_Num); - -- Call to write configuration pragmas (append them to gnat.adc) - -- Input is the file number for the chop file and U identifies the - -- unit entry for the configuration pragmas. - - function Get_Config_Pragmas - (Input : File_Num; - U : Unit_Num) - return String_Access; - -- Call to read configuration pragmas from given unit entry, and - -- return a buffer containing the pragmas to be appended to - -- following units. Input is the file number for the chop file and - -- U identifies the unit entry for the configuration pragmas. - - procedure Write_Source_Reference_Pragma - (Info : Unit_Info; - Line : Line_Num; - FD : File_Descriptor; - EOL : EOL_String; - Success : in out Boolean); - -- If Success is True on entry, writes a source reference pragma using - -- the chop file from Info, and the given line number. On return Success - -- indicates whether the write succeeded. If Success is False on entry, - -- or if the global flag Source_References is False, then the call to - -- Write_Source_Reference_Pragma has no effect. EOL indicates the end - -- of line sequence to be written at the end of the pragma. - - procedure Write_Unit - (Source : access String; - Num : Unit_Num; - TS_Time : OS_Time; - Success : out Boolean); - -- Write one compilation unit of the source to file - - --------- - -- dup -- - --------- - - function dup (handle : File_Descriptor) return File_Descriptor is - begin - return File_Descriptor (System.CRTL.dup (int (handle))); - end dup; - - ---------- - -- dup2 -- - ---------- - - function dup2 (from, to : File_Descriptor) return File_Descriptor is - begin - return File_Descriptor (System.CRTL.dup2 (int (from), int (to))); - end dup2; - - --------------- - -- Error_Msg -- - --------------- - - procedure Error_Msg (Message : String; Warning : Boolean := False) is - begin - Put_Line (Standard_Error, Message); - - if not Warning then - Set_Exit_Status (Failure); - - if Exit_On_Error then - raise Terminate_Program; - end if; - end if; - end Error_Msg; - - --------------------- - -- File_Time_Stamp -- - --------------------- - - procedure File_Time_Stamp (Name : C_File_Name; Time : OS_Time) is - procedure Set_File_Time (Name : C_File_Name; Time : OS_Time); - pragma Import (C, Set_File_Time, "__gnat_set_file_time_name"); - - begin - Set_File_Time (Name, Time); - end File_Time_Stamp; - - ----------------- - -- Files_Exist -- - ----------------- - - function Files_Exist return Boolean is - Exists : Boolean := False; - - begin - for SNum in 1 .. SUnit_Num (Unit.Last) loop - - -- Only check and report for the last instance of duplicated files - - if not Is_Duplicated (SNum) then - declare - Info : constant Unit_Info := - Unit.Table (Sorted_Units.Table (SNum)); - - begin - if Is_Writable_File (Info.File_Name.all) then - if Hostparm.OpenVMS then - Error_Msg - (Info.File_Name.all - & " already exists, use /OVERWRITE to overwrite"); - else - Error_Msg (Info.File_Name.all - & " already exists, use -w to overwrite"); - end if; - - Exists := True; - end if; - end; - end if; - end loop; - - return Exists; - end Files_Exist; - - ------------------------ - -- Get_Config_Pragmas -- - ------------------------ - - function Get_Config_Pragmas - (Input : File_Num; - U : Unit_Num) - return String_Access - is - Info : Unit_Info renames Unit.Table (U); - FD : File_Descriptor; - Name : aliased constant String := - File.Table (Input).Name.all & ASCII.Nul; - Length : File_Offset; - Buffer : String_Access; - Success : Boolean; - Result : String_Access; - - begin - FD := Open_Read (Name'Address, Binary); - - if FD = Invalid_FD then - Error_Msg ("cannot open " & File.Table (Input).Name.all); - return null; - end if; - - Read_File (FD, Buffer, Success); - - -- A length of 0 indicates that the rest of the file belongs to - -- this unit. The actual length must be calculated now. Take into - -- account that the last character (EOF) must not be written. - - if Info.Length = 0 then - Length := Buffer'Last - (Buffer'First + Info.Offset); - else - Length := Info.Length; - end if; - - Result := new String'(Buffer (1 .. Length)); - Close (FD); - return Result; - end Get_Config_Pragmas; - - ------------- - -- Get_EOL -- - ------------- - - function Get_EOL - (Source : access String; - Start : Positive) - return EOL_String - is - Ptr : Positive := Start; - First : Positive; - Last : Natural; - - begin - -- Skip to end of line - - while Source (Ptr) /= ASCII.CR and then - Source (Ptr) /= ASCII.LF and then - Source (Ptr) /= EOF - loop - Ptr := Ptr + 1; - end loop; - - Last := Ptr; - - if Source (Ptr) /= EOF then - - -- Found CR or LF - - First := Ptr; - - else - First := Ptr + 1; - end if; - - -- Recognize CR/LF or LF/CR combination - - if (Source (Ptr + 1) = ASCII.CR or Source (Ptr + 1) = ASCII.LF) - and then Source (Ptr) /= Source (Ptr + 1) - then - Last := First + 1; - end if; - - return (Len => Last + 1 - First, Str => Source (First .. Last)); - end Get_EOL; - - ------------------- - -- Is_Duplicated -- - ------------------- - - function Is_Duplicated (U : SUnit_Num) return Boolean is - begin - return U < SUnit_Num (Unit.Last) - and then - Unit.Table (Sorted_Units.Table (U)).File_Name.all = - Unit.Table (Sorted_Units.Table (U + 1)).File_Name.all; - end Is_Duplicated; - - ----------------------- - -- Locate_Executable -- - ----------------------- - - function Locate_Executable - (Program_Name : String; - Look_For_Prefix : Boolean := True) return String_Access - is - Current_Command : constant String := Normalize_Pathname (Command_Name); - End_Of_Prefix : Natural; - Start_Of_Prefix : Positive; - Result : String_Access; - - begin - Start_Of_Prefix := Current_Command'First; - End_Of_Prefix := Start_Of_Prefix - 1; - - if Look_For_Prefix then - - -- Find Start_Of_Prefix - - for J in reverse Current_Command'Range loop - if Current_Command (J) = '/' or - Current_Command (J) = Directory_Separator or - Current_Command (J) = ':' - then - Start_Of_Prefix := J + 1; - exit; - end if; - end loop; - - -- Find End_Of_Prefix - - for J in reverse Start_Of_Prefix .. Current_Command'Last loop - if Current_Command (J) = '-' then - End_Of_Prefix := J; - exit; - end if; - end loop; - end if; - - declare - Command : constant String := - Current_Command (Start_Of_Prefix .. End_Of_Prefix) & - Program_Name; - begin - Result := Locate_Exec_On_Path (Command); - - if Result = null then - Error_Msg - (Command & ": installation problem, executable not found"); - end if; - end; - - return Result; - end Locate_Executable; - - --------------- - -- Parse_EOL -- - --------------- - - procedure Parse_EOL (Source : access String; Ptr : in out Positive) is - begin - -- Skip to end of line - - while Source (Ptr) /= ASCII.CR and then Source (Ptr) /= ASCII.LF - and then Source (Ptr) /= EOF - loop - Ptr := Ptr + 1; - end loop; - - if Source (Ptr) /= EOF then - Ptr := Ptr + 1; -- skip CR or LF - end if; - - -- Skip past CR/LF or LF/CR combination - - if (Source (Ptr) = ASCII.CR or Source (Ptr) = ASCII.LF) - and then Source (Ptr) /= Source (Ptr - 1) - then - Ptr := Ptr + 1; - end if; - end Parse_EOL; - - ---------------- - -- Parse_File -- - ---------------- - - function Parse_File (Num : File_Num) return Boolean is - Chop_Name : constant String_Access := File.Table (Num).Name; - Save_Stdout : constant File_Descriptor := dup (Standout); - Offset_Name : Temp_File_Name; - Offset_FD : File_Descriptor; - Buffer : String_Access; - Success : Boolean; - Failure : exception; - - begin - -- Display copy of GNAT command if verbose mode - - if Verbose_Mode then - Put (Gnat_Cmd.all); - - for J in 1 .. Gnat_Args'Length loop - Put (' '); - Put (Gnat_Args (J).all); - end loop; - - Put (' '); - Put_Line (Chop_Name.all); - end if; - - -- Create temporary file - - Create_Temp_File (Offset_FD, Offset_Name); - - if Offset_FD = Invalid_FD then - Error_Msg ("gnatchop: cannot create temporary file"); - Close (Save_Stdout); - return False; - end if; - - -- Redirect Stdout to this temporary file in the Unix way - - if dup2 (Offset_FD, Standout) = Invalid_FD then - Error_Msg ("gnatchop: cannot redirect stdout to temporary file"); - Close (Save_Stdout); - Close (Offset_FD); - return False; - end if; - - -- Call Gnat on the source filename argument with special options - -- to generate offset information. If this special compilation completes - -- successfully then we can do the actual gnatchop operation. - - Spawn (Gnat_Cmd.all, Gnat_Args.all & Chop_Name, Success); - - if not Success then - Error_Msg (Chop_Name.all & ": parse errors detected"); - Error_Msg (Chop_Name.all & ": chop may not be successful"); - end if; - - -- Restore stdout - - if dup2 (Save_Stdout, Standout) = Invalid_FD then - Error_Msg ("gnatchop: cannot restore stdout"); - end if; - - -- Reopen the file to start reading from the beginning - - Close (Offset_FD); - Close (Save_Stdout); - Offset_FD := Open_Read (Offset_Name'Address, Binary); - - if Offset_FD = Invalid_FD then - Error_Msg ("gnatchop: cannot access offset info"); - raise Failure; - end if; - - Read_File (Offset_FD, Buffer, Success); - - if not Success then - Error_Msg ("gnatchop: error reading offset info"); - Close (Offset_FD); - raise Failure; - else - Parse_Offset_Info (Num, Buffer); - end if; - - -- Close and delete temporary file - - Close (Offset_FD); - Delete_File (Offset_Name'Address, Success); - - return Success; - - exception - when Failure | Terminate_Program => - Close (Offset_FD); - Delete_File (Offset_Name'Address, Success); - return False; - - end Parse_File; - - ----------------------- - -- Parse_Offset_Info -- - ----------------------- - - procedure Parse_Offset_Info - (Chop_File : File_Num; - Source : access String) - is - First_Unit : constant Unit_Num := Unit.Last + 1; - Bufferg : String_Access := null; - Parse_Ptr : File_Offset := Source'First; - Token_Ptr : File_Offset; - Info : Unit_Info; - - function Match (Literal : String) return Boolean; - -- Checks if given string appears at the current Token_Ptr location - -- and if so, bumps Parse_Ptr past the token and returns True. If - -- the string is not present, sets Parse_Ptr to Token_Ptr and - -- returns False. - - ----------- - -- Match -- - ----------- - - function Match (Literal : String) return Boolean is - begin - Parse_Token (Source, Parse_Ptr, Token_Ptr); - - if Source'Last + 1 - Token_Ptr < Literal'Length - or else - Source (Token_Ptr .. Token_Ptr + Literal'Length - 1) /= Literal - then - Parse_Ptr := Token_Ptr; - return False; - end if; - - Parse_Ptr := Token_Ptr + Literal'Length; - return True; - end Match; - - -- Start of processing for Parse_Offset_Info - - begin - loop - -- Set default values, should get changed for all - -- units/pragmas except for the last - - Info.Chop_File := Chop_File; - Info.Length := 0; - - -- Parse the current line of offset information into Info - -- and exit the loop if there are any errors or on EOF. - - -- First case, parse a line in the following format: - - -- Unit x (spec) line 7, file offset 142, [SR, ]file name x.ads - - -- Note that the unit name can be an operator name in quotes. - -- This is of course illegal, but both GNAT and gnatchop handle - -- the case so that this error does not intefere with chopping. - - -- The SR ir present indicates that a source reference pragma - -- was processed as part of this unit (and that therefore no - -- Source_Reference pragma should be generated. - - if Match ("Unit") then - Parse_Token (Source, Parse_Ptr, Token_Ptr); - - if Match ("(body)") then - Info.Kind := Unit_Body; - elsif Match ("(spec)") then - Info.Kind := Unit_Spec; - else - exit; - end if; - - exit when not Match ("line"); - Parse_Token (Source, Parse_Ptr, Token_Ptr); - Info.Start_Line := Line_Num'Value - (Source (Token_Ptr .. Parse_Ptr - 1)); - - exit when not Match ("file offset"); - Parse_Token (Source, Parse_Ptr, Token_Ptr); - Info.Offset := File_Offset'Value - (Source (Token_Ptr .. Parse_Ptr - 1)); - - Info.SR_Present := Match ("SR, "); - - exit when not Match ("file name"); - Parse_Token (Source, Parse_Ptr, Token_Ptr); - Info.File_Name := new String' - (Directory.all & Source (Token_Ptr .. Parse_Ptr - 1)); - Parse_EOL (Source, Parse_Ptr); - - -- Second case, parse a line of the following form - - -- Configuration pragmas at line 10, file offset 223 - - elsif Match ("Configuration pragmas at") then - Info.Kind := Config_Pragmas; - Info.File_Name := Config_File_Name; - - exit when not Match ("line"); - Parse_Token (Source, Parse_Ptr, Token_Ptr); - Info.Start_Line := Line_Num'Value - (Source (Token_Ptr .. Parse_Ptr - 1)); - - exit when not Match ("file offset"); - Parse_Token (Source, Parse_Ptr, Token_Ptr); - Info.Offset := File_Offset'Value - (Source (Token_Ptr .. Parse_Ptr - 1)); - - Parse_EOL (Source, Parse_Ptr); - - -- Third case, parse a line of the following form - - -- Source_Reference pragma for file "filename" - - -- This appears at the start of the file only, and indicates - -- the name to be used on any generated Source_Reference pragmas. - - elsif Match ("Source_Reference pragma for file ") then - Parse_Token (Source, Parse_Ptr, Token_Ptr); - File.Table (Chop_File).SR_Name := - new String'(Source (Token_Ptr + 1 .. Parse_Ptr - 2)); - Parse_EOL (Source, Parse_Ptr); - goto Continue; - - -- Unrecognized keyword or end of file - - else - exit; - end if; - - -- Store the data in the Info record in the Unit.Table - - Unit.Increment_Last; - Unit.Table (Unit.Last) := Info; - - -- If this is not the first unit from the file, calculate - -- the length of the previous unit as difference of the offsets - - if Unit.Last > First_Unit then - Unit.Table (Unit.Last - 1).Length := - Info.Offset - Unit.Table (Unit.Last - 1).Offset; - end if; - - -- If not in compilation mode combine current unit with any - -- preceding configuration pragmas. - - if not Compilation_Mode - and then Unit.Last > First_Unit - and then Unit.Table (Unit.Last - 1).Kind = Config_Pragmas - then - Info.Start_Line := Unit.Table (Unit.Last - 1).Start_Line; - Info.Offset := Unit.Table (Unit.Last - 1).Offset; - - -- Delete the configuration pragma entry - - Unit.Table (Unit.Last - 1) := Info; - Unit.Decrement_Last; - end if; - - -- If in compilation mode, and previous entry is the initial - -- entry for the file and is for configuration pragmas, then - -- they are to be appended to every unit in the file. - - if Compilation_Mode - and then Unit.Last = First_Unit + 1 - and then Unit.Table (First_Unit).Kind = Config_Pragmas - then - Bufferg := - Get_Config_Pragmas - (Unit.Table (Unit.Last - 1).Chop_File, First_Unit); - Unit.Table (Unit.Last - 1) := Info; - Unit.Decrement_Last; - end if; - - Unit.Table (Unit.Last).Bufferg := Bufferg; - - -- If in compilation mode, and this is not the first item, - -- combine configuration pragmas with previous unit, which - -- will cause an error message to be generated when the unit - -- is compiled. - - if Compilation_Mode - and then Unit.Last > First_Unit - and then Unit.Table (Unit.Last).Kind = Config_Pragmas - then - Unit.Decrement_Last; - end if; - - <<Continue>> - null; - - end loop; - - -- Find out if the loop was exited prematurely because of - -- an error or if the EOF marker was found. - - if Source (Parse_Ptr) /= EOF then - Error_Msg - (File.Table (Chop_File).Name.all & ": error parsing offset info"); - return; - end if; - - -- Handle case of a chop file consisting only of config pragmas - - if Unit.Last = First_Unit - and then Unit.Table (Unit.Last).Kind = Config_Pragmas - then - -- In compilation mode, we append such a file to gnat.adc - - if Compilation_Mode then - Write_Config_File (Unit.Table (Unit.Last).Chop_File, First_Unit); - Unit.Decrement_Last; - - -- In default (non-compilation) mode, this is invalid - - else - Error_Msg - (File.Table (Chop_File).Name.all & - ": no units found (only pragmas)"); - Unit.Decrement_Last; - end if; - end if; - - -- Handle case of a chop file ending with config pragmas. This can - -- happen only in default non-compilation mode, since in compilation - -- mode such configuration pragmas are part of the preceding unit. - -- We simply concatenate such pragmas to the previous file which - -- will cause a compilation error, which is appropriate. - - if Unit.Last > First_Unit - and then Unit.Table (Unit.Last).Kind = Config_Pragmas - then - Unit.Decrement_Last; - end if; - end Parse_Offset_Info; - - ----------------- - -- Parse_Token -- - ----------------- - - procedure Parse_Token - (Source : access String; - Ptr : in out Positive; - Token_Ptr : out Positive) - is - In_Quotes : Boolean := False; - - begin - -- Skip separators - - while Source (Ptr) = ' ' or Source (Ptr) = ',' loop - Ptr := Ptr + 1; - end loop; - - Token_Ptr := Ptr; - - -- Find end-of-token - - while (In_Quotes or else not (Source (Ptr) = ' ' or Source (Ptr) = ',')) - and then Source (Ptr) >= ' ' - loop - if Source (Ptr) = '"' then - In_Quotes := not In_Quotes; - end if; - - Ptr := Ptr + 1; - end loop; - end Parse_Token; - - --------------- - -- Read_File -- - --------------- - - procedure Read_File - (FD : File_Descriptor; - Contents : out String_Access; - Success : out Boolean) - is - Length : constant File_Offset := File_Offset (File_Length (FD)); - -- Include room for EOF char - Buffer : constant String_Access := new String (1 .. Length + 1); - - This_Read : Integer; - Read_Ptr : File_Offset := 1; - - begin - - loop - This_Read := Read (FD, - A => Buffer (Read_Ptr)'Address, - N => Length + 1 - Read_Ptr); - Read_Ptr := Read_Ptr + Integer'Max (This_Read, 0); - exit when This_Read <= 0; - end loop; - - Buffer (Read_Ptr) := EOF; - Contents := new String (1 .. Read_Ptr); - Contents.all := Buffer (1 .. Read_Ptr); - - -- Things aren't simple on VMS due to the plethora of file types - -- and organizations. It seems clear that there shouldn't be more - -- bytes read than are contained in the file though. - - if Hostparm.OpenVMS then - Success := Read_Ptr <= Length + 1; - else - Success := Read_Ptr = Length + 1; - end if; - end Read_File; - - ---------------------------- - -- Report_Duplicate_Units -- - ---------------------------- - - function Report_Duplicate_Units return Boolean is - US : SUnit_Num; - U : Unit_Num; - - Duplicates : Boolean := False; - - begin - US := 1; - while US < SUnit_Num (Unit.Last) loop - U := Sorted_Units.Table (US); - - if Is_Duplicated (US) then - Duplicates := True; - - -- Move to last two versions of duplicated file to make it clearer - -- to understand which file is retained in case of overwriting. - - while US + 1 < SUnit_Num (Unit.Last) loop - exit when not Is_Duplicated (US + 1); - US := US + 1; - end loop; - - U := Sorted_Units.Table (US); - - if Overwrite_Files then - Warning_Msg (Unit.Table (U).File_Name.all - & " is duplicated (all but last will be skipped)"); - - elsif Unit.Table (U).Chop_File = - Unit.Table (Sorted_Units.Table (US + 1)).Chop_File - then - Error_Msg (Unit.Table (U).File_Name.all - & " is duplicated in " - & File.Table (Unit.Table (U).Chop_File).Name.all); - - else - Error_Msg (Unit.Table (U).File_Name.all - & " in " - & File.Table (Unit.Table (U).Chop_File).Name.all - & " is duplicated in " - & File.Table - (Unit.Table - (Sorted_Units.Table (US + 1)).Chop_File).Name.all); - end if; - end if; - - US := US + 1; - end loop; - - if Duplicates and not Overwrite_Files then - if Hostparm.OpenVMS then - Put_Line - ("use /OVERWRITE to overwrite files and keep last version"); - else - Put_Line ("use -w to overwrite files and keep last version"); - end if; - end if; - - return Duplicates; - end Report_Duplicate_Units; - - -------------------- - -- Scan_Arguments -- - -------------------- - - function Scan_Arguments return Boolean is - Kset : Boolean := False; - -- Set true if -k switch found - - begin - Initialize_Option_Scan; - - -- Scan options first - - loop - case Getopt ("c gnat? h k? p q r v w x -GCC=!") is - when ASCII.NUL => - exit; - - when '-' => - Gcc := new String'(Parameter); - Gcc_Set := True; - - when 'c' => - Compilation_Mode := True; - - when 'g' => - Gnat_Args := - new Argument_List'(Gnat_Args.all & - new String'("-gnat" & Parameter)); - - when 'h' => - Usage; - raise Terminate_Program; - - when 'k' => - declare - Param : String_Access := new String'(Parameter); - - begin - if Param.all /= "" then - for J in Param'Range loop - if Param (J) not in '0' .. '9' then - if Hostparm.OpenVMS then - Error_Msg ("/FILE_NAME_MAX_LENGTH=nnn" & - " requires numeric parameter"); - else - Error_Msg ("-k# requires numeric parameter"); - end if; - - return False; - end if; - end loop; - - else - if Hostparm.OpenVMS then - Param := new String'("39"); - else - Param := new String'("8"); - end if; - end if; - - Gnat_Args := - new Argument_List'(Gnat_Args.all & - new String'("-gnatk" & Param.all)); - Kset := True; - end; - - when 'p' => - Preserve_Mode := True; - - when 'q' => - Quiet_Mode := True; - - when 'r' => - Source_References := True; - - when 'v' => - Verbose_Mode := True; - - -- Why is following written to standard error. Most other - -- tools write to standard output ??? - - Put (Standard_Error, "GNATCHOP "); - Put_Line (Standard_Error, Gnatvsn.Gnat_Version_String); - Put_Line - (Standard_Error, "Copyright 1998-2005, AdaCore"); - - when 'w' => - Overwrite_Files := True; - - when 'x' => - Exit_On_Error := True; - - when others => - null; - end case; - end loop; - - if not Kset and then Maximum_File_Name_Length > 0 then - - -- If this system has restricted filename lengths, tell gnat1 - -- about them, removing the leading blank from the image string. - - Gnat_Args := - new Argument_List'(Gnat_Args.all - & new String'("-gnatk" - & Maximum_File_Name_Length_String - (Maximum_File_Name_Length_String'First + 1 - .. Maximum_File_Name_Length_String'Last))); - end if; - - -- Scan file names - - loop - declare - S : constant String := Get_Argument (Do_Expansion => True); - - begin - exit when S = ""; - File.Increment_Last; - File.Table (File.Last).Name := new String'(S); - File.Table (File.Last).SR_Name := null; - end; - end loop; - - -- Case of more than one file where last file is a directory - - if File.Last > 1 - and then Is_Directory (File.Table (File.Last).Name.all) - then - Directory := File.Table (File.Last).Name; - File.Decrement_Last; - - -- Make sure Directory is terminated with a directory separator, - -- so we can generate the output by just appending a filename. - - if Directory (Directory'Last) /= Directory_Separator - and then Directory (Directory'Last) /= '/' - then - Directory := new String'(Directory.all & Directory_Separator); - end if; - - -- At least one filename must be given - - elsif File.Last = 0 then - Usage; - return False; - - -- No directory given, set directory to null, so that we can just - -- concatenate the directory name to the file name unconditionally. - - else - Directory := new String'(""); - end if; - - -- Finally check all filename arguments - - for File_Num in 1 .. File.Last loop - declare - F : constant String := File.Table (File_Num).Name.all; - - begin - - if Is_Directory (F) then - Error_Msg (F & " is a directory, cannot be chopped"); - return False; - - elsif not Is_Regular_File (F) then - Error_Msg (F & " not found"); - return False; - end if; - end; - end loop; - - return True; - - exception - when Invalid_Switch => - Error_Msg ("invalid switch " & Full_Switch); - return False; - - when Invalid_Parameter => - if Hostparm.OpenVMS then - Error_Msg ("/FILE_NAME_MAX_LENGTH=nnn qualifier" & - " requires numeric parameter"); - else - Error_Msg ("-k switch requires numeric parameter"); - end if; - - return False; - - end Scan_Arguments; - - ---------------- - -- Sort_Units -- - ---------------- - - procedure Sort_Units is - - procedure Move (From : Natural; To : Natural); - -- Procedure used to sort the unit list - -- Unit.Table (To) := Unit_List (From); used by sort - - function Lt (Left, Right : Natural) return Boolean; - -- Compares Left and Right units based on file name (first), - -- Chop_File (second) and Offset (third). This ordering is - -- important to keep the last version in case of duplicate files. - - package Unit_Sort is new GNAT.Heap_Sort_G (Move, Lt); - -- Used for sorting on filename to detect duplicates - - -------- - -- Lt -- - -------- - - function Lt (Left, Right : Natural) return Boolean is - L : Unit_Info renames - Unit.Table (Sorted_Units.Table (SUnit_Num (Left))); - - R : Unit_Info renames - Unit.Table (Sorted_Units.Table (SUnit_Num (Right))); - - begin - return L.File_Name.all < R.File_Name.all - or else (L.File_Name.all = R.File_Name.all - and then (L.Chop_File < R.Chop_File - or else (L.Chop_File = R.Chop_File - and then L.Offset < R.Offset))); - end Lt; - - ---------- - -- Move -- - ---------- - - procedure Move (From : Natural; To : Natural) is - begin - Sorted_Units.Table (SUnit_Num (To)) := - Sorted_Units.Table (SUnit_Num (From)); - end Move; - - -- Start of processing for Sort_Units - - begin - Sorted_Units.Set_Last (SUnit_Num (Unit.Last)); - - for J in 1 .. Unit.Last loop - Sorted_Units.Table (SUnit_Num (J)) := J; - end loop; - - -- Sort Unit.Table, using Sorted_Units.Table (0) as scratch - - Unit_Sort.Sort (Natural (Unit.Last)); - - -- Set the Sorted_Index fields in the unit tables - - for J in 1 .. SUnit_Num (Unit.Last) loop - Unit.Table (Sorted_Units.Table (J)).Sorted_Index := J; - end loop; - end Sort_Units; - - ----------- - -- Usage -- - ----------- - - procedure Usage is - begin - Put_Line - ("Usage: gnatchop [-c] [-h] [-k#] " & - "[-r] [-p] [-q] [-v] [-w] [-x] [--GCC=xx] file [file ...] [dir]"); - - New_Line; - Put_Line - (" -c compilation mode, configuration pragmas " & - "follow RM rules"); - - Put_Line - (" -gnatxxx passes the -gnatxxx switch to gnat parser"); - - Put_Line - (" -h help: output this usage information"); - - Put_Line - (" -k# krunch file names of generated files to " & - "no more than # characters"); - - Put_Line - (" -k krunch file names of generated files to " & - "no more than 8 characters"); - - Put_Line - (" -p preserve time stamp, output files will " & - "have same stamp as input"); - - Put_Line - (" -q quiet mode, no output of generated file " & - "names"); - - Put_Line - (" -r generate Source_Reference pragmas refer" & - "encing original source file"); - - Put_Line - (" -v verbose mode, output version and generat" & - "ed commands"); - - Put_Line - (" -w overwrite existing filenames"); - - Put_Line - (" -x exit on error"); - - Put_Line - (" --GCC=xx specify the path of the gnat parser to be used"); - - New_Line; - Put_Line - (" file... list of source files to be chopped"); - - Put_Line - (" dir directory location for split files (defa" & - "ult = current directory)"); - end Usage; - - ----------------- - -- Warning_Msg -- - ----------------- - - procedure Warning_Msg (Message : String) is - begin - Warning_Count := Warning_Count + 1; - Put_Line (Standard_Error, "warning: " & Message); - end Warning_Msg; - - ------------------------- - -- Write_Chopped_Files -- - ------------------------- - - function Write_Chopped_Files (Input : File_Num) return Boolean is - Name : aliased constant String := - File.Table (Input).Name.all & ASCII.Nul; - FD : File_Descriptor; - Buffer : String_Access; - Success : Boolean; - TS_Time : OS_Time; - - begin - FD := Open_Read (Name'Address, Binary); - TS_Time := File_Time_Stamp (FD); - - if FD = Invalid_FD then - Error_Msg ("cannot open " & File.Table (Input).Name.all); - return False; - end if; - - Read_File (FD, Buffer, Success); - - if not Success then - Error_Msg ("cannot read " & File.Table (Input).Name.all); - Close (FD); - return False; - end if; - - if not Quiet_Mode then - Put_Line ("splitting " & File.Table (Input).Name.all & " into:"); - end if; - - -- Only chop those units that come from this file - - for Num in 1 .. Unit.Last loop - if Unit.Table (Num).Chop_File = Input then - Write_Unit (Buffer, Num, TS_Time, Success); - exit when not Success; - end if; - end loop; - - Close (FD); - return Success; - - end Write_Chopped_Files; - - ----------------------- - -- Write_Config_File -- - ----------------------- - - procedure Write_Config_File (Input : File_Num; U : Unit_Num) is - FD : File_Descriptor; - Name : aliased constant String := "gnat.adc" & ASCII.NUL; - Buffer : String_Access; - Success : Boolean; - Append : Boolean; - Buffera : String_Access; - Bufferl : Natural; - - begin - Write_gnat_adc := True; - FD := Open_Read_Write (Name'Address, Binary); - - if FD = Invalid_FD then - FD := Create_File (Name'Address, Binary); - Append := False; - - if not Quiet_Mode then - Put_Line ("writing configuration pragmas from " & - File.Table (Input).Name.all & " to gnat.adc"); - end if; - - else - Append := True; - - if not Quiet_Mode then - Put_Line - ("appending configuration pragmas from " & - File.Table (Input).Name.all & " to gnat.adc"); - end if; - end if; - - Success := FD /= Invalid_FD; - - if not Success then - Error_Msg ("cannot create gnat.adc"); - return; - end if; - - -- In append mode, acquire existing gnat.adc file - - if Append then - Read_File (FD, Buffera, Success); - - if not Success then - Error_Msg ("cannot read gnat.adc"); - return; - end if; - - -- Find location of EOF byte if any to exclude from append - - Bufferl := 1; - while Bufferl <= Buffera'Last - and then Buffera (Bufferl) /= EOF - loop - Bufferl := Bufferl + 1; - end loop; - - Bufferl := Bufferl - 1; - Close (FD); - - -- Write existing gnat.adc to new gnat.adc file - - FD := Create_File (Name'Address, Binary); - Success := Write (FD, Buffera (1)'Address, Bufferl) = Bufferl; - - if not Success then - Error_Msg ("error writing gnat.adc"); - return; - end if; - end if; - - Buffer := Get_Config_Pragmas (Input, U); - - if Buffer /= null then - Success := Write (FD, Buffer.all'Address, Buffer'Length) = - Buffer'Length; - - if not Success then - Error_Msg ("disk full writing gnat.adc"); - return; - end if; - end if; - - Close (FD); - end Write_Config_File; - - ----------------------------------- - -- Write_Source_Reference_Pragma -- - ----------------------------------- - - procedure Write_Source_Reference_Pragma - (Info : Unit_Info; - Line : Line_Num; - FD : File_Descriptor; - EOL : EOL_String; - Success : in out Boolean) - is - FTE : File_Entry renames File.Table (Info.Chop_File); - Nam : String_Access; - - begin - if Success and Source_References and not Info.SR_Present then - if FTE.SR_Name /= null then - Nam := FTE.SR_Name; - else - Nam := FTE.Name; - end if; - - declare - Reference : aliased String := - "pragma Source_Reference (000000, """ - & Nam.all & """);" & EOL.Str; - - Pos : Positive := Reference'First; - Lin : Line_Num := Line; - - begin - while Reference (Pos + 1) /= ',' loop - Pos := Pos + 1; - end loop; - - while Reference (Pos) = '0' loop - Reference (Pos) := Character'Val - (Character'Pos ('0') + Lin mod 10); - Lin := Lin / 10; - Pos := Pos - 1; - end loop; - - -- Assume there are enough zeroes for any program length - - pragma Assert (Lin = 0); - - Success := - Write (FD, Reference'Address, Reference'Length) - = Reference'Length; - end; - end if; - end Write_Source_Reference_Pragma; - - ---------------- - -- Write_Unit -- - ---------------- - - procedure Write_Unit - (Source : access String; - Num : Unit_Num; - TS_Time : OS_Time; - Success : out Boolean) - is - Info : Unit_Info renames Unit.Table (Num); - FD : File_Descriptor; - Name : aliased constant String := Info.File_Name.all & ASCII.NUL; - Length : File_Offset; - EOL : constant EOL_String := - Get_EOL (Source, Source'First + Info.Offset); - - begin - -- Skip duplicated files - - if Is_Duplicated (Info.Sorted_Index) then - Put_Line (" " & Info.File_Name.all & " skipped"); - Success := Overwrite_Files; - return; - end if; - - if Overwrite_Files then - FD := Create_File (Name'Address, Binary); - else - FD := Create_New_File (Name'Address, Binary); - end if; - - Success := FD /= Invalid_FD; - - if not Success then - Error_Msg ("cannot create " & Info.File_Name.all); - return; - end if; - - -- A length of 0 indicates that the rest of the file belongs to - -- this unit. The actual length must be calculated now. Take into - -- account that the last character (EOF) must not be written. - - if Info.Length = 0 then - Length := Source'Last - (Source'First + Info.Offset); - else - Length := Info.Length; - end if; - - -- Prepend configuration pragmas if necessary - - if Success and then Info.Bufferg /= null then - Write_Source_Reference_Pragma (Info, 1, FD, EOL, Success); - Success := - Write (FD, Info.Bufferg.all'Address, Info.Bufferg'Length) = - Info.Bufferg'Length; - end if; - - Write_Source_Reference_Pragma (Info, Info.Start_Line, FD, EOL, Success); - - if Success then - Success := Write (FD, Source (Source'First + Info.Offset)'Address, - Length) = Length; - end if; - - if not Success then - Error_Msg ("disk full writing " & Info.File_Name.all); - return; - end if; - - if not Quiet_Mode then - Put_Line (" " & Info.File_Name.all); - end if; - - Close (FD); - - if Preserve_Mode then - File_Time_Stamp (Name'Address, TS_Time); - end if; - - end Write_Unit; - --- Start of processing for gnatchop - -begin - -- Add the directory where gnatchop is invoked in front of the - -- path, if gnatchop is invoked with directory information. - -- Only do this if the platform is not VMS, where the notion of path - -- does not really exist. - - if not Hostparm.OpenVMS then - declare - Command : constant String := Command_Name; - - begin - for Index in reverse Command'Range loop - if Command (Index) = Directory_Separator then - declare - Absolute_Dir : constant String := - Normalize_Pathname - (Command (Command'First .. Index)); - - PATH : constant String := - Absolute_Dir & - Path_Separator & - Getenv ("PATH").all; - - begin - Setenv ("PATH", PATH); - end; - - exit; - end if; - end loop; - end; - end if; - - -- Process command line options and initialize global variables - - if not Scan_Arguments then - Set_Exit_Status (Failure); - return; - end if; - - -- Check presence of required executables - - Gnat_Cmd := Locate_Executable (Gcc.all, not Gcc_Set); - - if Gnat_Cmd = null then - goto No_Files_Written; - end if; - - -- First parse all files and read offset information - - for Num in 1 .. File.Last loop - if not Parse_File (Num) then - goto No_Files_Written; - end if; - end loop; - - -- Check if any units have been found (assumes non-empty Unit.Table) - - if Unit.Last = 0 then - if not Write_gnat_adc then - Error_Msg ("no compilation units found", Warning => True); - end if; - - goto No_Files_Written; - end if; - - Sort_Units; - - -- Check if any duplicate files would be created. If so, emit - -- a warning if Overwrite_Files is true, otherwise generate an error. - - if Report_Duplicate_Units and then not Overwrite_Files then - goto No_Files_Written; - end if; - - -- Check if any files exist, if so do not write anything - -- Because all files have been parsed and checked already, - -- there won't be any duplicates - - if not Overwrite_Files and then Files_Exist then - goto No_Files_Written; - end if; - - -- After this point, all source files are read in succession - -- and chopped into their destination files. - - -- As the Source_File_Name pragmas are handled as logical file 0, - -- write it first. - - for F in 1 .. File.Last loop - if not Write_Chopped_Files (F) then - Set_Exit_Status (Failure); - return; - end if; - end loop; - - if Warning_Count > 0 then - declare - Warnings_Msg : constant String := Warning_Count'Img & " warning(s)"; - begin - Error_Msg (Warnings_Msg (2 .. Warnings_Msg'Last), Warning => True); - end; - end if; - - return; - -<<No_Files_Written>> - - -- Special error exit for all situations where no files have - -- been written. - - if not Write_gnat_adc then - Error_Msg ("no source files written", Warning => True); - end if; - - return; - -exception - when Terminate_Program => - null; - -end Gnatchop; |