1 ------------------------------------------------------------------------------
3 -- GNAT COMPILER COMPONENTS --
9 -- Copyright (C) 2002-2004, 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 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, 59 Temple Place - Suite 330, Boston, --
20 -- MA 02111-1307, USA. --
22 -- GNAT was originally developed by the GNAT team at New York University. --
23 -- Extensive contributions were provided by Ada Core Technologies Inc. --
25 ------------------------------------------------------------------------------
28 with Err_Vars; use Err_Vars;
31 with Namet; use Namet;
33 with Osint; use Osint;
34 with Output; use Output;
39 with Stringt; use Stringt;
40 with Types; use Types;
42 with Ada.Text_IO; use Ada.Text_IO;
43 with GNAT.Case_Util; use GNAT.Case_Util;
44 with GNAT.Command_Line;
45 with GNAT.Directory_Operations; use GNAT.Directory_Operations;
46 with GNAT.OS_Lib; use GNAT.OS_Lib;
50 Copyright_Displayed : Boolean := False;
51 -- Used to prevent multiple displays of the copyright notice
53 ------------------------
54 -- Argument Line Data --
55 ------------------------
57 Infile_Name : Name_Id := No_Name;
58 Outfile_Name : Name_Id := No_Name;
59 Deffile_Name : Name_Id := No_Name;
61 Output_Directory : Name_Id := No_Name;
62 -- Used when the specified output is an existing directory
64 Input_Directory : Name_Id := No_Name;
65 -- Used when the specified input and output are existing directories
67 Source_Ref_Pragma : Boolean := False;
68 -- Record command line options (set if -r switch set)
70 Text_Outfile : aliased Ada.Text_IO.File_Type;
71 Outfile : constant File_Access := Text_Outfile'Access;
73 File_Name_Buffer_Initial_Size : constant := 50;
74 File_Name_Buffer : String_Access :=
75 new String (1 .. File_Name_Buffer_Initial_Size);
76 -- A buffer to build output file names from input file names.
82 procedure Display_Copyright;
83 -- Display the copyright notice
86 -- Null procedure, needed by instantiation of Scng below
88 package Scanner is new Scng
95 -- The scanner for the preprocessor
97 function Is_ASCII_Letter (C : Character) return Boolean;
98 -- True if C is in 'a' .. 'z' or in 'A' .. 'Z'
100 procedure Double_File_Name_Buffer;
101 -- Double the size of the file name buffer.
103 procedure Preprocess_Infile_Name;
104 -- When the specified output is a directory, preprocess the infile name
105 -- for symbol substitution, to get the output file name.
107 procedure Process_Files;
108 -- Process the single input file or all the files in the directory tree
109 -- rooted at the input directory.
111 procedure Process_Command_Line_Symbol_Definition (S : String);
112 -- Process a -D switch on the command line
114 procedure Put_Char_To_Outfile (C : Character);
115 -- Output one character to the output file.
116 -- Used to initialize the preprocessor.
118 procedure New_EOL_To_Outfile;
119 -- Output a new line to the output file.
120 -- Used to initialize the preprocessor.
122 procedure Scan_Command_Line;
123 -- Scan the switches and the file names
128 -----------------------
129 -- Display_Copyright --
130 -----------------------
132 procedure Display_Copyright is
134 if not Copyright_Displayed then
135 Write_Line ("GNAT Preprocessor " & Gnatvsn.Gnat_Version_String);
136 Write_Line ("Copyright 1996-2004 Free Software Foundation, Inc.");
137 Copyright_Displayed := True;
139 end Display_Copyright;
141 -----------------------------
142 -- Double_File_Name_Buffer --
143 -----------------------------
145 procedure Double_File_Name_Buffer is
146 New_Buffer : constant String_Access :=
147 new String (1 .. 2 * File_Name_Buffer'Length);
149 New_Buffer (File_Name_Buffer'Range) := File_Name_Buffer.all;
150 Free (File_Name_Buffer);
151 File_Name_Buffer := New_Buffer;
152 end Double_File_Name_Buffer;
158 procedure Gnatprep is
160 -- Do some initializations (order is important here!)
167 -- Initialize the preprocessor
170 (Error_Msg => Errutil.Error_Msg'Access,
171 Scan => Scanner.Scan'Access,
172 Set_Ignore_Errors => Errutil.Set_Ignore_Errors'Access,
173 Put_Char => Put_Char_To_Outfile'Access,
174 New_EOL => New_EOL_To_Outfile'Access);
176 -- Set the scanner characteristics for the preprocessor
178 Scanner.Set_Special_Character ('#');
179 Scanner.Set_Special_Character ('$');
180 Scanner.Set_End_Of_Line_As_Token (True);
182 -- Initialize the mapping table of symbols to values
184 Prep.Symbol_Table.Init (Prep.Mapping);
186 -- Parse the switches and arguments
190 if Opt.Verbose_Mode then
194 -- Test we had all the arguments needed
196 if Infile_Name = No_Name then
197 -- No input file specified, just output the usage and exit
202 elsif Outfile_Name = No_Name then
203 -- No output file specified, just output the usage and exit
209 -- If a pragma Source_File_Name, we need to keep line numbers.
210 -- So, if the deleted lines are not put as comment, we must output them
213 if Source_Ref_Pragma and (not Opt.Comment_Deleted_Lines) then
214 Opt.Blank_Deleted_Lines := True;
217 -- If we have a definition file, parse it
219 if Deffile_Name /= No_Name then
221 Deffile : Source_File_Index;
225 Deffile := Sinput.C.Load_File (Get_Name_String (Deffile_Name));
227 -- Set Main_Source_File to the definition file for the benefit of
230 Sinput.Main_Source_File := Deffile;
232 if Deffile = No_Source_File then
233 Fail ("unable to find definition file """,
234 Get_Name_String (Deffile_Name),
238 Scanner.Initialize_Scanner (No_Unit, Deffile);
244 -- If there are errors in the definition file, output these errors
247 if Total_Errors_Detected > 0 then
248 Errutil.Finalize (Source_Type => "definition");
249 Fail ("errors in definition file """,
250 Get_Name_String (Deffile_Name), """");
253 -- If -s switch was specified, print a sorted list of symbol names and
256 if Opt.List_Preprocessing_Symbols then
257 Prep.List_Symbols (Foreword => "");
260 Output_Directory := No_Name;
261 Input_Directory := No_Name;
263 -- Check if the specified output is an existing directory
265 if Is_Directory (Get_Name_String (Outfile_Name)) then
266 Output_Directory := Outfile_Name;
268 -- As the output is an existing directory, check if the input too
271 if Is_Directory (Get_Name_String (Infile_Name)) then
272 Input_Directory := Infile_Name;
276 -- And process the single input or the files in the directory tree
277 -- rooted at the input directory.
283 ---------------------
284 -- Is_ASCII_Letter --
285 ---------------------
287 function Is_ASCII_Letter (C : Character) return Boolean is
289 return C in 'A' .. 'Z' or else C in 'a' .. 'z';
292 ------------------------
293 -- New_EOL_To_Outfile --
294 ------------------------
296 procedure New_EOL_To_Outfile is
298 New_Line (Outfile.all);
299 end New_EOL_To_Outfile;
305 procedure Post_Scan is
310 ----------------------------
311 -- Preprocess_Infile_Name --
312 ----------------------------
314 procedure Preprocess_Infile_Name is
316 First : Positive := 1;
322 -- Initialize the buffer with the name of the input file
324 Get_Name_String (Infile_Name);
327 while File_Name_Buffer'Length < Len loop
328 Double_File_Name_Buffer;
331 File_Name_Buffer (1 .. Len) := Name_Buffer (1 .. Len);
333 -- Look for possible symbols in the file name
335 while First < Len loop
337 -- A symbol starts with a dollar sign followed by a letter
339 if File_Name_Buffer (First) = '$' and then
340 Is_ASCII_Letter (File_Name_Buffer (First + 1))
344 -- Find the last letter of the symbol
346 while Last < Len and then
347 Is_ASCII_Letter (File_Name_Buffer (Last + 1))
352 -- Get the symbol name id
354 Name_Len := Last - First;
355 Name_Buffer (1 .. Name_Len) :=
356 File_Name_Buffer (First + 1 .. Last);
357 To_Lower (Name_Buffer (1 .. Name_Len));
360 -- And look for this symbol name in the symbol table
362 for Index in 1 .. Symbol_Table.Last (Mapping) loop
363 Data := Mapping.Table (Index);
365 if Data.Symbol = Symbol then
367 -- We found the symbol. If its value is not a string,
368 -- replace the symbol in the file name with the value of
371 if not Data.Is_A_String then
372 String_To_Name_Buffer (Data.Value);
375 Sym_Len : constant Positive := Last - First + 1;
376 Offset : constant Integer := Name_Len - Sym_Len;
377 New_Len : constant Natural := Len + Offset;
380 while New_Len > File_Name_Buffer'Length loop
381 Double_File_Name_Buffer;
384 File_Name_Buffer (Last + 1 + Offset .. New_Len) :=
385 File_Name_Buffer (Last + 1 .. Len);
387 Last := Last + Offset;
388 File_Name_Buffer (First .. Last) :=
389 Name_Buffer (1 .. Name_Len);
397 -- Skip over the symbol name or its value: we are not checking
398 -- for another symbol name in the value.
407 -- We now have the output file name in the buffer. Get the output
408 -- path and put it in Outfile_Name.
410 Get_Name_String (Output_Directory);
411 Add_Char_To_Name_Buffer (Directory_Separator);
412 Add_Str_To_Name_Buffer (File_Name_Buffer (1 .. Len));
413 Outfile_Name := Name_Find;
414 end Preprocess_Infile_Name;
416 --------------------------------------------
417 -- Process_Command_Line_Symbol_Definition --
418 --------------------------------------------
420 procedure Process_Command_Line_Symbol_Definition (S : String) is
425 -- Check the symbol definition and get the symbol and its value.
426 -- Fail if symbol definition is illegal.
428 Check_Command_Line_Symbol_Definition (S, Data);
430 Symbol := Index_Of (Data.Symbol);
432 -- If symbol does not alrady exist, create a new entry in the mapping
435 if Symbol = No_Symbol then
436 Symbol_Table.Increment_Last (Mapping);
437 Symbol := Symbol_Table.Last (Mapping);
440 Mapping.Table (Symbol) := Data;
441 end Process_Command_Line_Symbol_Definition;
447 procedure Process_Files is
449 procedure Process_One_File;
450 -- Process input file Infile_Name and put the result in file
453 procedure Recursive_Process (In_Dir : String; Out_Dir : String);
454 -- Process recursively files in In_Dir. Results go to Out_Dir.
456 ----------------------
457 -- Process_One_File --
458 ----------------------
460 procedure Process_One_File is
461 Infile : Source_File_Index;
464 -- Create the output file; fails if this does not work.
467 Create (Text_Outfile, Out_File, Get_Name_String (Outfile_Name));
472 ("unable to create output file """,
473 Get_Name_String (Outfile_Name), """");
476 -- Load the input file
478 Infile := Sinput.C.Load_File (Get_Name_String (Infile_Name));
480 if Infile = No_Source_File then
481 Fail ("unable to find input file """,
482 Get_Name_String (Infile_Name), """");
485 -- Set Main_Source_File to the input file for the benefit of
488 Sinput.Main_Source_File := Infile;
490 Scanner.Initialize_Scanner (No_Unit, Infile);
492 -- Output the SFN pragma if asked to
494 if Source_Ref_Pragma then
495 Put_Line (Outfile.all, "pragma Source_Reference (1, """ &
496 Get_Name_String (Sinput.File_Name (Infile)) &
500 -- Preprocess the input file
504 -- In verbose mode, if there is no error, report it
506 if Opt.Verbose_Mode and then Err_Vars.Total_Errors_Detected = 0 then
507 Errutil.Finalize (Source_Type => "input");
510 -- If we had some errors, delete the output file, and report
513 if Err_Vars.Total_Errors_Detected > 0 then
514 if Outfile /= Standard_Output then
515 Delete (Text_Outfile);
518 Errutil.Finalize (Source_Type => "input");
522 -- otherwise, close the output file, and we are done.
524 elsif Outfile /= Standard_Output then
525 Close (Text_Outfile);
527 end Process_One_File;
529 -----------------------
530 -- Recursive_Process --
531 -----------------------
533 procedure Recursive_Process (In_Dir : String; Out_Dir : String) is
535 Name : String (1 .. 255);
537 In_Dir_Name : Name_Id;
538 Out_Dir_Name : Name_Id;
540 procedure Set_Directory_Names;
541 -- Establish or reestablish the current input and output directories
543 -------------------------
544 -- Set_Directory_Names --
545 -------------------------
547 procedure Set_Directory_Names is
549 Input_Directory := In_Dir_Name;
550 Output_Directory := Out_Dir_Name;
551 end Set_Directory_Names;
554 -- Open the current input directory
557 Open (Dir_In, In_Dir);
560 when Directory_Error =>
561 Fail ("could not read directory " & In_Dir);
564 -- Set the new input and output directory names
566 Name_Len := In_Dir'Length;
567 Name_Buffer (1 .. Name_Len) := In_Dir;
568 In_Dir_Name := Name_Find;
569 Name_Len := Out_Dir'Length;
570 Name_Buffer (1 .. Name_Len) := Out_Dir;
571 Out_Dir_Name := Name_Find;
575 -- Traverse the input directory
577 Read (Dir_In, Name, Last);
580 if Name (1 .. Last) /= "." and then Name (1 .. Last) /= ".." then
582 Input : constant String :=
583 In_Dir & Directory_Separator & Name (1 .. Last);
584 Output : constant String :=
585 Out_Dir & Directory_Separator & Name (1 .. Last);
588 -- If input is an ordinary file, process it
590 if Is_Regular_File (Input) then
591 -- First get the output file name
594 Name_Buffer (1 .. Name_Len) := Name (1 .. Last);
595 Infile_Name := Name_Find;
596 Preprocess_Infile_Name;
598 -- Set the input file name and process the file
600 Name_Len := Input'Length;
601 Name_Buffer (1 .. Name_Len) := Input;
602 Infile_Name := Name_Find;
605 elsif Is_Directory (Input) then
606 -- Input is a directory. If the corresponding output
607 -- directory does not already exist, create it.
609 if not Is_Directory (Output) then
611 Make_Dir (Dir_Name => Output);
614 when Directory_Error =>
615 Fail ("could not create directory """,
620 -- And process this new input directory
622 Recursive_Process (Input, Output);
624 -- Reestablish the input and output directory names
625 -- that have been modified by the recursive call.
632 end Recursive_Process;
635 if Output_Directory = No_Name then
636 -- If the output is not a directory, fail if the input is
637 -- an existing directory, to avoid possible problems.
639 if Is_Directory (Get_Name_String (Infile_Name)) then
640 Fail ("input file """ & Get_Name_String (Infile_Name) &
641 """ is a directory");
644 -- Just process the single input file
648 elsif Input_Directory = No_Name then
649 -- Get the output file name from the input file name, and process
650 -- the single input file.
652 Preprocess_Infile_Name;
656 -- Recursively process files in the directory tree rooted at the
660 (In_Dir => Get_Name_String (Input_Directory),
661 Out_Dir => Get_Name_String (Output_Directory));
665 -------------------------
666 -- Put_Char_To_Outfile --
667 -------------------------
669 procedure Put_Char_To_Outfile (C : Character) is
671 Put (Outfile.all, C);
672 end Put_Char_To_Outfile;
674 -----------------------
675 -- Scan_Command_Line --
676 -----------------------
678 procedure Scan_Command_Line is
682 -- Parse the switches
686 Switch := GNAT.Command_Line.Getopt ("D: b c r s u v");
693 Process_Command_Line_Symbol_Definition
694 (S => GNAT.Command_Line.Parameter);
697 Opt.Blank_Deleted_Lines := True;
700 Opt.Comment_Deleted_Lines := True;
703 Source_Ref_Pragma := True;
706 Opt.List_Preprocessing_Symbols := True;
709 Opt.Undefined_Symbols_Are_False := True;
712 Opt.Verbose_Mode := True;
715 Fail ("Invalid Switch: -" & Switch);
719 when GNAT.Command_Line.Invalid_Switch =>
720 Write_Str ("Invalid Switch: -");
721 Write_Line (GNAT.Command_Line.Full_Switch);
727 -- Get the file names
731 S : constant String := GNAT.Command_Line.Get_Argument;
734 exit when S'Length = 0;
736 Name_Len := S'Length;
737 Name_Buffer (1 .. Name_Len) := S;
739 if Infile_Name = No_Name then
740 Infile_Name := Name_Find;
741 elsif Outfile_Name = No_Name then
742 Outfile_Name := Name_Find;
743 elsif Deffile_Name = No_Name then
744 Deffile_Name := Name_Find;
746 Fail ("too many arguments specifed");
750 end Scan_Command_Line;
759 Write_Line ("Usage: gnatprep [-bcrsuv] [-Dsymbol=value] " &
760 "infile outfile [deffile]");
762 Write_Line (" infile Name of the input file");
763 Write_Line (" outfile Name of the output file");
764 Write_Line (" deffile Name of the definition file");
766 Write_Line ("gnatprep switches:");
767 Write_Line (" -b Replace preprocessor lines by blank lines");
768 Write_Line (" -c Keep preprocessor lines as comments");
769 Write_Line (" -D Associate symbol with value");
770 Write_Line (" -r Generate Source_Reference pragma");
771 Write_Line (" -s Print a sorted list of symbol names and values");
772 Write_Line (" -u Treat undefined symbols as FALSE");
773 Write_Line (" -v Verbose mode");