OSDN Git Service

* gnat1drv.adb, gnatbind.adb, gnatchop.adb, gnatfind.adb, gnatlink.adb,
[pf3gnuchains/gcc-fork.git] / gcc / ada / gprep.adb
1 ------------------------------------------------------------------------------
2 --                                                                          --
3 --                         GNAT COMPILER COMPONENTS                         --
4 --                                                                          --
5 --                                G P R E P                                 --
6 --                                                                          --
7 --                                 B o d y                                  --
8 --                                                                          --
9 --          Copyright (C) 2002-2004, 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 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.                                                      --
21 --                                                                          --
22 -- GNAT was originally developed  by the GNAT team at  New York University. --
23 -- Extensive contributions were provided by Ada Core Technologies Inc.      --
24 --                                                                          --
25 ------------------------------------------------------------------------------
26
27 with Csets;
28 with Err_Vars; use Err_Vars;
29 with Errutil;
30 with Gnatvsn;
31 with Namet;    use Namet;
32 with Opt;
33 with Osint;    use Osint;
34 with Output;   use Output;
35 with Prep;     use Prep;
36 with Scng;
37 with Sinput.C;
38 with Snames;
39 with Stringt;  use Stringt;
40 with Types;    use Types;
41
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;
47
48 package body GPrep is
49
50    Copyright_Displayed : Boolean := False;
51    --  Used to prevent multiple displays of the copyright notice
52
53    ------------------------
54    -- Argument Line Data --
55    ------------------------
56
57    Infile_Name  : Name_Id := No_Name;
58    Outfile_Name : Name_Id := No_Name;
59    Deffile_Name : Name_Id := No_Name;
60
61    Output_Directory : Name_Id := No_Name;
62    --  Used when the specified output is an existing directory
63
64    Input_Directory : Name_Id := No_Name;
65    --  Used when the specified input and output are existing directories
66
67    Source_Ref_Pragma : Boolean := False;
68    --  Record command line options (set if -r switch set)
69
70    Text_Outfile : aliased Ada.Text_IO.File_Type;
71    Outfile      : constant File_Access := Text_Outfile'Access;
72
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.
77
78    -----------------
79    -- Subprograms --
80    -----------------
81
82    procedure Display_Copyright;
83    --  Display the copyright notice
84
85    procedure Post_Scan;
86    --  Null procedure, needed by instantiation of Scng below
87
88    package Scanner is new Scng
89      (Post_Scan,
90       Errutil.Error_Msg,
91       Errutil.Error_Msg_S,
92       Errutil.Error_Msg_SC,
93       Errutil.Error_Msg_SP,
94       Errutil.Style);
95    --  The scanner for the preprocessor
96
97    function Is_ASCII_Letter (C : Character) return Boolean;
98    --  True if C is in 'a' .. 'z' or in 'A' .. 'Z'
99
100    procedure Double_File_Name_Buffer;
101    --  Double the size of the file name buffer.
102
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.
106
107    procedure Process_Files;
108    --  Process the single input file or all the files in the directory tree
109    --  rooted at the input directory.
110
111    procedure Process_Command_Line_Symbol_Definition (S : String);
112    --  Process a -D switch on the command line
113
114    procedure Put_Char_To_Outfile (C : Character);
115    --  Output one character to the output file.
116    --  Used to initialize the preprocessor.
117
118    procedure New_EOL_To_Outfile;
119    --  Output a new line to the output file.
120    --  Used to initialize the preprocessor.
121
122    procedure Scan_Command_Line;
123    --  Scan the switches and the file names
124
125    procedure Usage;
126    --  Display the usage
127
128    -----------------------
129    -- Display_Copyright --
130    -----------------------
131
132    procedure Display_Copyright is
133    begin
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;
138       end if;
139    end Display_Copyright;
140
141    -----------------------------
142    -- Double_File_Name_Buffer --
143    -----------------------------
144
145    procedure Double_File_Name_Buffer is
146       New_Buffer : constant String_Access :=
147                      new String (1 .. 2 * File_Name_Buffer'Length);
148    begin
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;
153
154    --------------
155    -- Gnatprep --
156    --------------
157
158    procedure Gnatprep is
159    begin
160       --  Do some initializations (order is important here!)
161
162       Csets.Initialize;
163       Namet.Initialize;
164       Snames.Initialize;
165       Stringt.Initialize;
166
167       --  Initialize the preprocessor
168
169       Prep.Initialize
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);
175
176       --  Set the scanner characteristics for the preprocessor
177
178       Scanner.Set_Special_Character ('#');
179       Scanner.Set_Special_Character ('$');
180       Scanner.Set_End_Of_Line_As_Token (True);
181
182       --  Initialize the mapping table of symbols to values
183
184       Prep.Symbol_Table.Init (Prep.Mapping);
185
186       --  Parse the switches and arguments
187
188       Scan_Command_Line;
189
190       if Opt.Verbose_Mode then
191          Display_Copyright;
192       end if;
193
194       --  Test we had all the arguments needed
195
196       if Infile_Name = No_Name then
197          --  No input file specified, just output the usage and exit
198
199          Usage;
200          return;
201
202       elsif Outfile_Name = No_Name then
203          --  No output file specified, just output the usage and exit
204
205          Usage;
206          return;
207       end if;
208
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
211       --  as blank lines.
212
213       if Source_Ref_Pragma and (not Opt.Comment_Deleted_Lines) then
214          Opt.Blank_Deleted_Lines := True;
215       end if;
216
217       --  If we have a definition file, parse it
218
219       if Deffile_Name /= No_Name then
220          declare
221             Deffile : Source_File_Index;
222
223          begin
224             Errutil.Initialize;
225             Deffile := Sinput.C.Load_File (Get_Name_String (Deffile_Name));
226
227             --  Set Main_Source_File to the definition file for the benefit of
228             --  Errutil.Finalize.
229
230             Sinput.Main_Source_File := Deffile;
231
232             if Deffile = No_Source_File then
233                Fail ("unable to find definition file """,
234                      Get_Name_String (Deffile_Name),
235                      """");
236             end if;
237
238             Scanner.Initialize_Scanner (No_Unit, Deffile);
239
240             Prep.Parse_Def_File;
241          end;
242       end if;
243
244       --  If there are errors in the definition file, output these errors
245       --  and exit.
246
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), """");
251       end if;
252
253       --  If -s switch was specified, print a sorted list of symbol names and
254       --  values, if any.
255
256       if Opt.List_Preprocessing_Symbols then
257          Prep.List_Symbols (Foreword => "");
258       end if;
259
260       Output_Directory := No_Name;
261       Input_Directory  := No_Name;
262
263       --  Check if the specified output is an existing directory
264
265       if Is_Directory (Get_Name_String (Outfile_Name)) then
266          Output_Directory := Outfile_Name;
267
268          --  As the output is an existing directory, check if the input too
269          --  is a directory.
270
271          if Is_Directory (Get_Name_String (Infile_Name)) then
272             Input_Directory := Infile_Name;
273          end if;
274       end if;
275
276       --  And process the single input or the files in the directory tree
277       --  rooted at the input directory.
278
279       Process_Files;
280
281    end Gnatprep;
282
283    ---------------------
284    -- Is_ASCII_Letter --
285    ---------------------
286
287    function Is_ASCII_Letter (C : Character) return Boolean is
288    begin
289       return C in 'A' .. 'Z' or else C in 'a' .. 'z';
290    end Is_ASCII_Letter;
291
292    ------------------------
293    -- New_EOL_To_Outfile --
294    ------------------------
295
296    procedure New_EOL_To_Outfile is
297    begin
298       New_Line (Outfile.all);
299    end New_EOL_To_Outfile;
300
301    ---------------
302    -- Post_Scan --
303    ---------------
304
305    procedure Post_Scan is
306    begin
307       null;
308    end Post_Scan;
309
310    ----------------------------
311    -- Preprocess_Infile_Name --
312    ----------------------------
313
314    procedure Preprocess_Infile_Name is
315       Len    : Natural;
316       First  : Positive := 1;
317       Last   : Natural;
318       Symbol : Name_Id;
319       Data   : Symbol_Data;
320
321    begin
322       --  Initialize the buffer with the name of the input file
323
324       Get_Name_String (Infile_Name);
325       Len := Name_Len;
326
327       while File_Name_Buffer'Length < Len loop
328          Double_File_Name_Buffer;
329       end loop;
330
331       File_Name_Buffer (1 .. Len) := Name_Buffer (1 .. Len);
332
333       --  Look for possible symbols in the file name
334
335       while First < Len loop
336
337          --  A symbol starts with a dollar sign followed by a letter
338
339          if File_Name_Buffer (First) = '$' and then
340            Is_ASCII_Letter (File_Name_Buffer (First + 1))
341          then
342             Last := First + 1;
343
344             --  Find the last letter of the symbol
345
346             while Last < Len and then
347                Is_ASCII_Letter (File_Name_Buffer (Last + 1))
348             loop
349                Last := Last + 1;
350             end loop;
351
352             --  Get the symbol name id
353
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));
358             Symbol := Name_Find;
359
360             --  And look for this symbol name in the symbol table
361
362             for Index in 1 .. Symbol_Table.Last (Mapping) loop
363                Data := Mapping.Table (Index);
364
365                if Data.Symbol = Symbol then
366
367                   --  We found the symbol. If its value is not a string,
368                   --  replace the symbol in the file name with the value of
369                   --  the symbol.
370
371                   if not Data.Is_A_String then
372                      String_To_Name_Buffer (Data.Value);
373
374                      declare
375                         Sym_Len : constant Positive := Last - First + 1;
376                         Offset : constant Integer := Name_Len - Sym_Len;
377                         New_Len : constant Natural := Len + Offset;
378
379                      begin
380                         while New_Len > File_Name_Buffer'Length loop
381                            Double_File_Name_Buffer;
382                         end loop;
383
384                         File_Name_Buffer (Last + 1 + Offset .. New_Len) :=
385                           File_Name_Buffer (Last + 1 .. Len);
386                         Len := New_Len;
387                         Last := Last + Offset;
388                         File_Name_Buffer (First .. Last) :=
389                           Name_Buffer (1 .. Name_Len);
390                      end;
391                   end if;
392
393                   exit;
394                end if;
395             end loop;
396
397             --  Skip over the symbol name or its value: we are not checking
398             --  for another symbol name in the value.
399
400             First := Last + 1;
401
402          else
403             First := First + 1;
404          end if;
405       end loop;
406
407       --  We now have the output file name in the buffer. Get the output
408       --  path and put it in Outfile_Name.
409
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;
415
416    --------------------------------------------
417    -- Process_Command_Line_Symbol_Definition --
418    --------------------------------------------
419
420    procedure Process_Command_Line_Symbol_Definition (S : String) is
421       Data   : Symbol_Data;
422       Symbol : Symbol_Id;
423
424    begin
425       --  Check the symbol definition and get the symbol and its value.
426       --  Fail if symbol definition is illegal.
427
428       Check_Command_Line_Symbol_Definition (S, Data);
429
430       Symbol := Index_Of (Data.Symbol);
431
432       --  If symbol does not alrady exist, create a new entry in the mapping
433       --  table.
434
435       if Symbol = No_Symbol then
436          Symbol_Table.Increment_Last (Mapping);
437          Symbol := Symbol_Table.Last (Mapping);
438       end if;
439
440       Mapping.Table (Symbol) := Data;
441    end Process_Command_Line_Symbol_Definition;
442
443    -------------------
444    -- Process_Files --
445    -------------------
446
447    procedure Process_Files is
448
449       procedure Process_One_File;
450       --  Process input file Infile_Name and put the result in file
451       --  Outfile_Name.
452
453       procedure Recursive_Process (In_Dir : String; Out_Dir : String);
454       --  Process recursively files in In_Dir. Results go to Out_Dir.
455
456       ----------------------
457       -- Process_One_File --
458       ----------------------
459
460       procedure Process_One_File is
461          Infile : Source_File_Index;
462
463       begin
464          --  Create the output file; fails if this does not work.
465
466          begin
467             Create (Text_Outfile, Out_File, Get_Name_String (Outfile_Name));
468
469          exception
470             when others =>
471                Fail
472                  ("unable to create output file """,
473                   Get_Name_String (Outfile_Name), """");
474          end;
475
476          --  Load the input file
477
478          Infile := Sinput.C.Load_File (Get_Name_String (Infile_Name));
479
480          if Infile = No_Source_File then
481             Fail ("unable to find input file """,
482                   Get_Name_String (Infile_Name), """");
483          end if;
484
485          --  Set Main_Source_File to the input file for the benefit of
486          --  Errutil.Finalize.
487
488          Sinput.Main_Source_File := Infile;
489
490          Scanner.Initialize_Scanner (No_Unit, Infile);
491
492          --  Output the SFN pragma if asked to
493
494          if Source_Ref_Pragma then
495             Put_Line (Outfile.all, "pragma Source_Reference (1, """ &
496                       Get_Name_String (Sinput.File_Name (Infile)) &
497                       """);");
498          end if;
499
500          --  Preprocess the input file
501
502          Prep.Preprocess;
503
504          --  In verbose mode, if there is no error, report it
505
506          if Opt.Verbose_Mode and then Err_Vars.Total_Errors_Detected = 0 then
507             Errutil.Finalize (Source_Type => "input");
508          end if;
509
510          --  If we had some errors, delete the output file, and report
511          --  the errors.
512
513          if Err_Vars.Total_Errors_Detected > 0 then
514             if Outfile /= Standard_Output then
515                Delete (Text_Outfile);
516             end if;
517
518             Errutil.Finalize (Source_Type => "input");
519
520             OS_Exit (0);
521
522          --  otherwise, close the output file, and we are done.
523
524          elsif Outfile /= Standard_Output then
525             Close (Text_Outfile);
526          end if;
527       end Process_One_File;
528
529       -----------------------
530       -- Recursive_Process --
531       -----------------------
532
533       procedure Recursive_Process (In_Dir : String; Out_Dir : String) is
534          Dir_In : Dir_Type;
535          Name : String (1 .. 255);
536          Last : Natural;
537          In_Dir_Name  : Name_Id;
538          Out_Dir_Name : Name_Id;
539
540          procedure Set_Directory_Names;
541          --  Establish or reestablish the current input and output directories
542
543          -------------------------
544          -- Set_Directory_Names --
545          -------------------------
546
547          procedure Set_Directory_Names is
548          begin
549             Input_Directory := In_Dir_Name;
550             Output_Directory := Out_Dir_Name;
551          end Set_Directory_Names;
552
553       begin
554          --  Open the current input directory
555
556          begin
557             Open (Dir_In, In_Dir);
558
559          exception
560             when Directory_Error =>
561                Fail ("could not read directory " & In_Dir);
562          end;
563
564          --  Set the new input and output directory names
565
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;
572
573          Set_Directory_Names;
574
575          --  Traverse the input directory
576          loop
577             Read (Dir_In, Name, Last);
578             exit when Last = 0;
579
580             if Name (1 .. Last) /= "." and then Name (1 .. Last) /= ".." then
581                declare
582                   Input : constant String :=
583                             In_Dir & Directory_Separator & Name (1 .. Last);
584                   Output : constant String :=
585                              Out_Dir & Directory_Separator & Name (1 .. Last);
586
587                begin
588                   --  If input is an ordinary file, process it
589
590                   if Is_Regular_File (Input) then
591                      --  First get the output file name
592
593                      Name_Len := Last;
594                      Name_Buffer (1 .. Name_Len) := Name (1 .. Last);
595                      Infile_Name := Name_Find;
596                      Preprocess_Infile_Name;
597
598                      --  Set the input file name and process the file
599
600                      Name_Len := Input'Length;
601                      Name_Buffer (1 .. Name_Len) := Input;
602                      Infile_Name := Name_Find;
603                      Process_One_File;
604
605                   elsif Is_Directory (Input) then
606                      --  Input is a directory. If the corresponding output
607                      --  directory does not already exist, create it.
608
609                      if not Is_Directory (Output) then
610                         begin
611                            Make_Dir (Dir_Name => Output);
612
613                         exception
614                            when Directory_Error =>
615                               Fail ("could not create directory """,
616                                     Output, """");
617                         end;
618                      end if;
619
620                      --  And process this new input directory
621
622                      Recursive_Process (Input, Output);
623
624                      --  Reestablish the input and output directory names
625                      --  that have been modified by the recursive call.
626
627                      Set_Directory_Names;
628                   end if;
629                end;
630             end if;
631          end loop;
632       end Recursive_Process;
633
634    begin
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.
638
639          if Is_Directory (Get_Name_String (Infile_Name)) then
640             Fail ("input file """ & Get_Name_String (Infile_Name) &
641                   """ is a directory");
642          end if;
643
644          --  Just process the single input file
645
646          Process_One_File;
647
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.
651
652          Preprocess_Infile_Name;
653          Process_One_File;
654
655       else
656          --  Recursively process files in the directory tree rooted at the
657          --  input directory.
658
659          Recursive_Process
660            (In_Dir => Get_Name_String (Input_Directory),
661             Out_Dir => Get_Name_String (Output_Directory));
662       end if;
663    end Process_Files;
664
665    -------------------------
666    -- Put_Char_To_Outfile --
667    -------------------------
668
669    procedure Put_Char_To_Outfile (C : Character) is
670    begin
671       Put (Outfile.all, C);
672    end Put_Char_To_Outfile;
673
674    -----------------------
675    -- Scan_Command_Line --
676    -----------------------
677
678    procedure Scan_Command_Line is
679       Switch : Character;
680
681    begin
682       --  Parse the switches
683
684       loop
685          begin
686             Switch := GNAT.Command_Line.Getopt ("D: b c r s u v");
687             case Switch is
688
689                when ASCII.NUL =>
690                   exit;
691
692                when 'D' =>
693                   Process_Command_Line_Symbol_Definition
694                     (S => GNAT.Command_Line.Parameter);
695
696                when 'b' =>
697                   Opt.Blank_Deleted_Lines := True;
698
699                when 'c' =>
700                   Opt.Comment_Deleted_Lines := True;
701
702                when 'r' =>
703                   Source_Ref_Pragma := True;
704
705                when 's' =>
706                   Opt.List_Preprocessing_Symbols := True;
707
708                when 'u' =>
709                   Opt.Undefined_Symbols_Are_False := True;
710
711                when 'v' =>
712                   Opt.Verbose_Mode := True;
713
714                when others =>
715                   Fail ("Invalid Switch: -" & Switch);
716             end case;
717
718          exception
719             when GNAT.Command_Line.Invalid_Switch =>
720                Write_Str ("Invalid Switch: -");
721                Write_Line (GNAT.Command_Line.Full_Switch);
722                Usage;
723                OS_Exit (1);
724          end;
725       end loop;
726
727       --  Get the file names
728
729       loop
730          declare
731             S : constant String := GNAT.Command_Line.Get_Argument;
732
733          begin
734             exit when S'Length = 0;
735
736             Name_Len := S'Length;
737             Name_Buffer (1 .. Name_Len) := S;
738
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;
745             else
746                Fail ("too many arguments specifed");
747             end if;
748          end;
749       end loop;
750    end Scan_Command_Line;
751
752    -----------
753    -- Usage --
754    -----------
755
756    procedure Usage is
757    begin
758       Display_Copyright;
759       Write_Line ("Usage: gnatprep [-bcrsuv] [-Dsymbol=value] " &
760                     "infile outfile [deffile]");
761       Write_Eol;
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");
765       Write_Eol;
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");
774       Write_Eol;
775    end Usage;
776
777 end GPrep;