OSDN Git Service

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