ada: Remove extra parentheses
[official-gcc.git] / gcc / ada / gprep.adb
blob5b589558458aecd6e3433d313d0ad84523e9067f
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT COMPILER COMPONENTS --
4 -- --
5 -- G P R E P --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2002-2023, Free Software Foundation, Inc. --
10 -- --
11 -- GNAT is free software; you can redistribute it and/or modify it under --
12 -- terms of the GNU General Public License as published by the Free Soft- --
13 -- ware Foundation; either version 3, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. 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 COPYING3. If not, go to --
19 -- http://www.gnu.org/licenses for a complete copy of the license. --
20 -- --
21 -- GNAT was originally developed by the GNAT team at New York University. --
22 -- Extensive contributions were provided by Ada Core Technologies Inc. --
23 -- --
24 ------------------------------------------------------------------------------
26 with Atree; use Atree;
27 with Csets;
28 with Errutil;
29 with Namet; use Namet;
30 with Opt;
31 with Osint; use Osint;
32 with Output; use Output;
33 with Prep; use Prep;
34 with Scng;
35 with Sinput.C;
36 with Snames;
37 with Stringt; use Stringt;
38 with Switch; use Switch;
39 with Types; use Types;
40 with Uintp;
42 with Ada.Command_Line; use Ada.Command_Line;
43 with Ada.Text_IO; use Ada.Text_IO;
45 with GNAT.Case_Util; use GNAT.Case_Util;
46 with GNAT.Command_Line;
47 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
49 with System.OS_Lib; use System.OS_Lib;
51 package body GPrep is
53 Copyright_Displayed : Boolean := False;
54 -- Used to prevent multiple displays of the copyright notice
56 ------------------------
57 -- Argument Line Data --
58 ------------------------
60 Unix_Line_Terminators : Boolean := False;
61 -- Set to True with option -T
63 type String_Array is array (Boolean) of String_Access;
64 Yes_No : constant String_Array :=
65 (False => new String'("YES"),
66 True => new String'("NO"));
68 Infile_Name : Name_Id := No_Name;
69 Outfile_Name : Name_Id := No_Name;
70 Deffile_Name : Name_Id := No_Name;
72 Output_Directory : Name_Id := No_Name;
73 -- Used when the specified output is an existing directory
75 Input_Directory : Name_Id := No_Name;
76 -- Used when the specified input and output are existing directories
78 Source_Ref_Pragma : Boolean := False;
79 -- Record command line options (set if -r switch set)
81 Text_Outfile : aliased Ada.Text_IO.File_Type;
82 Outfile : constant File_Access := Text_Outfile'Access;
84 File_Name_Buffer_Initial_Size : constant := 50;
85 File_Name_Buffer : String_Access :=
86 new String (1 .. File_Name_Buffer_Initial_Size);
87 -- A buffer to build output file names from input file names
89 -----------------
90 -- Subprograms --
91 -----------------
93 procedure Display_Copyright;
94 -- Display the copyright notice
96 procedure Post_Scan is null;
97 -- Needed by instantiation of Scng below
99 package Scanner is new Scng
100 (Post_Scan,
101 Errutil.Error_Msg,
102 Errutil.Error_Msg_S,
103 Errutil.Error_Msg_SC,
104 Errutil.Error_Msg_SP,
105 Errutil.Style);
106 -- The scanner for the preprocessor
108 function Is_ASCII_Letter (C : Character) return Boolean;
109 -- True if C is in 'a' .. 'z' or in 'A' .. 'Z'
111 procedure Double_File_Name_Buffer;
112 -- Double the size of the file name buffer
114 procedure Preprocess_Infile_Name;
115 -- When the specified output is a directory, preprocess the infile name
116 -- for symbol substitution, to get the output file name.
118 procedure Process_Files;
119 -- Process the single input file or all the files in the directory tree
120 -- rooted at the input directory.
122 procedure Process_Command_Line_Symbol_Definition (S : String);
123 -- Process a -D switch on the command line
125 procedure Put_Char_To_Outfile (C : Character);
126 -- Output one character to the output file. Used to initialize the
127 -- preprocessor.
129 procedure New_EOL_To_Outfile;
130 -- Output a new line to the output file. Used to initialize the
131 -- preprocessor.
133 procedure Scan_Command_Line;
134 -- Scan the switches and the file names
136 procedure Usage;
137 -- Display the usage
139 -----------------------
140 -- Display_Copyright --
141 -----------------------
143 procedure Display_Copyright is
144 begin
145 if not Copyright_Displayed then
146 Display_Version ("GNAT Preprocessor", "1996");
147 Copyright_Displayed := True;
148 end if;
149 end Display_Copyright;
151 -----------------------------
152 -- Double_File_Name_Buffer --
153 -----------------------------
155 procedure Double_File_Name_Buffer is
156 New_Buffer : constant String_Access :=
157 new String (1 .. 2 * File_Name_Buffer'Length);
158 begin
159 New_Buffer (File_Name_Buffer'Range) := File_Name_Buffer.all;
160 Free (File_Name_Buffer);
161 File_Name_Buffer := New_Buffer;
162 end Double_File_Name_Buffer;
164 --------------
165 -- Gnatprep --
166 --------------
168 procedure Gnatprep is
169 begin
170 -- Do some initializations (order is important here)
172 Csets.Initialize;
173 Uintp.Initialize;
174 Snames.Initialize;
175 Stringt.Initialize;
176 Prep.Initialize;
178 -- Initialize the preprocessor
180 Prep.Setup_Hooks
181 (Error_Msg => Errutil.Error_Msg'Access,
182 Scan => Scanner.Scan'Access,
183 Set_Ignore_Errors => Errutil.Set_Ignore_Errors'Access,
184 Put_Char => Put_Char_To_Outfile'Access,
185 New_EOL => New_EOL_To_Outfile'Access);
187 -- Set the scanner characteristics for the preprocessor
189 Scanner.Set_Special_Character ('#');
190 Scanner.Set_Special_Character ('$');
191 Scanner.Set_End_Of_Line_As_Token (True);
193 -- Initialize the mapping table of symbols to values
195 Prep.Symbol_Table.Init (Prep.Mapping);
197 -- Parse the switches and arguments
199 Scan_Command_Line;
201 if Opt.Verbose_Mode then
202 Display_Copyright;
203 end if;
205 -- Test we had all the arguments needed
207 if Infile_Name = No_Name then
209 -- No input file specified, just output the usage and exit
211 if Argument_Count = 0 then
212 Usage;
213 else
214 GNAT.Command_Line.Try_Help;
215 end if;
217 return;
219 elsif Outfile_Name = No_Name then
221 -- No output file specified, exit
223 GNAT.Command_Line.Try_Help;
224 return;
225 end if;
227 -- If a pragma Source_File_Name, we need to keep line numbers. So, if
228 -- the deleted lines are not put as comment, we must output them as
229 -- blank lines.
231 if Source_Ref_Pragma and not Opt.Comment_Deleted_Lines then
232 Opt.Blank_Deleted_Lines := True;
233 end if;
235 -- If we have a definition file, parse it
237 if Deffile_Name /= No_Name then
238 declare
239 Deffile : Source_File_Index;
241 begin
242 Errutil.Initialize;
243 Deffile := Sinput.C.Load_File (Get_Name_String (Deffile_Name));
245 -- Set Main_Source_File to the definition file for the benefit of
246 -- Errutil.Finalize.
248 Sinput.Main_Source_File := Deffile;
250 if Deffile = No_Source_File then
251 Fail ("unable to find definition file """
252 & Get_Name_String (Deffile_Name)
253 & """");
254 elsif Deffile = No_Access_To_Source_File then
255 Fail ("unabled to read definition file """
256 & Get_Name_String (Deffile_Name)
257 & """");
258 end if;
260 Scanner.Initialize_Scanner (Deffile);
262 -- Parse the definition file without "replace in comments"
264 declare
265 Replace : constant Boolean := Opt.Replace_In_Comments;
266 begin
267 Opt.Replace_In_Comments := False;
268 Prep.Parse_Def_File;
269 Opt.Replace_In_Comments := Replace;
270 end;
271 end;
272 end if;
274 -- If there are errors in the definition file, output them and exit
276 if Total_Errors_Detected > 0 then
277 Errutil.Finalize (Source_Type => "definition");
278 Fail ("errors in definition file """
279 & Get_Name_String (Deffile_Name)
280 & """");
281 end if;
283 -- If -s switch was specified, print a sorted list of symbol names and
284 -- values, if any.
286 if Opt.List_Preprocessing_Symbols then
287 Prep.List_Symbols (Foreword => "");
288 end if;
290 Output_Directory := No_Name;
291 Input_Directory := No_Name;
293 -- Check if the specified output is an existing directory
295 if Is_Directory (Get_Name_String (Outfile_Name)) then
296 Output_Directory := Outfile_Name;
298 -- As the output is an existing directory, check if the input too
299 -- is a directory.
301 if Is_Directory (Get_Name_String (Infile_Name)) then
302 Input_Directory := Infile_Name;
303 end if;
304 end if;
306 -- And process the single input or the files in the directory tree
307 -- rooted at the input directory.
309 Process_Files;
310 end Gnatprep;
312 ---------------------
313 -- Is_ASCII_Letter --
314 ---------------------
316 function Is_ASCII_Letter (C : Character) return Boolean is
317 begin
318 return C in 'A' .. 'Z' or else C in 'a' .. 'z';
319 end Is_ASCII_Letter;
321 ------------------------
322 -- New_EOL_To_Outfile --
323 ------------------------
325 procedure New_EOL_To_Outfile is
326 begin
327 New_Line (Outfile.all);
328 end New_EOL_To_Outfile;
330 ----------------------------
331 -- Preprocess_Infile_Name --
332 ----------------------------
334 procedure Preprocess_Infile_Name is
335 Len : Natural;
336 First : Positive;
337 Last : Natural;
338 Symbol : Name_Id;
339 Data : Symbol_Data;
341 begin
342 -- Initialize the buffer with the name of the input file
344 Get_Name_String (Infile_Name);
345 Len := Name_Len;
347 while File_Name_Buffer'Length < Len loop
348 Double_File_Name_Buffer;
349 end loop;
351 File_Name_Buffer (1 .. Len) := Name_Buffer (1 .. Len);
353 -- Look for possible symbols in the file name
355 First := 1;
356 while First < Len loop
358 -- A symbol starts with a dollar sign followed by a letter
360 if File_Name_Buffer (First) = '$' and then
361 Is_ASCII_Letter (File_Name_Buffer (First + 1))
362 then
363 Last := First + 1;
365 -- Find the last letter of the symbol
367 while Last < Len and then
368 Is_ASCII_Letter (File_Name_Buffer (Last + 1))
369 loop
370 Last := Last + 1;
371 end loop;
373 -- Get the symbol name id
375 Name_Len := Last - First;
376 Name_Buffer (1 .. Name_Len) :=
377 File_Name_Buffer (First + 1 .. Last);
378 To_Lower (Name_Buffer (1 .. Name_Len));
379 Symbol := Name_Find;
381 -- And look for this symbol name in the symbol table
383 for Index in 1 .. Symbol_Table.Last (Mapping) loop
384 Data := Mapping.Table (Index);
386 if Data.Symbol = Symbol then
388 -- We found the symbol. If its value is not a string,
389 -- replace the symbol in the file name with the value of
390 -- the symbol.
392 if not Data.Is_A_String then
393 String_To_Name_Buffer (Data.Value);
395 declare
396 Sym_Len : constant Positive := Last - First + 1;
397 Offset : constant Integer := Name_Len - Sym_Len;
398 New_Len : constant Natural := Len + Offset;
400 begin
401 while New_Len > File_Name_Buffer'Length loop
402 Double_File_Name_Buffer;
403 end loop;
405 File_Name_Buffer (Last + 1 + Offset .. New_Len) :=
406 File_Name_Buffer (Last + 1 .. Len);
407 Len := New_Len;
408 Last := Last + Offset;
409 File_Name_Buffer (First .. Last) :=
410 Name_Buffer (1 .. Name_Len);
411 end;
412 end if;
414 exit;
415 end if;
416 end loop;
418 -- Skip over the symbol name or its value: we are not checking
419 -- for another symbol name in the value.
421 First := Last + 1;
423 else
424 First := First + 1;
425 end if;
426 end loop;
428 -- We now have the output file name in the buffer. Get the output
429 -- path and put it in Outfile_Name.
431 Get_Name_String (Output_Directory);
432 Add_Char_To_Name_Buffer (Directory_Separator);
433 Add_Str_To_Name_Buffer (File_Name_Buffer (1 .. Len));
434 Outfile_Name := Name_Find;
435 end Preprocess_Infile_Name;
437 --------------------------------------------
438 -- Process_Command_Line_Symbol_Definition --
439 --------------------------------------------
441 procedure Process_Command_Line_Symbol_Definition (S : String) is
442 Data : Symbol_Data;
443 Symbol : Symbol_Id;
445 begin
446 -- Check the symbol definition and get the symbol and its value.
447 -- Fail if symbol definition is illegal.
449 Check_Command_Line_Symbol_Definition (S, Data);
451 Symbol := Index_Of (Data.Symbol);
453 -- If symbol does not already exist, create a new entry in the mapping
454 -- table.
456 if Symbol = No_Symbol then
457 Symbol_Table.Increment_Last (Mapping);
458 Symbol := Symbol_Table.Last (Mapping);
459 end if;
461 Mapping.Table (Symbol) := Data;
462 end Process_Command_Line_Symbol_Definition;
464 -------------------
465 -- Process_Files --
466 -------------------
468 procedure Process_Files is
470 procedure Process_One_File;
471 -- Process input file Infile_Name and put the result in file
472 -- Outfile_Name.
474 procedure Recursive_Process (In_Dir : String; Out_Dir : String);
475 -- Process recursively files in In_Dir. Results go to Out_Dir
477 ----------------------
478 -- Process_One_File --
479 ----------------------
481 procedure Process_One_File is
482 Infile : Source_File_Index;
484 Modified : Boolean;
485 pragma Warnings (Off, Modified);
487 begin
488 -- Create the output file (fails if this does not work)
490 begin
491 Create
492 (File => Text_Outfile,
493 Mode => Out_File,
494 Name => Get_Name_String (Outfile_Name),
495 Form => "Text_Translation=" &
496 Yes_No (Unix_Line_Terminators).all);
498 exception
499 when others =>
500 Fail
501 ("unable to create output file """
502 & Get_Name_String (Outfile_Name)
503 & """");
504 end;
506 -- Load the input file
508 Infile := Sinput.C.Load_File (Get_Name_String (Infile_Name));
510 if Infile = No_Source_File then
511 Fail ("unable to find input file """
512 & Get_Name_String (Infile_Name)
513 & """");
514 elsif Infile = No_Access_To_Source_File then
515 Fail ("unable to read input file """
516 & Get_Name_String (Infile_Name)
517 & """");
518 end if;
520 -- Set Main_Source_File to the input file for the benefit of
521 -- Errutil.Finalize.
523 Sinput.Main_Source_File := Infile;
525 Scanner.Initialize_Scanner (Infile);
527 -- Output the pragma Source_Reference if asked to
529 if Source_Ref_Pragma then
530 Put_Line
531 (Outfile.all,
532 "pragma Source_Reference (1, """ &
533 Get_Name_String (Sinput.Full_File_Name (Infile)) & """);");
534 end if;
536 -- Preprocess the input file
538 Prep.Preprocess (Modified);
540 -- In verbose mode, if there is no error, report it
542 if Opt.Verbose_Mode and then Total_Errors_Detected = 0 then
543 Errutil.Finalize (Source_Type => "input");
544 end if;
546 -- If we had some errors, delete the output file, and report them
548 if Total_Errors_Detected > 0 then
549 if Outfile /= Standard_Output then
550 Delete (Text_Outfile);
551 end if;
553 Errutil.Finalize (Source_Type => "input");
555 OS_Exit (0);
557 -- Otherwise, close the output file, and we are done
559 elsif Outfile /= Standard_Output then
560 Close (Text_Outfile);
561 end if;
562 end Process_One_File;
564 -----------------------
565 -- Recursive_Process --
566 -----------------------
568 procedure Recursive_Process (In_Dir : String; Out_Dir : String) is
569 Dir_In : Dir_Type;
570 Name : String (1 .. 255);
571 Last : Natural;
572 In_Dir_Name : Name_Id;
573 Out_Dir_Name : Name_Id;
575 procedure Set_Directory_Names;
576 -- Establish or reestablish the current input and output directories
578 -------------------------
579 -- Set_Directory_Names --
580 -------------------------
582 procedure Set_Directory_Names is
583 begin
584 Input_Directory := In_Dir_Name;
585 Output_Directory := Out_Dir_Name;
586 end Set_Directory_Names;
588 -- Start of processing for Recursive_Process
590 begin
591 -- Open the current input directory
593 begin
594 Open (Dir_In, In_Dir);
596 exception
597 when Directory_Error =>
598 Fail ("could not read directory " & In_Dir);
599 end;
601 -- Set the new input and output directory names
603 Name_Len := In_Dir'Length;
604 Name_Buffer (1 .. Name_Len) := In_Dir;
605 In_Dir_Name := Name_Find;
606 Name_Len := Out_Dir'Length;
607 Name_Buffer (1 .. Name_Len) := Out_Dir;
608 Out_Dir_Name := Name_Find;
610 Set_Directory_Names;
612 -- Traverse the input directory
613 loop
614 Read (Dir_In, Name, Last);
615 exit when Last = 0;
617 if Name (1 .. Last) /= "." and then Name (1 .. Last) /= ".." then
618 declare
619 Input : constant String :=
620 In_Dir & Directory_Separator & Name (1 .. Last);
621 Output : constant String :=
622 Out_Dir & Directory_Separator & Name (1 .. Last);
624 begin
625 -- If input is an ordinary file, process it
627 if Is_Regular_File (Input) then
628 -- First get the output file name
630 Name_Len := Last;
631 Name_Buffer (1 .. Name_Len) := Name (1 .. Last);
632 Infile_Name := Name_Find;
633 Preprocess_Infile_Name;
635 -- Set the input file name and process the file
637 Name_Len := Input'Length;
638 Name_Buffer (1 .. Name_Len) := Input;
639 Infile_Name := Name_Find;
640 Process_One_File;
642 elsif Is_Directory (Input) then
643 -- Input is a directory. If the corresponding output
644 -- directory does not already exist, create it.
646 if not Is_Directory (Output) then
647 begin
648 Make_Dir (Dir_Name => Output);
650 exception
651 when Directory_Error =>
652 Fail ("could not create directory """
653 & Output
654 & """");
655 end;
656 end if;
658 -- And process this new input directory
660 Recursive_Process (Input, Output);
662 -- Reestablish the input and output directory names
663 -- that have been modified by the recursive call.
665 Set_Directory_Names;
666 end if;
667 end;
668 end if;
669 end loop;
670 end Recursive_Process;
672 -- Start of processing for Process_Files
674 begin
675 if Output_Directory = No_Name then
677 -- If the output is not a directory, fail if the input is
678 -- an existing directory, to avoid possible problems.
680 if Is_Directory (Get_Name_String (Infile_Name)) then
681 Fail ("input file """ & Get_Name_String (Infile_Name) &
682 """ is a directory");
683 end if;
685 -- Just process the single input file
687 Process_One_File;
689 elsif Input_Directory = No_Name then
691 -- Get the output file name from the input file name, and process
692 -- the single input file.
694 Preprocess_Infile_Name;
695 Process_One_File;
697 else
698 -- Recursively process files in the directory tree rooted at the
699 -- input directory.
701 Recursive_Process
702 (In_Dir => Get_Name_String (Input_Directory),
703 Out_Dir => Get_Name_String (Output_Directory));
704 end if;
705 end Process_Files;
707 -------------------------
708 -- Put_Char_To_Outfile --
709 -------------------------
711 procedure Put_Char_To_Outfile (C : Character) is
712 begin
713 Put (Outfile.all, C);
714 end Put_Char_To_Outfile;
716 -----------------------
717 -- Scan_Command_Line --
718 -----------------------
720 procedure Scan_Command_Line is
721 Switch : Character;
723 procedure Check_Version_And_Help is new Check_Version_And_Help_G (Usage);
725 -- Start of processing for Scan_Command_Line
727 begin
728 -- First check for --version or --help
730 Check_Version_And_Help ("GNATPREP", "1996");
732 -- Now scan the other switches
734 GNAT.Command_Line.Initialize_Option_Scan;
736 loop
737 begin
738 Switch := GNAT.Command_Line.Getopt ("D: a b c C r s T u v");
740 case Switch is
741 when ASCII.NUL =>
742 exit;
744 when 'D' =>
745 Process_Command_Line_Symbol_Definition
746 (S => GNAT.Command_Line.Parameter);
748 when 'a' =>
749 Opt.No_Deletion := True;
750 Opt.Undefined_Symbols_Are_False := True;
752 when 'b' =>
753 Opt.Blank_Deleted_Lines := True;
755 when 'c' =>
756 Opt.Comment_Deleted_Lines := True;
758 when 'C' =>
759 Opt.Replace_In_Comments := True;
761 when 'r' =>
762 Source_Ref_Pragma := True;
764 when 's' =>
765 Opt.List_Preprocessing_Symbols := True;
767 when 'T' =>
768 Unix_Line_Terminators := True;
770 when 'u' =>
771 Opt.Undefined_Symbols_Are_False := True;
773 when 'v' =>
774 Opt.Verbose_Mode := True;
776 when others =>
777 Fail ("Invalid Switch: -" & Switch);
778 end case;
780 exception
781 when GNAT.Command_Line.Invalid_Switch =>
782 Write_Str ("Invalid Switch: -");
783 Write_Line (GNAT.Command_Line.Full_Switch);
784 GNAT.Command_Line.Try_Help;
785 OS_Exit (1);
786 end;
787 end loop;
789 -- Get the file names
791 loop
792 declare
793 S : constant String := GNAT.Command_Line.Get_Argument;
795 begin
796 exit when S'Length = 0;
798 Name_Len := S'Length;
799 Name_Buffer (1 .. Name_Len) := S;
801 if Infile_Name = No_Name then
802 Infile_Name := Name_Find;
803 elsif Outfile_Name = No_Name then
804 Outfile_Name := Name_Find;
805 elsif Deffile_Name = No_Name then
806 Deffile_Name := Name_Find;
807 else
808 Fail ("too many arguments specified");
809 end if;
810 end;
811 end loop;
812 end Scan_Command_Line;
814 -----------
815 -- Usage --
816 -----------
818 procedure Usage is
819 begin
820 Display_Copyright;
821 Write_Line ("Usage: gnatprep [-bcrsuv] [-Dsymbol=value] " &
822 "infile outfile [deffile]");
823 Write_Eol;
824 Write_Line (" infile Name of the input file");
825 Write_Line (" outfile Name of the output file");
826 Write_Line (" deffile Name of the definition file");
827 Write_Eol;
828 Write_Line ("gnatprep switches:");
829 Display_Usage_Version_And_Help;
830 Write_Line (" -b Replace preprocessor lines by blank lines");
831 Write_Line (" -c Keep preprocessor lines as comments");
832 Write_Line (" -C Do symbol replacements within comments");
833 Write_Line (" -D Associate symbol with value");
834 Write_Line (" -r Generate Source_Reference pragma");
835 Write_Line (" -s Print a sorted list of symbol names and values");
836 Write_Line (" -T Use LF as line terminators");
837 Write_Line (" -u Treat undefined symbols as FALSE");
838 Write_Line (" -v Verbose mode");
839 Write_Eol;
840 end Usage;
842 end GPrep;