1 ------------------------------------------------------------------------------
3 -- GNAT SYSTEM UTILITIES --
9 -- Copyright (C) 2003-2008, 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 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. --
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
34 -- 2. <in-file> is the file name of the Texinfo file to be
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
55 -- The following steps are performed:
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
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
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.
78 -- Any occurrences of ^alpha^beta^ are replaced by alpha. The sequence
79 -- must fit on a single line.
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 -- Process @ifset and @ifclear for the target flags (unw, vms);
89 -- this is because we have menu problems if we let makeinfo handle
90 -- these ifset/ifclear pairs.
91 -- Note: @ifset/@ifclear commands for the edition flags (FSFEDITION,
92 -- PROEDITION, GPLEDITION) are passed through unchanged
94 with Ada
.Command_Line
; use Ada
.Command_Line
;
95 with Ada
.Strings
; use Ada
.Strings
;
96 with Ada
.Strings
.Fixed
; use Ada
.Strings
.Fixed
;
97 with Ada
.Strings
.Unbounded
; use Ada
.Strings
.Unbounded
;
98 with Ada
.Strings
.Maps
; use Ada
.Strings
.Maps
;
99 with Ada
.Strings
.Maps
.Constants
; use Ada
.Strings
.Maps
.Constants
;
100 with Ada
.Streams
.Stream_IO
; use Ada
.Streams
.Stream_IO
;
101 with Ada
.Text_IO
; use Ada
.Text_IO
;
103 with GNAT
.Spitbol
; use GNAT
.Spitbol
;
104 with GNAT
.Spitbol
.Table_VString
; use GNAT
.Spitbol
.Table_VString
;
106 procedure Xgnatugn
is
109 -- Print usage information. Invoked if an invalid command line is
112 subtype Sfile
is Ada
.Streams
.Stream_IO
.File_Type
;
115 -- The preprocessed output is written to this file
117 type Input_File
is record
119 Data
: Ada
.Text_IO
.File_Type
;
122 -- Records information on an input file. Name and Line are used
123 -- in error messages, Line is updated automatically by Get_Line.
125 function Get_Line
(Input
: access Input_File
) return String;
126 -- Returns a line from Input and performs the necessary
127 -- line-oriented checks (length, character set, trailing spaces).
129 procedure Put_Line
(F
: Sfile
; S
: String);
130 procedure Put_Line
(F
: Sfile
; S
: VString
);
131 -- Local version of Put_Line ensures Unix style line endings
133 Number_Of_Warnings
: Natural := 0;
134 Number_Of_Errors
: Natural := 0;
135 Warnings_Enabled
: Boolean;
139 At_Character
: Natural;
144 -- Prints a message reporting an error on line Input.Line. If
145 -- At_Character is not 0, indicate the exact character at which
150 At_Character
: Natural;
155 -- Like Error, but just print a warning message
157 Dictionary_File
: aliased Input_File
;
158 procedure Read_Dictionary_File
;
159 -- Dictionary_File is opened using the name given on the command
160 -- line. It contains the replacements for the Ug_Words list.
161 -- Read_Dictionary_File reads Dictionary_File and fills the
164 Source_File
: aliased Input_File
;
165 procedure Process_Source_File
;
166 -- Source_File is opened using the name given on the command line.
167 -- It contains the Texinfo source code. Process_Source_File
168 -- performs the necessary replacements.
170 type Flag_Type
is (UNW
, VMS
, FSFEDITION
, PROEDITION
, GPLEDITION
);
171 -- The flags permitted in @ifset or @ifclear commands:
173 -- Targets for preprocessing
174 -- UNW (Unix and Windows) or VMS
176 -- Editions of the manual
177 -- FSFEDITION, PROEDITION, or GPLEDITION
179 -- Conditional commands for target are processed by xgnatugn
181 -- Conditional commands for edition are passed through unchanged
183 subtype Target_Type
is Flag_Type
range UNW
.. VMS
;
184 subtype Edition_Type
is Flag_Type
range FSFEDITION
.. GPLEDITION
;
186 Target
: Target_Type
;
187 -- The Target variable is initialized using the command line
189 Valid_Characters
: constant Character_Set
:=
190 To_Set
(Span
=> (' ', '~'));
191 -- This array controls which characters are permitted in the input
192 -- file (after line breaks have been removed). Valid characters
193 -- are all printable ASCII characters and the space character.
195 Word_Characters
: constant Character_Set
:=
197 (('0', '9'), ('a', 'z'), ('A', 'Z')))
199 -- The characters which are permitted in words. Other (valid)
200 -- characters are assumed to be delimiters between words. Note that
201 -- this set has to include all characters of the source words of the
202 -- Ug_Words dictionary.
204 Reject_Trailing_Spaces
: constant Boolean := True;
205 -- Controls whether Xgnatug rejects superfluous space characters
206 -- at the end of lines.
208 Maximum_Line_Length
: constant Positive := 79;
209 Fatal_Line_Length_Limit
: constant Positive := 5000;
210 Fatal_Line_Length
: exception;
211 -- If Maximum_Line_Length is exceeded in an input file, an error
212 -- message is printed. If Fatal_Line_Length is exceeded,
213 -- execution terminates with a Fatal_Line_Length exception.
215 VMS_Escape_Character
: constant Character := '^';
216 -- The character used to mark VMS alternatives (^alpha^beta^)
218 Extensions
: GNAT
.Spitbol
.Table_VString
.Table
(20);
219 procedure Initialize_Extensions
;
220 -- This table records extensions and their replacement for
221 -- rewriting filenames in the VMS version of the manual.
223 function Is_Extension
(Extension
: String) return Boolean;
224 function Get_Replacement_Extension
(Extension
: String) return String;
225 -- These functions query the replacement table. Is_Extension
226 -- checks if the given string is a known extension.
227 -- Get_Replacement returns the replacement extension.
229 Ug_Words
: GNAT
.Spitbol
.Table_VString
.Table
(200);
230 function Is_Known_Word
(Word
: String) return Boolean;
231 function Get_Replacement_Word
(Word
: String) return String;
232 -- The Ug_Words table lists replacement words for the VMS version
233 -- of the manual. Is_Known_Word and Get_Replacement_Word query
234 -- this table. The table is filled using Read_Dictionary_File.
236 function Rewrite_Source_Line
(Line
: String) return String;
237 -- This subprogram takes a line and rewrites it according to Target.
238 -- It relies on information in Source_File to generate error messages.
240 type Conditional
is (Set
, Clear
);
241 procedure Push_Conditional
(Cond
: Conditional
; Flag
: Target_Type
);
242 procedure Pop_Conditional
(Cond
: Conditional
);
243 -- These subprograms deal with conditional processing (@ifset/@ifclear).
244 -- They rely on information in Source_File to generate error messages.
246 function Currently_Excluding
return Boolean;
247 -- Returns true if conditional processing directives imply that the
248 -- current line should not be included in the output.
250 function VMS_Context_Determined
return Boolean;
251 -- Returns true if, in the current conditional preprocessing context, we
252 -- always have a VMS or a non-VMS version, regardless of the value of
255 function In_VMS_Section
return Boolean;
256 -- Returns True if in an "@ifset vms" section
258 procedure Check_No_Pending_Conditional
;
259 -- Checks that all preprocessing directives have been properly matched by
260 -- their @end counterpart. If this is not the case, print an error
263 -- The following definitions implement a stack to track the conditional
264 -- preprocessing context.
266 type Conditional_Context
is record
267 Starting_Line
: Positive;
273 Conditional_Stack_Depth
: constant := 3;
276 array (1 .. Conditional_Stack_Depth
) of Conditional_Context
;
278 Conditional_TOS
: Natural := 0;
279 -- Pointer to the Top Of Stack for Conditional_Stack
287 Put_Line
(Standard_Error
,
288 "usage: xgnatugn TARGET SOURCE DICTIONARY [OUTFILE [WARNINGS]]");
290 Put_Line
(Standard_Error
, "TARGET is one of:");
292 for T
in Target_Type
'Range loop
293 Put_Line
(Standard_Error
, " " & Target_Type
'Image (T
));
297 Put_Line
(Standard_Error
, "SOURCE is the source file to process.");
299 Put_Line
(Standard_Error
, "DICTIONARY is the name of a file "
300 & "that contains word replacements");
301 Put_Line
(Standard_Error
, "for the VMS version.");
303 Put_Line
(Standard_Error
,
304 "OUT-FILE, if present, is the output file to be created;");
305 Put_Line
(Standard_Error
,
306 "If OUT-FILE is absent, the output file is either " &
307 "gnat_ugn_unw.texi, ");
308 Put_Line
(Standard_Error
,
309 "or gnat_ugn_vms.texi, depending on TARGET.");
311 Put_Line
(Standard_Error
,
312 "WARNINGS, if present, is any string;");
313 Put_Line
(Standard_Error
,
314 "it will result in warning messages (e.g., line too long))");
315 Put_Line
(Standard_Error
,
316 "being output to Standard_Error.");
323 function Get_Line
(Input
: access Input_File
) return String is
324 Line_Buffer
: String (1 .. Fatal_Line_Length_Limit
);
328 Input
.Line
:= Input
.Line
+ 1;
329 Get_Line
(Input
.Data
, Line_Buffer
, Last
);
331 if Last
= Line_Buffer
'Last then
332 Error
(Input
.all, "line exceeds fatal line length limit");
333 raise Fatal_Line_Length
;
337 Line
: String renames Line_Buffer
(Line_Buffer
'First .. Last
);
340 for J
in Line
'Range loop
341 if not Is_In
(Line
(J
), Valid_Characters
) then
342 Error
(Input
.all, J
, "invalid character");
347 if Line
'Length > Maximum_Line_Length
then
348 Warning
(Input
.all, Maximum_Line_Length
+ 1, "line too long");
351 if Reject_Trailing_Spaces
352 and then Line
'Length > 0
353 and then Line
(Line
'Last) = ' '
355 Error
(Input
.all, Line
'Last, "trailing space character");
358 return Trim
(Line
, Right
);
366 procedure Put_Line
(F
: Sfile
; S
: String) is
368 String'Write (Stream
(F
), S
);
369 Character'Write (Stream
(F
), ASCII
.LF
);
372 procedure Put_Line
(F
: Sfile
; S
: VString
) is
374 Put_Line
(F
, To_String
(S
));
386 Error
(Input
, 0, Message
);
391 At_Character
: Natural;
394 Line_Image
: constant String := Integer'Image (Input
.Line
);
395 At_Character_Image
: constant String := Integer'Image (At_Character
);
396 -- These variables are required because we have to drop the leading
400 Number_Of_Errors
:= Number_Of_Errors
+ 1;
402 if At_Character
> 0 then
403 Put_Line
(Standard_Error
,
405 & Line_Image
(Line_Image
'First + 1 .. Line_Image
'Last) & ':'
406 & At_Character_Image
(At_Character_Image
'First + 1
407 .. At_Character_Image
'Last)
411 Put_Line
(Standard_Error
,
413 & Line_Image
(Line_Image
'First + 1 .. Line_Image
'Last)
428 if Warnings_Enabled
then
429 Warning
(Input
, 0, Message
);
435 At_Character
: Natural;
438 Line_Image
: constant String := Integer'Image (Input
.Line
);
439 At_Character_Image
: constant String := Integer'Image (At_Character
);
440 -- These variables are required because we have to drop the leading
444 if not Warnings_Enabled
then
448 Number_Of_Warnings
:= Number_Of_Warnings
+ 1;
450 if At_Character
> 0 then
451 Put_Line
(Standard_Error
,
453 & Line_Image
(Line_Image
'First + 1 .. Line_Image
'Last) & ':'
454 & At_Character_Image
(At_Character_Image
'First + 1
455 .. At_Character_Image
'Last)
459 Put_Line
(Standard_Error
,
461 & Line_Image
(Line_Image
'First + 1 .. Line_Image
'Last)
467 --------------------------
468 -- Read_Dictionary_File --
469 --------------------------
471 procedure Read_Dictionary_File
is
473 while not End_Of_File
(Dictionary_File
.Data
) loop
475 Line
: constant String :=
476 Get_Line
(Dictionary_File
'Access);
477 Split
: constant Natural :=
478 Index
(Line
, (1 => VMS_Escape_Character
));
481 if Line
'Length = 0 then
482 Error
(Dictionary_File
, "empty line in dictionary file");
484 elsif Line
(Line
'First) = ' ' then
485 Error
(Dictionary_File
, 1, "line starts with space character");
488 Error
(Dictionary_File
, "line does not contain "
489 & VMS_Escape_Character
& " character");
492 Source
: constant String :=
493 Trim
(Line
(1 .. Split
- 1), Both
);
494 Target
: constant String :=
495 Trim
(Line
(Split
+ 1 .. Line
'Last), Both
);
496 Two_Spaces
: constant Natural :=
498 Non_Word_Character
: constant Natural :=
505 if Two_Spaces
/= 0 then
506 Error
(Dictionary_File
, Two_Spaces
,
507 "multiple space characters in source word");
510 if Non_Word_Character
/= 0 then
511 Error
(Dictionary_File
, Non_Word_Character
,
512 "illegal character in source word");
515 if Source
'Length = 0 then
516 Error
(Dictionary_File
, "source is empty");
518 elsif Target
'Length = 0 then
519 Error
(Dictionary_File
, "target is empty");
522 Set
(Ug_Words
, Source
, V
(Target
));
524 -- Ensure that if Source is a sequence of words
525 -- "WORD1 WORD2 ...", we already have a mapping for
528 for J
in Source
'Range loop
529 if Source
(J
) = ' ' then
531 Prefix
: String renames
532 Source
(Source
'First .. J
- 1);
535 if not Is_Known_Word
(Prefix
) then
536 Error
(Dictionary_File
,
538 & "' not known at this point");
548 end Read_Dictionary_File
;
550 -------------------------
551 -- Rewrite_Source_Line --
552 -------------------------
554 function Rewrite_Source_Line
(Line
: String) return String is
556 -- We use a simple lexer to split the line into tokens:
558 -- Word consisting entirely of Word_Characters
559 -- VMS_Alternative ^alpha^beta^ replacement (but not ^^^)
560 -- Space a space character
561 -- Other everything else (sequence of non-word characters)
562 -- VMS_Error incomplete VMS alternative
563 -- End_Of_Line no more characters on this line
565 -- A sequence of three VMS_Escape_Characters is automatically
566 -- collapsed to an Other token.
568 type Token_Span
is record
569 First
, Last
: Positive;
571 -- The character range covered by a token in Line
573 type Token_Kind
is (End_Of_Line
, Word
, Other
,
574 VMS_Alternative
, VMS_Error
);
575 type Token_Record
(Kind
: Token_Kind
:= End_Of_Line
) is record
580 when VMS_Alternative
=>
581 Non_VMS
, VMS
: Token_Span
;
582 when VMS_Error | End_Of_Line
=>
587 Input_Position
: Positive := Line
'First;
588 Token
: Token_Record
;
589 -- The position of the next character to be processed by Next_Token
591 procedure Next_Token
;
592 -- Returns the next token in Line, starting at Input_Position
594 Rewritten_Line
: VString
;
595 -- Collects the line as it is rewritten
597 procedure Rewrite_Word
;
598 -- The current token is assumed to be a Word. When processing the VMS
599 -- version of the manual, additional tokens are gathered to check if
600 -- we have a file name or a sequence of known words.
602 procedure Maybe_Rewrite_Extension
;
603 -- The current token is assumed to be Other. When processing the VMS
604 -- version of the manual and the token represents a single dot ".",
605 -- the following word is rewritten according to the rules for
608 VMS_Token_Seen
: Boolean := False;
609 -- This is set to true if a VMS_Alternative has been encountered, or a
616 procedure Next_Token
is
617 Remaining_Line
: String renames Line
(Input_Position
.. Line
'Last);
618 Last_Character
: Natural;
621 if Remaining_Line
'Length = 0 then
622 Token
:= (End_Of_Line
, Remaining_Line
'First);
626 -- ^alpha^beta^, the VMS_Alternative case
628 if Remaining_Line
(Remaining_Line
'First) = VMS_Escape_Character
then
630 VMS_Second_Character
, VMS_Third_Character
: Natural;
633 if VMS_Token_Seen
then
634 Error
(Source_File
, Remaining_Line
'First,
635 "multiple " & VMS_Escape_Character
636 & " characters on a single line");
638 VMS_Token_Seen
:= True;
641 -- Find the second and third escape character. If one of
642 -- them is not present, generate an error token.
644 VMS_Second_Character
:=
645 Index
(Remaining_Line
(Remaining_Line
'First + 1
646 .. Remaining_Line
'Last),
647 (1 => VMS_Escape_Character
));
649 if VMS_Second_Character
= 0 then
650 Input_Position
:= Remaining_Line
'Last + 1;
651 Token
:= (VMS_Error
, Remaining_Line
'First);
655 VMS_Third_Character
:=
656 Index
(Remaining_Line
(VMS_Second_Character
+ 1
657 .. Remaining_Line
'Last),
658 (1 => VMS_Escape_Character
));
660 if VMS_Third_Character
= 0 then
661 Input_Position
:= Remaining_Line
'Last + 1;
662 Token
:= (VMS_Error
, Remaining_Line
'First);
666 -- Consume all the characters we are about to include in
669 Input_Position
:= VMS_Third_Character
+ 1;
671 -- Check if we are in a ^^^ situation, and return an Other
672 -- token in this case.
674 if Remaining_Line
'First + 1 = VMS_Second_Character
675 and then Remaining_Line
'First + 2 = VMS_Third_Character
677 Token
:= (Other
, Remaining_Line
'First,
678 (Remaining_Line
'First, Remaining_Line
'First));
682 Token
:= (VMS_Alternative
, Remaining_Line
'First,
683 (Remaining_Line
'First + 1, VMS_Second_Character
- 1),
684 (VMS_Second_Character
+ 1, VMS_Third_Character
- 1));
687 end if; -- VMS_Alternative
689 -- The Word case. Search for characters not in Word_Characters.
690 -- We have found a word if the first non-word character is not
691 -- the first character in Remaining_Line, i.e. if Remaining_Line
692 -- starts with a word character.
694 Last_Character
:= Index
(Remaining_Line
, Word_Characters
, Outside
);
695 if Last_Character
/= Remaining_Line
'First then
697 -- If we haven't found a character which is not in
698 -- Word_Characters, all remaining characters are part of the
699 -- current Word token.
701 if Last_Character
= 0 then
702 Last_Character
:= Remaining_Line
'Last + 1;
705 Input_Position
:= Last_Character
;
706 Token
:= (Word
, Remaining_Line
'First,
707 (Remaining_Line
'First, Last_Character
- 1));
711 -- Remaining characters are in the Other category. To speed
712 -- up processing, we collect them together if there are several
715 Input_Position
:= Last_Character
+ 1;
717 Remaining_Line
'First,
718 (Remaining_Line
'First, Last_Character
));
725 procedure Rewrite_Word
is
727 renames Line
(Token
.Span
.First
.. Token
.Span
.Last
);
730 -- We do not perform any error checking below, so we can just skip
731 -- all processing for the non-VMS version.
733 if Target
/= VMS
then
734 Append
(Rewritten_Line
, First_Word
);
739 if Is_Known_Word
(First_Word
) then
741 -- If we have a word from the dictionary, we look for the
742 -- longest possible sequence we can rewrite.
745 Seq
: Token_Span
:= Token
.Span
;
746 Lost_Space
: Boolean := False;
751 if Token
.Kind
= Other
752 and then Line
(Token
.Span
.First
.. Token
.Span
.Last
) = " "
755 if Token
.Kind
/= Word
756 or else not Is_Known_Word
(Line
(Seq
.First
759 -- When we reach this point, the following
760 -- conditions are true:
762 -- Seq is a known word.
763 -- The previous token was a space character.
764 -- Seq extended to the current token is not a
772 -- Extend Seq to cover the current (known) word
774 Seq
.Last
:= Token
.Span
.Last
;
779 -- When we reach this point, the following conditions
782 -- Seq is a known word.
783 -- The previous token was a word.
784 -- The current token is not a space character.
790 -- Rewrite Seq, and add the lost space if necessary
792 Append
(Rewritten_Line
,
793 Get_Replacement_Word
(Line
(Seq
.First
.. Seq
.Last
)));
795 Append
(Rewritten_Line
, ' ');
798 -- The unknown token will be processed during the
799 -- next iteration of the main loop.
806 if Token
.Kind
= Other
807 and then Line
(Token
.Span
.First
.. Token
.Span
.Last
) = "."
809 -- Deal with extensions
813 and then Is_Extension
(Line
(Token
.Span
.First
816 -- We have discovered a file extension. Convert the file
817 -- name to upper case.
819 Append
(Rewritten_Line
,
820 Translate
(First_Word
, Upper_Case_Map
) & '.');
821 Append
(Rewritten_Line
,
822 Get_Replacement_Extension
823 (Line
(Token
.Span
.First
.. Token
.Span
.Last
)));
826 -- We already have: Word ".", followed by an unknown token
828 Append
(Rewritten_Line
, First_Word
& '.');
830 -- The unknown token will be processed during the next
831 -- iteration of the main loop.
835 -- We have an unknown Word, followed by an unknown token.
836 -- The unknown token will be processed by the outer loop.
838 Append
(Rewritten_Line
, First_Word
);
842 -----------------------------
843 -- Maybe_Rewrite_Extension --
844 -----------------------------
846 procedure Maybe_Rewrite_Extension
is
848 -- Again, we need no special processing in the non-VMS case
851 and then Line
(Token
.Span
.First
.. Token
.Span
.Last
) = "."
853 -- This extension is not preceded by a word, otherwise
854 -- Rewrite_Word would have handled it.
858 and then Is_Extension
(Line
(Token
.Span
.First
861 Append
(Rewritten_Line
, '.' & Get_Replacement_Extension
862 (Line
(Token
.Span
.First
.. Token
.Span
.Last
)));
865 Append
(Rewritten_Line
, '.');
868 Append
(Rewritten_Line
, Line
(Token
.Span
.First
869 .. Token
.Span
.Last
));
872 end Maybe_Rewrite_Extension
;
874 -- Start of processing for Process_Source_Line
877 -- The following parser recognizes the following special token
880 -- Word "." Word rewrite as file name if second word is extension
881 -- Word " " Word rewrite as a single word using Ug_Words table
893 Maybe_Rewrite_Extension
;
895 when VMS_Alternative
=>
896 if VMS_Context_Determined
then
897 if (not In_VMS_Section
)
899 Line
(Token
.VMS
.First
.. Token
.VMS
.Last
) /=
900 Line
(Token
.Non_VMS
.First
.. Token
.Non_VMS
.Last
)
902 Warning
(Source_File
, Token
.First
,
903 "VMS alternative already determined "
904 & "by conditionals");
908 Append
(Rewritten_Line
, Line
(Token
.VMS
.First
911 Append
(Rewritten_Line
, Line
(Token
.Non_VMS
.First
912 .. Token
.Non_VMS
.Last
));
917 Error
(Source_File
, Token
.First
, "invalid VMS alternative");
922 return S
(Rewritten_Line
);
923 end Rewrite_Source_Line
;
925 -------------------------
926 -- Process_Source_File --
927 -------------------------
929 procedure Process_Source_File
is
930 Ifset
: constant String := "@ifset ";
931 Ifclear
: constant String := "@ifclear ";
932 Endsetclear
: constant String := "@end ";
933 -- Strings to be recognized for conditional processing
936 while not End_Of_File
(Source_File
.Data
) loop
938 Line
: constant String := Get_Line
(Source_File
'Access);
939 Rewritten
: constant String := Rewrite_Source_Line
(Line
);
940 -- We unconditionally rewrite the line so that we can check the
941 -- syntax of all lines, and not only those which are actually
942 -- included in the output.
944 Have_Conditional
: Boolean := False;
945 -- True if we have encountered a conditional preprocessing
949 -- The kind of the directive
955 -- If the line starts with @ifset or @ifclear, we try to convert
956 -- the following flag to one of our flag types. If we fail,
957 -- Have_Conditional remains False.
959 if Line
'Length >= Ifset
'Length
960 and then Line
(1 .. Ifset
'Length) = Ifset
965 Arg
: constant String :=
966 Trim
(Line
(Ifset
'Length + 1 .. Line
'Last), Both
);
969 Flag
:= Flag_Type
'Value (Arg
);
970 Have_Conditional
:= True;
974 if Translate
(Target_Type
'Image (Flag
),
978 Error
(Source_File
, "flag has to be lowercase");
985 when Constraint_Error
=>
986 Error
(Source_File
, "unknown flag for '@ifset'");
989 elsif Line
'Length >= Ifclear
'Length
990 and then Line
(1 .. Ifclear
'Length) = Ifclear
995 Arg
: constant String :=
996 Trim
(Line
(Ifclear
'Length + 1 .. Line
'Last), Both
);
999 Flag
:= Flag_Type
'Value (Arg
);
1000 Have_Conditional
:= True;
1004 if Translate
(Target_Type
'Image (Flag
),
1008 Error
(Source_File
, "flag has to be lowercase");
1011 when Edition_Type
=>
1015 when Constraint_Error
=>
1016 Error
(Source_File
, "unknown flag for '@ifclear'");
1020 if Have_Conditional
and (Flag
in Target_Type
) then
1022 -- We create a new conditional context and suppress the
1023 -- directive in the output.
1025 Push_Conditional
(Cond
, Flag
);
1027 elsif Line
'Length >= Endsetclear
'Length
1028 and then Line
(1 .. Endsetclear
'Length) = Endsetclear
1029 and then (Flag
in Target_Type
)
1031 -- The '@end ifset'/'@end ifclear' case is handled here. We
1032 -- have to pop the conditional context.
1035 First
, Last
: Natural;
1038 Find_Token
(Source
=> Line
(Endsetclear
'Length + 1
1046 Error
(Source_File
, "'@end' without argument");
1048 if Line
(First
.. Last
) = "ifset" then
1049 Have_Conditional
:= True;
1051 elsif Line
(First
.. Last
) = "ifclear" then
1052 Have_Conditional
:= True;
1056 if Have_Conditional
then
1057 Pop_Conditional
(Cond
);
1060 -- We fall through to the ordinary case for other @end
1063 end if; -- @end without argument
1065 end if; -- Have_Conditional
1067 if (not Have_Conditional
) or (Flag
in Edition_Type
) then
1069 -- The ordinary case
1071 if not Currently_Excluding
then
1072 Put_Line
(Output_File
, Rewritten
);
1078 Check_No_Pending_Conditional
;
1079 end Process_Source_File
;
1081 ---------------------------
1082 -- Initialize_Extensions --
1083 ---------------------------
1085 procedure Initialize_Extensions
is
1087 procedure Add
(Extension
: String);
1088 -- Adds an extension which is replaced with itself (in upper
1091 procedure Add
(Extension
, Replacement
: String);
1092 -- Adds an extension with a custom replacement
1098 procedure Add
(Extension
: String) is
1100 Add
(Extension
, Translate
(Extension
, Upper_Case_Map
));
1103 procedure Add
(Extension
, Replacement
: String) is
1105 Set
(Extensions
, Extension
, V
(Replacement
));
1108 -- Start of processing for Initialize_Extensions
1111 -- To avoid performance degradation, increase the constant in the
1112 -- definition of Extensions above if you add more extensions here.
1123 end Initialize_Extensions
;
1129 function Is_Extension
(Extension
: String) return Boolean is
1131 return Present
(Extensions
, Extension
);
1134 -------------------------------
1135 -- Get_Replacement_Extension --
1136 -------------------------------
1138 function Get_Replacement_Extension
(Extension
: String) return String is
1140 return S
(Get
(Extensions
, Extension
));
1141 end Get_Replacement_Extension
;
1147 function Is_Known_Word
(Word
: String) return Boolean is
1149 return Present
(Ug_Words
, Word
);
1152 --------------------------
1153 -- Get_Replacement_Word --
1154 --------------------------
1156 function Get_Replacement_Word
(Word
: String) return String is
1158 return S
(Get
(Ug_Words
, Word
));
1159 end Get_Replacement_Word
;
1161 ----------------------
1162 -- Push_Conditional --
1163 ----------------------
1165 procedure Push_Conditional
(Cond
: Conditional
; Flag
: Target_Type
) is
1166 Will_Exclude
: Boolean;
1169 -- If we are already in an excluding context, inherit this property,
1170 -- otherwise calculate it from scratch.
1172 if Conditional_TOS
> 0
1173 and then Conditional_Stack
(Conditional_TOS
).Excluding
1175 Will_Exclude
:= True;
1179 Will_Exclude
:= Flag
/= Target
;
1181 Will_Exclude
:= Flag
= Target
;
1185 -- Check if the current directive is pointless because of a previous,
1186 -- enclosing directive.
1188 for J
in 1 .. Conditional_TOS
loop
1189 if Conditional_Stack
(J
).Flag
= Flag
then
1190 Warning
(Source_File
, "directive without effect because of line"
1191 & Integer'Image (Conditional_Stack
(J
).Starting_Line
));
1195 Conditional_TOS
:= Conditional_TOS
+ 1;
1196 Conditional_Stack
(Conditional_TOS
) :=
1197 (Starting_Line
=> Source_File
.Line
,
1200 Excluding
=> Will_Exclude
);
1201 end Push_Conditional
;
1203 ---------------------
1204 -- Pop_Conditional --
1205 ---------------------
1207 procedure Pop_Conditional
(Cond
: Conditional
) is
1209 if Conditional_TOS
> 0 then
1212 if Conditional_Stack
(Conditional_TOS
).Cond
/= Set
then
1214 "'@end ifset' does not match '@ifclear' at line"
1215 & Integer'Image (Conditional_Stack
1216 (Conditional_TOS
).Starting_Line
));
1220 if Conditional_Stack
(Conditional_TOS
).Cond
/= Clear
then
1222 "'@end ifclear' does not match '@ifset' at line"
1223 & Integer'Image (Conditional_Stack
1224 (Conditional_TOS
).Starting_Line
));
1228 Conditional_TOS
:= Conditional_TOS
- 1;
1234 "'@end ifset' without corresponding '@ifset'");
1238 "'@end ifclear' without corresponding '@ifclear'");
1241 end Pop_Conditional
;
1243 -------------------------
1244 -- Currently_Excluding --
1245 -------------------------
1247 function Currently_Excluding
return Boolean is
1249 return Conditional_TOS
> 0
1250 and then Conditional_Stack
(Conditional_TOS
).Excluding
;
1251 end Currently_Excluding
;
1253 ----------------------------
1254 -- VMS_Context_Determined --
1255 ----------------------------
1257 function VMS_Context_Determined
return Boolean is
1259 for J
in 1 .. Conditional_TOS
loop
1260 if Conditional_Stack
(J
).Flag
= VMS
then
1266 end VMS_Context_Determined
;
1268 --------------------
1269 -- In_VMS_Section --
1270 --------------------
1272 function In_VMS_Section
return Boolean is
1274 for J
in 1 .. Conditional_TOS
loop
1275 if Conditional_Stack
(J
).Flag
= VMS
then
1276 return Conditional_Stack
(J
).Cond
= Set
;
1283 ----------------------------------
1284 -- Check_No_Pending_Conditional --
1285 ----------------------------------
1287 procedure Check_No_Pending_Conditional
is
1289 for J
in 1 .. Conditional_TOS
loop
1290 case Conditional_Stack
(J
).Cond
is
1292 Error
(Source_File
, "Missing '@end ifset' for '@ifset' at line"
1293 & Integer'Image (Conditional_Stack
(J
).Starting_Line
));
1297 "Missing '@end ifclear' for '@ifclear' at line"
1298 & Integer'Image (Conditional_Stack
(J
).Starting_Line
));
1301 end Check_No_Pending_Conditional
;
1303 -- Start of processing for Xgnatugn
1305 Valid_Command_Line
: Boolean;
1306 Output_File_Name
: VString
;
1309 Initialize_Extensions
;
1310 Valid_Command_Line
:= Argument_Count
in 3 .. 5;
1312 -- First argument: Target
1314 if Valid_Command_Line
then
1316 Target
:= Flag_Type
'Value (Argument
(1));
1318 if not Target
'Valid then
1319 Valid_Command_Line
:= False;
1323 when Constraint_Error
=>
1324 Valid_Command_Line
:= False;
1328 -- Second argument: Source_File
1330 if Valid_Command_Line
then
1332 Source_File
.Name
:= V
(Argument
(2));
1333 Open
(Source_File
.Data
, In_File
, Argument
(2));
1336 when Ada
.Text_IO
.Name_Error
=>
1337 Valid_Command_Line
:= False;
1341 -- Third argument: Dictionary_File
1343 if Valid_Command_Line
then
1345 Dictionary_File
.Name
:= V
(Argument
(3));
1346 Open
(Dictionary_File
.Data
, In_File
, Argument
(3));
1349 when Ada
.Text_IO
.Name_Error
=>
1350 Valid_Command_Line
:= False;
1354 -- Fourth argument: Output_File
1356 if Valid_Command_Line
then
1357 if Argument_Count
in 4 .. 5 then
1358 Output_File_Name
:= V
(Argument
(4));
1362 Output_File_Name
:= V
("gnat_ugn_unw.texi");
1364 Output_File_Name
:= V
("gnat_ugn_vms.texi");
1368 Warnings_Enabled
:= Argument_Count
= 5;
1371 Create
(Output_File
, Out_File
, S
(Output_File_Name
));
1374 when Ada
.Text_IO
.Name_Error | Ada
.Text_IO
.Use_Error
=>
1375 Valid_Command_Line
:= False;
1379 if not Valid_Command_Line
then
1381 Set_Exit_Status
(Failure
);
1384 Read_Dictionary_File
;
1385 Close
(Dictionary_File
.Data
);
1387 -- Main processing starts here
1389 Process_Source_File
;
1390 Close
(Output_File
);
1391 Close
(Source_File
.Data
);
1393 New_Line
(Standard_Error
);
1395 if Number_Of_Warnings
= 0 then
1396 Put_Line
(Standard_Error
, " NO Warnings");
1399 Put
(Standard_Error
, Integer'Image (Number_Of_Warnings
));
1400 Put
(Standard_Error
, " Warning");
1402 if Number_Of_Warnings
> 1 then
1403 Put
(Standard_Error
, "s");
1406 New_Line
(Standard_Error
);
1409 if Number_Of_Errors
= 0 then
1410 Put_Line
(Standard_Error
, " NO Errors");
1413 Put
(Standard_Error
, Integer'Image (Number_Of_Errors
));
1414 Put
(Standard_Error
, " Error");
1416 if Number_Of_Errors
> 1 then
1417 Put
(Standard_Error
, "s");
1420 New_Line
(Standard_Error
);
1423 if Number_Of_Errors
/= 0 then
1424 Set_Exit_Status
(Failure
);
1426 Set_Exit_Status
(Success
);