re PR tree-optimization/58143 (wrong code at -O3)
[official-gcc.git] / gcc / ada / xgnatugn.adb
blob4706701e9b1081357842750aa50cd241b2d566a8
1 ------------------------------------------------------------------------------
2 -- --
3 -- GNAT SYSTEM UTILITIES --
4 -- --
5 -- X G N A T U G N --
6 -- --
7 -- B o d y --
8 -- --
9 -- Copyright (C) 2003-2013, 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 ------------------------------------------------------------------------------
23 -- This utility is used to process the source of gnat_ugn.texi to make a
24 -- version suitable for running through standard Texinfo processor. It is
25 -- invoked as follows:
27 -- xgnatugn <target> <in-file> <word-list> [ <out-file> [ <warnings> ] ]
29 -- 1. <target> is the target type of the manual, which is one of:
31 -- unw Unix and Windows platforms
32 -- vms OpenVMS
34 -- 2. <in-file> is the file name of the Texinfo file to be
35 -- preprocessed.
37 -- 3. <word-list> is the name of the word list file. This file is used for
38 -- rewriting the VMS edition. Each line contains a word mapping: The source
39 -- word in the first column, the target word in the second column. The
40 -- columns are separated by a '^' character. When preprocessing for VMS, the
41 -- first word is replaced with the second. (Words consist of letters,
42 -- digits, and the four characters "?-_~". A sequence of multiple words can
43 -- be replaced if they are listed in the first column, separated by a single
44 -- space character. If multiple words are to be replaced, there must be a
45 -- replacement for each prefix.)
47 -- 4. <out-file> (optional) is the name of the output file. It defaults to
48 -- gnat_ugn_unw.texi or gnat_ugn_vms.texi, depending on the target.
50 -- 5. <warnings> (optional, and allowed only if <out-file> is explicit)
51 -- can be any string. If present, it indicates that warning messages are
52 -- to be output to Standard_Error. If absent, no warning messages are
53 -- generated.
55 -- The following steps are performed:
57 -- In VMS mode
59 -- Any occurrences of ^alpha^beta^ are replaced by beta. The sequence
60 -- must fit on a single line, and there can only be one occurrence on a
61 -- line.
63 -- Any occurrences of a word in the Ug_Words list are replaced by the
64 -- appropriate vms equivalents. Note that replacements do not occur
65 -- within ^alpha^beta^ sequences.
67 -- Any occurrence of [filename].extension, where extension one of the
68 -- following:
70 -- "o", "ads", "adb", "ali", "ada", "atb", "ats", "adc", "c"
72 -- replaced by the appropriate VMS names (all upper case with .o
73 -- replaced .OBJ). Note that replacements do not occur within
74 -- ^alpha^beta^ sequences.
76 -- In UNW mode
78 -- Any occurrences of ^alpha^beta^ are replaced by alpha. The sequence
79 -- must fit on a single line.
81 -- In both modes
83 -- The sequence ^^^ is replaced by a single ^. This escape sequence
84 -- must be used if the literal character ^ is to appear in the
85 -- output. A line containing this escape sequence may not also contain
86 -- a ^alpha^beta^ sequence.
88 with Ada.Command_Line; use Ada.Command_Line;
89 with Ada.Strings; use Ada.Strings;
90 with Ada.Strings.Fixed; use Ada.Strings.Fixed;
91 with Ada.Strings.Unbounded; use Ada.Strings.Unbounded;
92 with Ada.Strings.Maps; use Ada.Strings.Maps;
93 with Ada.Strings.Maps.Constants; use Ada.Strings.Maps.Constants;
94 with Ada.Streams.Stream_IO; use Ada.Streams.Stream_IO;
95 with Ada.Text_IO; use Ada.Text_IO;
97 with GNAT.Spitbol; use GNAT.Spitbol;
98 with GNAT.Spitbol.Table_VString; use GNAT.Spitbol.Table_VString;
100 procedure Xgnatugn is
102 procedure Usage;
103 -- Print usage information. Invoked if an invalid command line is
104 -- encountered.
106 subtype Sfile is Ada.Streams.Stream_IO.File_Type;
108 Output_File : Sfile;
109 -- The preprocessed output is written to this file
111 type Input_File is record
112 Name : VString;
113 Data : Ada.Text_IO.File_Type;
114 Line : Natural := 0;
115 end record;
116 -- Records information on an input file. Name and Line are used
117 -- in error messages, Line is updated automatically by Get_Line.
119 function Get_Line (Input : access Input_File) return String;
120 -- Returns a line from Input and performs the necessary
121 -- line-oriented checks (length, character set, trailing spaces).
123 procedure Put_Line (F : Sfile; S : String);
124 -- Local version of Put_Line ensures Unix style line endings
126 First_Time : Boolean := True;
127 Number_Of_Warnings : Natural := 0;
128 Number_Of_Errors : Natural := 0;
129 Warnings_Enabled : Boolean;
131 procedure Error
132 (Input : Input_File;
133 At_Character : Natural;
134 Message : String);
135 procedure Error
136 (Input : Input_File;
137 Message : String);
138 -- Prints a message reporting an error on line Input.Line. If
139 -- At_Character is not 0, indicate the exact character at which
140 -- the error occurs.
142 procedure Warning
143 (Input : Input_File;
144 At_Character : Natural;
145 Message : String);
147 Dictionary_File : aliased Input_File;
148 procedure Read_Dictionary_File;
149 -- Dictionary_File is opened using the name given on the command
150 -- line. It contains the replacements for the Ug_Words list.
151 -- Read_Dictionary_File reads Dictionary_File and fills the
152 -- Ug_Words table.
154 Source_File : aliased Input_File;
155 procedure Process_Source_File;
156 -- Source_File is opened using the name given on the command line.
157 -- It contains the Texinfo source code. Process_Source_File
158 -- performs the necessary replacements.
160 type Flag_Type is (UNW, VMS, FSFEDITION, PROEDITION, GPLEDITION);
161 -- The flags permitted in @ifset or @ifclear commands:
163 -- Targets for preprocessing
164 -- UNW (Unix and Windows) or VMS
166 -- Editions of the manual
167 -- FSFEDITION, PROEDITION, or GPLEDITION
169 -- Conditional commands for target are processed by xgnatugn
171 -- Conditional commands for edition are passed through unchanged
173 subtype Target_Type is Flag_Type range UNW .. VMS;
175 Target : Target_Type;
176 -- The Target variable is initialized using the command line
178 Valid_Characters : constant Character_Set := To_Set (Span => (' ', '~'));
179 -- This array controls which characters are permitted in the input
180 -- file (after line breaks have been removed). Valid characters
181 -- are all printable ASCII characters and the space character.
183 Word_Characters : constant Character_Set :=
184 (To_Set (Ranges =>
185 (('0', '9'), ('a', 'z'), ('A', 'Z')))
186 or To_Set ("?-_~"));
187 -- The characters which are permitted in words. Other (valid)
188 -- characters are assumed to be delimiters between words. Note that
189 -- this set has to include all characters of the source words of the
190 -- Ug_Words dictionary.
192 Reject_Trailing_Spaces : constant Boolean := True;
193 -- Controls whether Xgnatug rejects superfluous space characters
194 -- at the end of lines.
196 Maximum_Line_Length : constant Positive := 79;
197 Fatal_Line_Length_Limit : constant Positive := 5000;
198 Fatal_Line_Length : exception;
199 -- If Maximum_Line_Length is exceeded in an input file, an error
200 -- message is printed. If Fatal_Line_Length is exceeded,
201 -- execution terminates with a Fatal_Line_Length exception.
203 VMS_Escape_Character : constant Character := '^';
204 -- The character used to mark VMS alternatives (^alpha^beta^)
206 Extensions : GNAT.Spitbol.Table_VString.Table (20);
207 procedure Initialize_Extensions;
208 -- This table records extensions and their replacement for
209 -- rewriting filenames in the VMS version of the manual.
211 function Is_Extension (Extension : String) return Boolean;
212 function Get_Replacement_Extension (Extension : String) return String;
213 -- These functions query the replacement table. Is_Extension
214 -- checks if the given string is a known extension.
215 -- Get_Replacement returns the replacement extension.
217 Ug_Words : GNAT.Spitbol.Table_VString.Table (200);
218 function Is_Known_Word (Word : String) return Boolean;
219 function Get_Replacement_Word (Word : String) return String;
220 -- The Ug_Words table lists replacement words for the VMS version
221 -- of the manual. Is_Known_Word and Get_Replacement_Word query
222 -- this table. The table is filled using Read_Dictionary_File.
224 function Rewrite_Source_Line (Line : String) return String;
225 -- This subprogram takes a line and rewrites it according to Target.
226 -- It relies on information in Source_File to generate error messages.
228 -----------
229 -- Usage --
230 -----------
232 procedure Usage is
233 begin
234 Put_Line (Standard_Error,
235 "usage: xgnatugn TARGET SOURCE DICTIONARY [OUTFILE [WARNINGS]]");
236 New_Line;
237 Put_Line (Standard_Error, "TARGET is one of:");
239 for T in Target_Type'Range loop
240 Put_Line (Standard_Error, " " & Target_Type'Image (T));
241 end loop;
243 New_Line;
244 Put_Line (Standard_Error, "SOURCE is the source file to process.");
245 New_Line;
246 Put_Line (Standard_Error, "DICTIONARY is the name of a file "
247 & "that contains word replacements");
248 Put_Line (Standard_Error, "for the VMS version.");
249 New_Line;
250 Put_Line (Standard_Error,
251 "OUT-FILE, if present, is the output file to be created;");
252 Put_Line (Standard_Error,
253 "If OUT-FILE is absent, the output file is either " &
254 "gnat_ugn_unw.texi, ");
255 Put_Line (Standard_Error,
256 "or gnat_ugn_vms.texi, depending on TARGET.");
257 New_Line;
258 Put_Line (Standard_Error,
259 "WARNINGS, if present, is any string;");
260 Put_Line (Standard_Error,
261 "it will result in warning messages (e.g., line too long))");
262 Put_Line (Standard_Error,
263 "being output to Standard_Error.");
264 end Usage;
266 --------------
267 -- Get_Line --
268 --------------
270 function Get_Line (Input : access Input_File) return String is
271 Line_Buffer : String (1 .. Fatal_Line_Length_Limit);
272 Last : Natural;
274 begin
275 Input.Line := Input.Line + 1;
276 Get_Line (Input.Data, Line_Buffer, Last);
278 if Last = Line_Buffer'Last then
279 Error (Input.all, "line exceeds fatal line length limit");
280 raise Fatal_Line_Length;
281 end if;
283 declare
284 Line : String renames Line_Buffer (Line_Buffer'First .. Last);
286 begin
287 for J in Line'Range loop
288 if not Is_In (Line (J), Valid_Characters) then
289 Error (Input.all, J, "invalid character");
290 exit;
291 end if;
292 end loop;
294 if Line'Length > Maximum_Line_Length then
295 Warning (Input.all, Maximum_Line_Length + 1, "line too long");
296 end if;
298 if Reject_Trailing_Spaces
299 and then Line'Length > 0
300 and then Line (Line'Last) = ' '
301 then
302 Error (Input.all, Line'Last, "trailing space character");
303 end if;
305 return Trim (Line, Right);
306 end;
307 end Get_Line;
309 --------------
310 -- Put_Line --
311 --------------
313 procedure Put_Line (F : Sfile; S : String) is
314 begin
315 String'Write (Stream (F), S);
316 Character'Write (Stream (F), ASCII.LF);
317 end Put_Line;
319 -----------
320 -- Error --
321 -----------
323 procedure Error
324 (Input : Input_File;
325 Message : String)
327 begin
328 Error (Input, 0, Message);
329 end Error;
331 procedure Error
332 (Input : Input_File;
333 At_Character : Natural;
334 Message : String)
336 Line_Image : constant String := Integer'Image (Input.Line);
337 At_Character_Image : constant String := Integer'Image (At_Character);
338 -- These variables are required because we have to drop the leading
339 -- space character.
341 begin
342 Number_Of_Errors := Number_Of_Errors + 1;
344 if At_Character > 0 then
345 Put_Line (Standard_Error,
346 S (Input.Name) & ':'
347 & Line_Image (Line_Image'First + 1 .. Line_Image'Last) & ':'
348 & At_Character_Image (At_Character_Image'First + 1
349 .. At_Character_Image'Last)
350 & ": "
351 & Message);
352 else
353 Put_Line (Standard_Error,
354 S (Input.Name) & ':'
355 & Line_Image (Line_Image'First + 1 .. Line_Image'Last)
356 & ": "
357 & Message);
358 end if;
359 end Error;
361 -------------
362 -- Warning --
363 -------------
365 procedure Warning
366 (Input : Input_File;
367 At_Character : Natural;
368 Message : String)
370 Line_Image : constant String := Integer'Image (Input.Line);
371 At_Character_Image : constant String := Integer'Image (At_Character);
372 -- These variables are required because we have to drop the leading
373 -- space character.
375 begin
376 if not Warnings_Enabled then
377 return;
378 end if;
380 Number_Of_Warnings := Number_Of_Warnings + 1;
382 if At_Character > 0 then
383 Put_Line (Standard_Error,
384 S (Input.Name) & ':'
385 & Line_Image (Line_Image'First + 1 .. Line_Image'Last) & ':'
386 & At_Character_Image (At_Character_Image'First + 1
387 .. At_Character_Image'Last)
388 & ": warning: "
389 & Message);
390 else
391 Put_Line (Standard_Error,
392 S (Input.Name) & ':'
393 & Line_Image (Line_Image'First + 1 .. Line_Image'Last)
394 & ": warning: "
395 & Message);
396 end if;
397 end Warning;
399 --------------------------
400 -- Read_Dictionary_File --
401 --------------------------
403 procedure Read_Dictionary_File is
404 begin
405 while not End_Of_File (Dictionary_File.Data) loop
406 declare
407 Line : constant String :=
408 Get_Line (Dictionary_File'Access);
409 Split : constant Natural :=
410 Index (Line, (1 => VMS_Escape_Character));
412 begin
413 if Line'Length = 0 then
414 Error (Dictionary_File, "empty line in dictionary file");
416 elsif Line (Line'First) = ' ' then
417 Error (Dictionary_File, 1, "line starts with space character");
419 elsif Split = 0 then
420 Error (Dictionary_File, "line does not contain "
421 & VMS_Escape_Character & " character");
422 else
423 declare
424 Source : constant String :=
425 Trim (Line (1 .. Split - 1), Both);
426 Target : constant String :=
427 Trim (Line (Split + 1 .. Line'Last), Both);
429 Two_Spaces : constant Natural := Index (Source, " ");
431 Non_Word_Character : constant Natural :=
432 Index (Source,
433 Word_Characters or
434 To_Set (" ."),
435 Outside);
437 begin
438 if Two_Spaces /= 0 then
439 Error (Dictionary_File, Two_Spaces,
440 "multiple space characters in source word");
441 end if;
443 if Non_Word_Character /= 0 then
444 Error (Dictionary_File, Non_Word_Character,
445 "illegal character in source word");
446 end if;
448 if Source'Length = 0 then
449 Error (Dictionary_File, "source is empty");
451 elsif Target'Length = 0 then
452 Error (Dictionary_File, "target is empty");
454 else
455 Set (Ug_Words, Source, V (Target));
457 -- Ensure that if Source is a sequence of words
458 -- "WORD1 WORD2 ...", we already have a mapping for
459 -- "WORD1".
461 for J in Source'Range loop
462 if Source (J) = ' ' then
463 declare
464 Prefix : String renames
465 Source (Source'First .. J - 1);
466 begin
467 if not Is_Known_Word (Prefix) then
468 Error (Dictionary_File,
469 "prefix '" & Prefix
470 & "' not known at this point");
471 end if;
472 end;
473 end if;
474 end loop;
475 end if;
476 end;
477 end if;
478 end;
479 end loop;
480 end Read_Dictionary_File;
482 -------------------------
483 -- Rewrite_Source_Line --
484 -------------------------
486 function Rewrite_Source_Line (Line : String) return String is
488 -- We use a simple lexer to split the line into tokens:
490 -- Word consisting entirely of Word_Characters
491 -- VMS_Alternative ^alpha^beta^ replacement (but not ^^^)
492 -- Space a space character
493 -- Other everything else (sequence of non-word characters)
494 -- VMS_Error incomplete VMS alternative
495 -- End_Of_Line no more characters on this line
497 -- A sequence of three VMS_Escape_Characters is automatically
498 -- collapsed to an Other token.
500 type Token_Span is record
501 First, Last : Positive;
502 end record;
503 -- The character range covered by a token in Line
505 type Token_Kind is (End_Of_Line, Word, Other,
506 VMS_Alternative, VMS_Error);
507 type Token_Record (Kind : Token_Kind := End_Of_Line) is record
508 First : Positive;
509 case Kind is
510 when Word | Other =>
511 Span : Token_Span;
512 when VMS_Alternative =>
513 Non_VMS, VMS : Token_Span;
514 when VMS_Error | End_Of_Line =>
515 null;
516 end case;
517 end record;
519 Input_Position : Positive := Line'First;
520 Token : Token_Record;
521 -- The position of the next character to be processed by Next_Token
523 procedure Next_Token;
524 -- Returns the next token in Line, starting at Input_Position
526 Rewritten_Line : VString;
527 -- Collects the line as it is rewritten
529 procedure Rewrite_Word;
530 -- The current token is assumed to be a Word. When processing the VMS
531 -- version of the manual, additional tokens are gathered to check if
532 -- we have a file name or a sequence of known words.
534 procedure Maybe_Rewrite_Extension;
535 -- The current token is assumed to be Other. When processing the VMS
536 -- version of the manual and the token represents a single dot ".",
537 -- the following word is rewritten according to the rules for
538 -- extensions.
540 VMS_Token_Seen : Boolean := False;
541 -- This is set to true if a VMS_Alternative has been encountered, or a
542 -- ^^^ token.
544 ----------------
545 -- Next_Token --
546 ----------------
548 procedure Next_Token is
549 Remaining_Line : String renames Line (Input_Position .. Line'Last);
550 Last_Character : Natural;
552 begin
553 if Remaining_Line'Length = 0 then
554 Token := (End_Of_Line, Remaining_Line'First);
555 return;
556 end if;
558 -- ^alpha^beta^, the VMS_Alternative case
560 if Remaining_Line (Remaining_Line'First) = VMS_Escape_Character then
561 declare
562 VMS_Second_Character, VMS_Third_Character : Natural;
564 begin
565 if VMS_Token_Seen then
566 Error (Source_File, Remaining_Line'First,
567 "multiple " & VMS_Escape_Character
568 & " characters on a single line");
569 else
570 VMS_Token_Seen := True;
571 end if;
573 -- Find the second and third escape character. If one of
574 -- them is not present, generate an error token.
576 VMS_Second_Character :=
577 Index (Remaining_Line (Remaining_Line'First + 1
578 .. Remaining_Line'Last),
579 (1 => VMS_Escape_Character));
581 if VMS_Second_Character = 0 then
582 Input_Position := Remaining_Line'Last + 1;
583 Token := (VMS_Error, Remaining_Line'First);
584 return;
585 end if;
587 VMS_Third_Character :=
588 Index (Remaining_Line (VMS_Second_Character + 1
589 .. Remaining_Line'Last),
590 (1 => VMS_Escape_Character));
592 if VMS_Third_Character = 0 then
593 Input_Position := Remaining_Line'Last + 1;
594 Token := (VMS_Error, Remaining_Line'First);
595 return;
596 end if;
598 -- Consume all the characters we are about to include in
599 -- the token.
601 Input_Position := VMS_Third_Character + 1;
603 -- Check if we are in a ^^^ situation, and return an Other
604 -- token in this case.
606 if Remaining_Line'First + 1 = VMS_Second_Character
607 and then Remaining_Line'First + 2 = VMS_Third_Character
608 then
609 Token := (Other, Remaining_Line'First,
610 (Remaining_Line'First, Remaining_Line'First));
611 return;
612 end if;
614 Token := (VMS_Alternative, Remaining_Line'First,
615 (Remaining_Line'First + 1, VMS_Second_Character - 1),
616 (VMS_Second_Character + 1, VMS_Third_Character - 1));
617 return;
618 end;
619 end if;
621 -- The Word case. Search for characters not in Word_Characters.
622 -- We have found a word if the first non-word character is not
623 -- the first character in Remaining_Line, i.e. if Remaining_Line
624 -- starts with a word character.
626 Last_Character := Index (Remaining_Line, Word_Characters, Outside);
627 if Last_Character /= Remaining_Line'First then
629 -- If we haven't found a character which is not in
630 -- Word_Characters, all remaining characters are part of the
631 -- current Word token.
633 if Last_Character = 0 then
634 Last_Character := Remaining_Line'Last + 1;
635 end if;
637 Input_Position := Last_Character;
638 Token := (Word, Remaining_Line'First,
639 (Remaining_Line'First, Last_Character - 1));
640 return;
641 end if;
643 -- Remaining characters are in the Other category. To speed
644 -- up processing, we collect them together if there are several
645 -- of them.
647 Input_Position := Last_Character + 1;
648 Token := (Other,
649 Remaining_Line'First,
650 (Remaining_Line'First, Last_Character));
651 end Next_Token;
653 ------------------
654 -- Rewrite_Word --
655 ------------------
657 procedure Rewrite_Word is
658 First_Word : String
659 renames Line (Token.Span.First .. Token.Span.Last);
661 begin
662 -- We do not perform any error checking below, so we can just skip
663 -- all processing for the non-VMS version.
665 if Target /= VMS then
666 Append (Rewritten_Line, First_Word);
667 Next_Token;
668 return;
669 end if;
671 if Is_Known_Word (First_Word) then
673 -- If we have a word from the dictionary, we look for the
674 -- longest possible sequence we can rewrite.
676 declare
677 Seq : Token_Span := Token.Span;
678 Lost_Space : Boolean := False;
680 begin
681 Next_Token;
682 loop
683 if Token.Kind = Other
684 and then Line (Token.Span.First .. Token.Span.Last) = " "
685 then
686 Next_Token;
688 if Token.Kind /= Word
689 or else not Is_Known_Word (Line (Seq.First
690 .. Token.Span.Last))
691 then
692 -- When we reach this point, the following conditions
693 -- are true:
695 -- Seq is a known word
697 -- The previous token was a space character
699 -- Seq extended to the current token is not a
700 -- known word.
702 Lost_Space := True;
703 exit;
705 else
706 -- Extend Seq to cover the current (known) word
708 Seq.Last := Token.Span.Last;
709 Next_Token;
710 end if;
712 else
713 -- When we reach this point, the following conditions
714 -- are true:
716 -- Seq is a known word
718 -- The previous token was a word
720 -- The current token is not a space character.
722 exit;
723 end if;
724 end loop;
726 -- Rewrite Seq, and add the lost space if necessary
728 Append (Rewritten_Line,
729 Get_Replacement_Word (Line (Seq.First .. Seq.Last)));
730 if Lost_Space then
731 Append (Rewritten_Line, ' ');
732 end if;
734 -- The unknown token will be processed during the
735 -- next iteration of the main loop.
736 return;
737 end;
738 end if;
740 Next_Token;
742 if Token.Kind = Other
743 and then Line (Token.Span.First .. Token.Span.Last) = "."
744 then
745 -- Deal with extensions
747 Next_Token;
748 if Token.Kind = Word
749 and then
750 Is_Extension (Line (Token.Span.First .. Token.Span.Last))
751 then
752 -- We have discovered a file extension. Convert the file
753 -- name to upper case.
755 Append (Rewritten_Line,
756 Translate (First_Word, Upper_Case_Map) & '.');
757 Append (Rewritten_Line,
758 Get_Replacement_Extension
759 (Line (Token.Span.First .. Token.Span.Last)));
760 Next_Token;
761 else
762 -- We already have: Word ".", followed by an unknown token
764 Append (Rewritten_Line, First_Word & '.');
766 -- The unknown token will be processed during the next
767 -- iteration of the main loop.
768 end if;
770 else
771 -- We have an unknown Word, followed by an unknown token.
772 -- The unknown token will be processed by the outer loop.
774 Append (Rewritten_Line, First_Word);
775 end if;
776 end Rewrite_Word;
778 -----------------------------
779 -- Maybe_Rewrite_Extension --
780 -----------------------------
782 procedure Maybe_Rewrite_Extension is
783 begin
784 -- Again, we need no special processing in the non-VMS case
786 if Target = VMS
787 and then Line (Token.Span.First .. Token.Span.Last) = "."
788 then
789 -- This extension is not preceded by a word, otherwise
790 -- Rewrite_Word would have handled it.
792 Next_Token;
794 if Token.Kind = Word
795 and then Is_Extension (Line (Token.Span.First
796 .. Token.Span.Last))
797 then
798 Append (Rewritten_Line, '.' & Get_Replacement_Extension
799 (Line (Token.Span.First .. Token.Span.Last)));
800 Next_Token;
801 else
802 Append (Rewritten_Line, '.');
803 end if;
805 else
806 Append (Rewritten_Line, Line (Token.Span.First
807 .. Token.Span.Last));
808 Next_Token;
809 end if;
810 end Maybe_Rewrite_Extension;
812 -- Start of processing for Process_Source_Line
814 begin
815 -- The following parser recognizes the following special token
816 -- sequences:
818 -- Word "." Word rewrite as file name if second word is extension
819 -- Word " " Word rewrite as a single word using Ug_Words table
821 Next_Token;
822 loop
823 case Token.Kind is
824 when End_Of_Line =>
825 exit;
827 when Word =>
828 Rewrite_Word;
830 when Other =>
831 Maybe_Rewrite_Extension;
833 when VMS_Alternative =>
834 if Target = VMS then
835 Append (Rewritten_Line, Line (Token.VMS.First
836 .. Token.VMS.Last));
837 else
838 Append (Rewritten_Line, Line (Token.Non_VMS.First
839 .. Token.Non_VMS.Last));
840 end if;
842 Next_Token;
844 when VMS_Error =>
845 Error (Source_File, Token.First, "invalid VMS alternative");
846 Next_Token;
847 end case;
848 end loop;
850 return S (Rewritten_Line);
851 end Rewrite_Source_Line;
853 -------------------------
854 -- Process_Source_File --
855 -------------------------
857 procedure Process_Source_File is
858 begin
859 while not End_Of_File (Source_File.Data) loop
860 declare
861 Line : constant String := Get_Line (Source_File'Access);
863 Rewritten : constant String := Rewrite_Source_Line (Line);
864 -- We unconditionally rewrite the line so that we can check the
865 -- syntax of all lines, and not only those which are actually
866 -- included in the output.
868 begin
869 if First_Time
870 and then Line'Length > 3 and then Line (1 .. 3) = "@if"
871 then
872 Put_Line (Output_File, "@set " & Argument (1));
873 First_Time := False;
874 end if;
876 Put_Line (Output_File, Rewritten);
877 end;
878 end loop;
879 end Process_Source_File;
881 ---------------------------
882 -- Initialize_Extensions --
883 ---------------------------
885 procedure Initialize_Extensions is
887 procedure Add (Extension : String);
888 -- Adds an extension which is replaced with itself (in upper case)
890 procedure Add (Extension, Replacement : String);
891 -- Adds an extension with a custom replacement
893 ---------
894 -- Add --
895 ---------
897 procedure Add (Extension : String) is
898 begin
899 Add (Extension, Translate (Extension, Upper_Case_Map));
900 end Add;
902 procedure Add (Extension, Replacement : String) is
903 begin
904 Set (Extensions, Extension, V (Replacement));
905 end Add;
907 -- Start of processing for Initialize_Extensions
909 begin
910 -- To avoid performance degradation, increase the constant in the
911 -- definition of Extensions above if you add more extensions here.
913 Add ("o", "OBJ");
914 Add ("ads");
915 Add ("adb");
916 Add ("ali");
917 Add ("ada");
918 Add ("atb");
919 Add ("ats");
920 Add ("adc");
921 Add ("c");
922 end Initialize_Extensions;
924 ------------------
925 -- Is_Extension --
926 ------------------
928 function Is_Extension (Extension : String) return Boolean is
929 begin
930 return Present (Extensions, Extension);
931 end Is_Extension;
933 -------------------------------
934 -- Get_Replacement_Extension --
935 -------------------------------
937 function Get_Replacement_Extension (Extension : String) return String is
938 begin
939 return S (Get (Extensions, Extension));
940 end Get_Replacement_Extension;
942 -------------------
943 -- Is_Known_Word --
944 -------------------
946 function Is_Known_Word (Word : String) return Boolean is
947 begin
948 return Present (Ug_Words, Word);
949 end Is_Known_Word;
951 --------------------------
952 -- Get_Replacement_Word --
953 --------------------------
955 function Get_Replacement_Word (Word : String) return String is
956 begin
957 return S (Get (Ug_Words, Word));
958 end Get_Replacement_Word;
960 -- Start of processing for Xgnatugn
962 Valid_Command_Line : Boolean;
963 Output_File_Name : VString;
965 begin
966 Initialize_Extensions;
967 Valid_Command_Line := Argument_Count in 3 .. 5;
969 -- First argument: Target
971 if Valid_Command_Line then
972 begin
973 Target := Flag_Type'Value (Argument (1));
975 if not Target'Valid then
976 Valid_Command_Line := False;
977 end if;
979 exception
980 when Constraint_Error =>
981 Valid_Command_Line := False;
982 end;
983 end if;
985 -- Second argument: Source_File
987 if Valid_Command_Line then
988 begin
989 Source_File.Name := V (Argument (2));
990 Open (Source_File.Data, In_File, Argument (2));
992 exception
993 when Ada.Text_IO.Name_Error =>
994 Valid_Command_Line := False;
995 end;
996 end if;
998 -- Third argument: Dictionary_File
1000 if Valid_Command_Line then
1001 begin
1002 Dictionary_File.Name := V (Argument (3));
1003 Open (Dictionary_File.Data, In_File, Argument (3));
1005 exception
1006 when Ada.Text_IO.Name_Error =>
1007 Valid_Command_Line := False;
1008 end;
1009 end if;
1011 -- Fourth argument: Output_File
1013 if Valid_Command_Line then
1014 if Argument_Count in 4 .. 5 then
1015 Output_File_Name := V (Argument (4));
1016 else
1017 case Target is
1018 when UNW =>
1019 Output_File_Name := V ("gnat_ugn_unw.texi");
1020 when VMS =>
1021 Output_File_Name := V ("gnat_ugn_vms.texi");
1022 end case;
1023 end if;
1025 Warnings_Enabled := Argument_Count = 5;
1027 begin
1028 Create (Output_File, Out_File, S (Output_File_Name));
1030 exception
1031 when Ada.Text_IO.Name_Error | Ada.Text_IO.Use_Error =>
1032 Valid_Command_Line := False;
1033 end;
1034 end if;
1036 if not Valid_Command_Line then
1037 Usage;
1038 Set_Exit_Status (Failure);
1040 else
1041 Read_Dictionary_File;
1042 Close (Dictionary_File.Data);
1044 -- Main processing starts here
1046 Process_Source_File;
1047 Close (Output_File);
1048 Close (Source_File.Data);
1050 New_Line (Standard_Error);
1052 if Number_Of_Warnings = 0 then
1053 Put_Line (Standard_Error, " NO Warnings");
1055 else
1056 Put (Standard_Error, Integer'Image (Number_Of_Warnings));
1057 Put (Standard_Error, " Warning");
1059 if Number_Of_Warnings > 1 then
1060 Put (Standard_Error, "s");
1061 end if;
1063 New_Line (Standard_Error);
1064 end if;
1066 if Number_Of_Errors = 0 then
1067 Put_Line (Standard_Error, " NO Errors");
1069 else
1070 Put (Standard_Error, Integer'Image (Number_Of_Errors));
1071 Put (Standard_Error, " Error");
1073 if Number_Of_Errors > 1 then
1074 Put (Standard_Error, "s");
1075 end if;
1077 New_Line (Standard_Error);
1078 end if;
1080 if Number_Of_Errors /= 0 then
1081 Set_Exit_Status (Failure);
1082 else
1083 Set_Exit_Status (Success);
1084 end if;
1085 end if;
1086 end Xgnatugn;