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 -- Local version of Put_Line ensures Unix style line endings
132 Number_Of_Warnings
: Natural := 0;
133 Number_Of_Errors
: Natural := 0;
134 Warnings_Enabled
: Boolean;
138 At_Character
: Natural;
143 -- Prints a message reporting an error on line Input.Line. If
144 -- At_Character is not 0, indicate the exact character at which
149 At_Character
: Natural;
154 -- Like Error, but just print a warning message
156 Dictionary_File
: aliased Input_File
;
157 procedure Read_Dictionary_File
;
158 -- Dictionary_File is opened using the name given on the command
159 -- line. It contains the replacements for the Ug_Words list.
160 -- Read_Dictionary_File reads Dictionary_File and fills the
163 Source_File
: aliased Input_File
;
164 procedure Process_Source_File
;
165 -- Source_File is opened using the name given on the command line.
166 -- It contains the Texinfo source code. Process_Source_File
167 -- performs the necessary replacements.
169 type Flag_Type
is (UNW
, VMS
, FSFEDITION
, PROEDITION
, GPLEDITION
);
170 -- The flags permitted in @ifset or @ifclear commands:
172 -- Targets for preprocessing
173 -- UNW (Unix and Windows) or VMS
175 -- Editions of the manual
176 -- FSFEDITION, PROEDITION, or GPLEDITION
178 -- Conditional commands for target are processed by xgnatugn
180 -- Conditional commands for edition are passed through unchanged
182 subtype Target_Type
is Flag_Type
range UNW
.. VMS
;
183 subtype Edition_Type
is Flag_Type
range FSFEDITION
.. GPLEDITION
;
185 Target
: Target_Type
;
186 -- The Target variable is initialized using the command line
188 Valid_Characters
: constant Character_Set
:=
189 To_Set
(Span
=> (' ', '~'));
190 -- This array controls which characters are permitted in the input
191 -- file (after line breaks have been removed). Valid characters
192 -- are all printable ASCII characters and the space character.
194 Word_Characters
: constant Character_Set
:=
196 (('0', '9'), ('a', 'z'), ('A', 'Z')))
198 -- The characters which are permitted in words. Other (valid)
199 -- characters are assumed to be delimiters between words. Note that
200 -- this set has to include all characters of the source words of the
201 -- Ug_Words dictionary.
203 Reject_Trailing_Spaces
: constant Boolean := True;
204 -- Controls whether Xgnatug rejects superfluous space characters
205 -- at the end of lines.
207 Maximum_Line_Length
: constant Positive := 79;
208 Fatal_Line_Length_Limit
: constant Positive := 5000;
209 Fatal_Line_Length
: exception;
210 -- If Maximum_Line_Length is exceeded in an input file, an error
211 -- message is printed. If Fatal_Line_Length is exceeded,
212 -- execution terminates with a Fatal_Line_Length exception.
214 VMS_Escape_Character
: constant Character := '^';
215 -- The character used to mark VMS alternatives (^alpha^beta^)
217 Extensions
: GNAT
.Spitbol
.Table_VString
.Table
(20);
218 procedure Initialize_Extensions
;
219 -- This table records extensions and their replacement for
220 -- rewriting filenames in the VMS version of the manual.
222 function Is_Extension
(Extension
: String) return Boolean;
223 function Get_Replacement_Extension
(Extension
: String) return String;
224 -- These functions query the replacement table. Is_Extension
225 -- checks if the given string is a known extension.
226 -- Get_Replacement returns the replacement extension.
228 Ug_Words
: GNAT
.Spitbol
.Table_VString
.Table
(200);
229 function Is_Known_Word
(Word
: String) return Boolean;
230 function Get_Replacement_Word
(Word
: String) return String;
231 -- The Ug_Words table lists replacement words for the VMS version
232 -- of the manual. Is_Known_Word and Get_Replacement_Word query
233 -- this table. The table is filled using Read_Dictionary_File.
235 function Rewrite_Source_Line
(Line
: String) return String;
236 -- This subprogram takes a line and rewrites it according to Target.
237 -- It relies on information in Source_File to generate error messages.
239 type Conditional
is (Set
, Clear
);
240 procedure Push_Conditional
(Cond
: Conditional
; Flag
: Target_Type
);
241 procedure Pop_Conditional
(Cond
: Conditional
);
242 -- These subprograms deal with conditional processing (@ifset/@ifclear).
243 -- They rely on information in Source_File to generate error messages.
245 function Currently_Excluding
return Boolean;
246 -- Returns true if conditional processing directives imply that the
247 -- current line should not be included in the output.
249 function VMS_Context_Determined
return Boolean;
250 -- Returns true if, in the current conditional preprocessing context, we
251 -- always have a VMS or a non-VMS version, regardless of the value of
254 function In_VMS_Section
return Boolean;
255 -- Returns True if in an "@ifset vms" section
257 procedure Check_No_Pending_Conditional
;
258 -- Checks that all preprocessing directives have been properly matched by
259 -- their @end counterpart. If this is not the case, print an error
262 -- The following definitions implement a stack to track the conditional
263 -- preprocessing context.
265 type Conditional_Context
is record
266 Starting_Line
: Positive;
272 Conditional_Stack_Depth
: constant := 3;
275 array (1 .. Conditional_Stack_Depth
) of Conditional_Context
;
277 Conditional_TOS
: Natural := 0;
278 -- Pointer to the Top Of Stack for Conditional_Stack
286 Put_Line
(Standard_Error
,
287 "usage: xgnatugn TARGET SOURCE DICTIONARY [OUTFILE [WARNINGS]]");
289 Put_Line
(Standard_Error
, "TARGET is one of:");
291 for T
in Target_Type
'Range loop
292 Put_Line
(Standard_Error
, " " & Target_Type
'Image (T
));
296 Put_Line
(Standard_Error
, "SOURCE is the source file to process.");
298 Put_Line
(Standard_Error
, "DICTIONARY is the name of a file "
299 & "that contains word replacements");
300 Put_Line
(Standard_Error
, "for the VMS version.");
302 Put_Line
(Standard_Error
,
303 "OUT-FILE, if present, is the output file to be created;");
304 Put_Line
(Standard_Error
,
305 "If OUT-FILE is absent, the output file is either " &
306 "gnat_ugn_unw.texi, ");
307 Put_Line
(Standard_Error
,
308 "or gnat_ugn_vms.texi, depending on TARGET.");
310 Put_Line
(Standard_Error
,
311 "WARNINGS, if present, is any string;");
312 Put_Line
(Standard_Error
,
313 "it will result in warning messages (e.g., line too long))");
314 Put_Line
(Standard_Error
,
315 "being output to Standard_Error.");
322 function Get_Line
(Input
: access Input_File
) return String is
323 Line_Buffer
: String (1 .. Fatal_Line_Length_Limit
);
327 Input
.Line
:= Input
.Line
+ 1;
328 Get_Line
(Input
.Data
, Line_Buffer
, Last
);
330 if Last
= Line_Buffer
'Last then
331 Error
(Input
.all, "line exceeds fatal line length limit");
332 raise Fatal_Line_Length
;
336 Line
: String renames Line_Buffer
(Line_Buffer
'First .. Last
);
339 for J
in Line
'Range loop
340 if not Is_In
(Line
(J
), Valid_Characters
) then
341 Error
(Input
.all, J
, "invalid character");
346 if Line
'Length > Maximum_Line_Length
then
347 Warning
(Input
.all, Maximum_Line_Length
+ 1, "line too long");
350 if Reject_Trailing_Spaces
351 and then Line
'Length > 0
352 and then Line
(Line
'Last) = ' '
354 Error
(Input
.all, Line
'Last, "trailing space character");
357 return Trim
(Line
, Right
);
365 procedure Put_Line
(F
: Sfile
; S
: String) is
367 String'Write (Stream
(F
), S
);
368 Character'Write (Stream
(F
), ASCII
.LF
);
380 Error
(Input
, 0, Message
);
385 At_Character
: Natural;
388 Line_Image
: constant String := Integer'Image (Input
.Line
);
389 At_Character_Image
: constant String := Integer'Image (At_Character
);
390 -- These variables are required because we have to drop the leading
394 Number_Of_Errors
:= Number_Of_Errors
+ 1;
396 if At_Character
> 0 then
397 Put_Line
(Standard_Error
,
399 & Line_Image
(Line_Image
'First + 1 .. Line_Image
'Last) & ':'
400 & At_Character_Image
(At_Character_Image
'First + 1
401 .. At_Character_Image
'Last)
405 Put_Line
(Standard_Error
,
407 & Line_Image
(Line_Image
'First + 1 .. Line_Image
'Last)
422 if Warnings_Enabled
then
423 Warning
(Input
, 0, Message
);
429 At_Character
: Natural;
432 Line_Image
: constant String := Integer'Image (Input
.Line
);
433 At_Character_Image
: constant String := Integer'Image (At_Character
);
434 -- These variables are required because we have to drop the leading
438 if not Warnings_Enabled
then
442 Number_Of_Warnings
:= Number_Of_Warnings
+ 1;
444 if At_Character
> 0 then
445 Put_Line
(Standard_Error
,
447 & Line_Image
(Line_Image
'First + 1 .. Line_Image
'Last) & ':'
448 & At_Character_Image
(At_Character_Image
'First + 1
449 .. At_Character_Image
'Last)
453 Put_Line
(Standard_Error
,
455 & Line_Image
(Line_Image
'First + 1 .. Line_Image
'Last)
461 --------------------------
462 -- Read_Dictionary_File --
463 --------------------------
465 procedure Read_Dictionary_File
is
467 while not End_Of_File
(Dictionary_File
.Data
) loop
469 Line
: constant String :=
470 Get_Line
(Dictionary_File
'Access);
471 Split
: constant Natural :=
472 Index
(Line
, (1 => VMS_Escape_Character
));
475 if Line
'Length = 0 then
476 Error
(Dictionary_File
, "empty line in dictionary file");
478 elsif Line
(Line
'First) = ' ' then
479 Error
(Dictionary_File
, 1, "line starts with space character");
482 Error
(Dictionary_File
, "line does not contain "
483 & VMS_Escape_Character
& " character");
486 Source
: constant String :=
487 Trim
(Line
(1 .. Split
- 1), Both
);
488 Target
: constant String :=
489 Trim
(Line
(Split
+ 1 .. Line
'Last), Both
);
490 Two_Spaces
: constant Natural :=
492 Non_Word_Character
: constant Natural :=
499 if Two_Spaces
/= 0 then
500 Error
(Dictionary_File
, Two_Spaces
,
501 "multiple space characters in source word");
504 if Non_Word_Character
/= 0 then
505 Error
(Dictionary_File
, Non_Word_Character
,
506 "illegal character in source word");
509 if Source
'Length = 0 then
510 Error
(Dictionary_File
, "source is empty");
512 elsif Target
'Length = 0 then
513 Error
(Dictionary_File
, "target is empty");
516 Set
(Ug_Words
, Source
, V
(Target
));
518 -- Ensure that if Source is a sequence of words
519 -- "WORD1 WORD2 ...", we already have a mapping for
522 for J
in Source
'Range loop
523 if Source
(J
) = ' ' then
525 Prefix
: String renames
526 Source
(Source
'First .. J
- 1);
529 if not Is_Known_Word
(Prefix
) then
530 Error
(Dictionary_File
,
532 & "' not known at this point");
542 end Read_Dictionary_File
;
544 -------------------------
545 -- Rewrite_Source_Line --
546 -------------------------
548 function Rewrite_Source_Line
(Line
: String) return String is
550 -- We use a simple lexer to split the line into tokens:
552 -- Word consisting entirely of Word_Characters
553 -- VMS_Alternative ^alpha^beta^ replacement (but not ^^^)
554 -- Space a space character
555 -- Other everything else (sequence of non-word characters)
556 -- VMS_Error incomplete VMS alternative
557 -- End_Of_Line no more characters on this line
559 -- A sequence of three VMS_Escape_Characters is automatically
560 -- collapsed to an Other token.
562 type Token_Span
is record
563 First
, Last
: Positive;
565 -- The character range covered by a token in Line
567 type Token_Kind
is (End_Of_Line
, Word
, Other
,
568 VMS_Alternative
, VMS_Error
);
569 type Token_Record
(Kind
: Token_Kind
:= End_Of_Line
) is record
574 when VMS_Alternative
=>
575 Non_VMS
, VMS
: Token_Span
;
576 when VMS_Error | End_Of_Line
=>
581 Input_Position
: Positive := Line
'First;
582 Token
: Token_Record
;
583 -- The position of the next character to be processed by Next_Token
585 procedure Next_Token
;
586 -- Returns the next token in Line, starting at Input_Position
588 Rewritten_Line
: VString
;
589 -- Collects the line as it is rewritten
591 procedure Rewrite_Word
;
592 -- The current token is assumed to be a Word. When processing the VMS
593 -- version of the manual, additional tokens are gathered to check if
594 -- we have a file name or a sequence of known words.
596 procedure Maybe_Rewrite_Extension
;
597 -- The current token is assumed to be Other. When processing the VMS
598 -- version of the manual and the token represents a single dot ".",
599 -- the following word is rewritten according to the rules for
602 VMS_Token_Seen
: Boolean := False;
603 -- This is set to true if a VMS_Alternative has been encountered, or a
610 procedure Next_Token
is
611 Remaining_Line
: String renames Line
(Input_Position
.. Line
'Last);
612 Last_Character
: Natural;
615 if Remaining_Line
'Length = 0 then
616 Token
:= (End_Of_Line
, Remaining_Line
'First);
620 -- ^alpha^beta^, the VMS_Alternative case
622 if Remaining_Line
(Remaining_Line
'First) = VMS_Escape_Character
then
624 VMS_Second_Character
, VMS_Third_Character
: Natural;
627 if VMS_Token_Seen
then
628 Error
(Source_File
, Remaining_Line
'First,
629 "multiple " & VMS_Escape_Character
630 & " characters on a single line");
632 VMS_Token_Seen
:= True;
635 -- Find the second and third escape character. If one of
636 -- them is not present, generate an error token.
638 VMS_Second_Character
:=
639 Index
(Remaining_Line
(Remaining_Line
'First + 1
640 .. Remaining_Line
'Last),
641 (1 => VMS_Escape_Character
));
643 if VMS_Second_Character
= 0 then
644 Input_Position
:= Remaining_Line
'Last + 1;
645 Token
:= (VMS_Error
, Remaining_Line
'First);
649 VMS_Third_Character
:=
650 Index
(Remaining_Line
(VMS_Second_Character
+ 1
651 .. Remaining_Line
'Last),
652 (1 => VMS_Escape_Character
));
654 if VMS_Third_Character
= 0 then
655 Input_Position
:= Remaining_Line
'Last + 1;
656 Token
:= (VMS_Error
, Remaining_Line
'First);
660 -- Consume all the characters we are about to include in
663 Input_Position
:= VMS_Third_Character
+ 1;
665 -- Check if we are in a ^^^ situation, and return an Other
666 -- token in this case.
668 if Remaining_Line
'First + 1 = VMS_Second_Character
669 and then Remaining_Line
'First + 2 = VMS_Third_Character
671 Token
:= (Other
, Remaining_Line
'First,
672 (Remaining_Line
'First, Remaining_Line
'First));
676 Token
:= (VMS_Alternative
, Remaining_Line
'First,
677 (Remaining_Line
'First + 1, VMS_Second_Character
- 1),
678 (VMS_Second_Character
+ 1, VMS_Third_Character
- 1));
681 end if; -- VMS_Alternative
683 -- The Word case. Search for characters not in Word_Characters.
684 -- We have found a word if the first non-word character is not
685 -- the first character in Remaining_Line, i.e. if Remaining_Line
686 -- starts with a word character.
688 Last_Character
:= Index
(Remaining_Line
, Word_Characters
, Outside
);
689 if Last_Character
/= Remaining_Line
'First then
691 -- If we haven't found a character which is not in
692 -- Word_Characters, all remaining characters are part of the
693 -- current Word token.
695 if Last_Character
= 0 then
696 Last_Character
:= Remaining_Line
'Last + 1;
699 Input_Position
:= Last_Character
;
700 Token
:= (Word
, Remaining_Line
'First,
701 (Remaining_Line
'First, Last_Character
- 1));
705 -- Remaining characters are in the Other category. To speed
706 -- up processing, we collect them together if there are several
709 Input_Position
:= Last_Character
+ 1;
711 Remaining_Line
'First,
712 (Remaining_Line
'First, Last_Character
));
719 procedure Rewrite_Word
is
721 renames Line
(Token
.Span
.First
.. Token
.Span
.Last
);
724 -- We do not perform any error checking below, so we can just skip
725 -- all processing for the non-VMS version.
727 if Target
/= VMS
then
728 Append
(Rewritten_Line
, First_Word
);
733 if Is_Known_Word
(First_Word
) then
735 -- If we have a word from the dictionary, we look for the
736 -- longest possible sequence we can rewrite.
739 Seq
: Token_Span
:= Token
.Span
;
740 Lost_Space
: Boolean := False;
745 if Token
.Kind
= Other
746 and then Line
(Token
.Span
.First
.. Token
.Span
.Last
) = " "
749 if Token
.Kind
/= Word
750 or else not Is_Known_Word
(Line
(Seq
.First
753 -- When we reach this point, the following
754 -- conditions are true:
756 -- Seq is a known word.
757 -- The previous token was a space character.
758 -- Seq extended to the current token is not a
766 -- Extend Seq to cover the current (known) word
768 Seq
.Last
:= Token
.Span
.Last
;
773 -- When we reach this point, the following conditions
776 -- Seq is a known word.
777 -- The previous token was a word.
778 -- The current token is not a space character.
784 -- Rewrite Seq, and add the lost space if necessary
786 Append
(Rewritten_Line
,
787 Get_Replacement_Word
(Line
(Seq
.First
.. Seq
.Last
)));
789 Append
(Rewritten_Line
, ' ');
792 -- The unknown token will be processed during the
793 -- next iteration of the main loop.
800 if Token
.Kind
= Other
801 and then Line
(Token
.Span
.First
.. Token
.Span
.Last
) = "."
803 -- Deal with extensions
807 and then Is_Extension
(Line
(Token
.Span
.First
810 -- We have discovered a file extension. Convert the file
811 -- name to upper case.
813 Append
(Rewritten_Line
,
814 Translate
(First_Word
, Upper_Case_Map
) & '.');
815 Append
(Rewritten_Line
,
816 Get_Replacement_Extension
817 (Line
(Token
.Span
.First
.. Token
.Span
.Last
)));
820 -- We already have: Word ".", followed by an unknown token
822 Append
(Rewritten_Line
, First_Word
& '.');
824 -- The unknown token will be processed during the next
825 -- iteration of the main loop.
829 -- We have an unknown Word, followed by an unknown token.
830 -- The unknown token will be processed by the outer loop.
832 Append
(Rewritten_Line
, First_Word
);
836 -----------------------------
837 -- Maybe_Rewrite_Extension --
838 -----------------------------
840 procedure Maybe_Rewrite_Extension
is
842 -- Again, we need no special processing in the non-VMS case
845 and then Line
(Token
.Span
.First
.. Token
.Span
.Last
) = "."
847 -- This extension is not preceded by a word, otherwise
848 -- Rewrite_Word would have handled it.
852 and then Is_Extension
(Line
(Token
.Span
.First
855 Append
(Rewritten_Line
, '.' & Get_Replacement_Extension
856 (Line
(Token
.Span
.First
.. Token
.Span
.Last
)));
859 Append
(Rewritten_Line
, '.');
862 Append
(Rewritten_Line
, Line
(Token
.Span
.First
863 .. Token
.Span
.Last
));
866 end Maybe_Rewrite_Extension
;
868 -- Start of processing for Process_Source_Line
871 -- The following parser recognizes the following special token
874 -- Word "." Word rewrite as file name if second word is extension
875 -- Word " " Word rewrite as a single word using Ug_Words table
887 Maybe_Rewrite_Extension
;
889 when VMS_Alternative
=>
890 if VMS_Context_Determined
then
891 if (not In_VMS_Section
)
893 Line
(Token
.VMS
.First
.. Token
.VMS
.Last
) /=
894 Line
(Token
.Non_VMS
.First
.. Token
.Non_VMS
.Last
)
896 Warning
(Source_File
, Token
.First
,
897 "VMS alternative already determined "
898 & "by conditionals");
902 Append
(Rewritten_Line
, Line
(Token
.VMS
.First
905 Append
(Rewritten_Line
, Line
(Token
.Non_VMS
.First
906 .. Token
.Non_VMS
.Last
));
911 Error
(Source_File
, Token
.First
, "invalid VMS alternative");
916 return S
(Rewritten_Line
);
917 end Rewrite_Source_Line
;
919 -------------------------
920 -- Process_Source_File --
921 -------------------------
923 procedure Process_Source_File
is
924 Ifset
: constant String := "@ifset ";
925 Ifclear
: constant String := "@ifclear ";
926 Endsetclear
: constant String := "@end ";
927 -- Strings to be recognized for conditional processing
930 while not End_Of_File
(Source_File
.Data
) loop
932 Line
: constant String := Get_Line
(Source_File
'Access);
933 Rewritten
: constant String := Rewrite_Source_Line
(Line
);
934 -- We unconditionally rewrite the line so that we can check the
935 -- syntax of all lines, and not only those which are actually
936 -- included in the output.
938 Have_Conditional
: Boolean := False;
939 -- True if we have encountered a conditional preprocessing
943 -- The kind of the directive
949 -- If the line starts with @ifset or @ifclear, we try to convert
950 -- the following flag to one of our flag types. If we fail,
951 -- Have_Conditional remains False.
953 if Line
'Length >= Ifset
'Length
954 and then Line
(1 .. Ifset
'Length) = Ifset
959 Arg
: constant String :=
960 Trim
(Line
(Ifset
'Length + 1 .. Line
'Last), Both
);
963 Flag
:= Flag_Type
'Value (Arg
);
964 Have_Conditional
:= True;
968 if Translate
(Target_Type
'Image (Flag
),
972 Error
(Source_File
, "flag has to be lowercase");
979 when Constraint_Error
=>
980 Error
(Source_File
, "unknown flag for '@ifset'");
983 elsif Line
'Length >= Ifclear
'Length
984 and then Line
(1 .. Ifclear
'Length) = Ifclear
989 Arg
: constant String :=
990 Trim
(Line
(Ifclear
'Length + 1 .. Line
'Last), Both
);
993 Flag
:= Flag_Type
'Value (Arg
);
994 Have_Conditional
:= True;
998 if Translate
(Target_Type
'Image (Flag
),
1002 Error
(Source_File
, "flag has to be lowercase");
1005 when Edition_Type
=>
1009 when Constraint_Error
=>
1010 Error
(Source_File
, "unknown flag for '@ifclear'");
1014 if Have_Conditional
and (Flag
in Target_Type
) then
1016 -- We create a new conditional context and suppress the
1017 -- directive in the output.
1019 Push_Conditional
(Cond
, Flag
);
1021 elsif Line
'Length >= Endsetclear
'Length
1022 and then Line
(1 .. Endsetclear
'Length) = Endsetclear
1023 and then (Flag
in Target_Type
)
1025 -- The '@end ifset'/'@end ifclear' case is handled here. We
1026 -- have to pop the conditional context.
1029 First
, Last
: Natural;
1032 Find_Token
(Source
=> Line
(Endsetclear
'Length + 1
1040 Error
(Source_File
, "'@end' without argument");
1042 if Line
(First
.. Last
) = "ifset" then
1043 Have_Conditional
:= True;
1045 elsif Line
(First
.. Last
) = "ifclear" then
1046 Have_Conditional
:= True;
1050 if Have_Conditional
then
1051 Pop_Conditional
(Cond
);
1054 -- We fall through to the ordinary case for other @end
1057 end if; -- @end without argument
1059 end if; -- Have_Conditional
1061 if (not Have_Conditional
) or (Flag
in Edition_Type
) then
1063 -- The ordinary case
1065 if not Currently_Excluding
then
1066 Put_Line
(Output_File
, Rewritten
);
1072 Check_No_Pending_Conditional
;
1073 end Process_Source_File
;
1075 ---------------------------
1076 -- Initialize_Extensions --
1077 ---------------------------
1079 procedure Initialize_Extensions
is
1081 procedure Add
(Extension
: String);
1082 -- Adds an extension which is replaced with itself (in upper
1085 procedure Add
(Extension
, Replacement
: String);
1086 -- Adds an extension with a custom replacement
1092 procedure Add
(Extension
: String) is
1094 Add
(Extension
, Translate
(Extension
, Upper_Case_Map
));
1097 procedure Add
(Extension
, Replacement
: String) is
1099 Set
(Extensions
, Extension
, V
(Replacement
));
1102 -- Start of processing for Initialize_Extensions
1105 -- To avoid performance degradation, increase the constant in the
1106 -- definition of Extensions above if you add more extensions here.
1117 end Initialize_Extensions
;
1123 function Is_Extension
(Extension
: String) return Boolean is
1125 return Present
(Extensions
, Extension
);
1128 -------------------------------
1129 -- Get_Replacement_Extension --
1130 -------------------------------
1132 function Get_Replacement_Extension
(Extension
: String) return String is
1134 return S
(Get
(Extensions
, Extension
));
1135 end Get_Replacement_Extension
;
1141 function Is_Known_Word
(Word
: String) return Boolean is
1143 return Present
(Ug_Words
, Word
);
1146 --------------------------
1147 -- Get_Replacement_Word --
1148 --------------------------
1150 function Get_Replacement_Word
(Word
: String) return String is
1152 return S
(Get
(Ug_Words
, Word
));
1153 end Get_Replacement_Word
;
1155 ----------------------
1156 -- Push_Conditional --
1157 ----------------------
1159 procedure Push_Conditional
(Cond
: Conditional
; Flag
: Target_Type
) is
1160 Will_Exclude
: Boolean;
1163 -- If we are already in an excluding context, inherit this property,
1164 -- otherwise calculate it from scratch.
1166 if Conditional_TOS
> 0
1167 and then Conditional_Stack
(Conditional_TOS
).Excluding
1169 Will_Exclude
:= True;
1173 Will_Exclude
:= Flag
/= Target
;
1175 Will_Exclude
:= Flag
= Target
;
1179 -- Check if the current directive is pointless because of a previous,
1180 -- enclosing directive.
1182 for J
in 1 .. Conditional_TOS
loop
1183 if Conditional_Stack
(J
).Flag
= Flag
then
1184 Warning
(Source_File
, "directive without effect because of line"
1185 & Integer'Image (Conditional_Stack
(J
).Starting_Line
));
1189 Conditional_TOS
:= Conditional_TOS
+ 1;
1190 Conditional_Stack
(Conditional_TOS
) :=
1191 (Starting_Line
=> Source_File
.Line
,
1194 Excluding
=> Will_Exclude
);
1195 end Push_Conditional
;
1197 ---------------------
1198 -- Pop_Conditional --
1199 ---------------------
1201 procedure Pop_Conditional
(Cond
: Conditional
) is
1203 if Conditional_TOS
> 0 then
1206 if Conditional_Stack
(Conditional_TOS
).Cond
/= Set
then
1208 "'@end ifset' does not match '@ifclear' at line"
1209 & Integer'Image (Conditional_Stack
1210 (Conditional_TOS
).Starting_Line
));
1214 if Conditional_Stack
(Conditional_TOS
).Cond
/= Clear
then
1216 "'@end ifclear' does not match '@ifset' at line"
1217 & Integer'Image (Conditional_Stack
1218 (Conditional_TOS
).Starting_Line
));
1222 Conditional_TOS
:= Conditional_TOS
- 1;
1228 "'@end ifset' without corresponding '@ifset'");
1232 "'@end ifclear' without corresponding '@ifclear'");
1235 end Pop_Conditional
;
1237 -------------------------
1238 -- Currently_Excluding --
1239 -------------------------
1241 function Currently_Excluding
return Boolean is
1243 return Conditional_TOS
> 0
1244 and then Conditional_Stack
(Conditional_TOS
).Excluding
;
1245 end Currently_Excluding
;
1247 ----------------------------
1248 -- VMS_Context_Determined --
1249 ----------------------------
1251 function VMS_Context_Determined
return Boolean is
1253 for J
in 1 .. Conditional_TOS
loop
1254 if Conditional_Stack
(J
).Flag
= VMS
then
1260 end VMS_Context_Determined
;
1262 --------------------
1263 -- In_VMS_Section --
1264 --------------------
1266 function In_VMS_Section
return Boolean is
1268 for J
in 1 .. Conditional_TOS
loop
1269 if Conditional_Stack
(J
).Flag
= VMS
then
1270 return Conditional_Stack
(J
).Cond
= Set
;
1277 ----------------------------------
1278 -- Check_No_Pending_Conditional --
1279 ----------------------------------
1281 procedure Check_No_Pending_Conditional
is
1283 for J
in 1 .. Conditional_TOS
loop
1284 case Conditional_Stack
(J
).Cond
is
1286 Error
(Source_File
, "Missing '@end ifset' for '@ifset' at line"
1287 & Integer'Image (Conditional_Stack
(J
).Starting_Line
));
1291 "Missing '@end ifclear' for '@ifclear' at line"
1292 & Integer'Image (Conditional_Stack
(J
).Starting_Line
));
1295 end Check_No_Pending_Conditional
;
1297 -- Start of processing for Xgnatugn
1299 Valid_Command_Line
: Boolean;
1300 Output_File_Name
: VString
;
1303 Initialize_Extensions
;
1304 Valid_Command_Line
:= Argument_Count
in 3 .. 5;
1306 -- First argument: Target
1308 if Valid_Command_Line
then
1310 Target
:= Flag_Type
'Value (Argument
(1));
1312 if not Target
'Valid then
1313 Valid_Command_Line
:= False;
1317 when Constraint_Error
=>
1318 Valid_Command_Line
:= False;
1322 -- Second argument: Source_File
1324 if Valid_Command_Line
then
1326 Source_File
.Name
:= V
(Argument
(2));
1327 Open
(Source_File
.Data
, In_File
, Argument
(2));
1330 when Ada
.Text_IO
.Name_Error
=>
1331 Valid_Command_Line
:= False;
1335 -- Third argument: Dictionary_File
1337 if Valid_Command_Line
then
1339 Dictionary_File
.Name
:= V
(Argument
(3));
1340 Open
(Dictionary_File
.Data
, In_File
, Argument
(3));
1343 when Ada
.Text_IO
.Name_Error
=>
1344 Valid_Command_Line
:= False;
1348 -- Fourth argument: Output_File
1350 if Valid_Command_Line
then
1351 if Argument_Count
in 4 .. 5 then
1352 Output_File_Name
:= V
(Argument
(4));
1356 Output_File_Name
:= V
("gnat_ugn_unw.texi");
1358 Output_File_Name
:= V
("gnat_ugn_vms.texi");
1362 Warnings_Enabled
:= Argument_Count
= 5;
1365 Create
(Output_File
, Out_File
, S
(Output_File_Name
));
1368 when Ada
.Text_IO
.Name_Error | Ada
.Text_IO
.Use_Error
=>
1369 Valid_Command_Line
:= False;
1373 if not Valid_Command_Line
then
1375 Set_Exit_Status
(Failure
);
1378 Read_Dictionary_File
;
1379 Close
(Dictionary_File
.Data
);
1381 -- Main processing starts here
1383 Process_Source_File
;
1384 Close
(Output_File
);
1385 Close
(Source_File
.Data
);
1387 New_Line
(Standard_Error
);
1389 if Number_Of_Warnings
= 0 then
1390 Put_Line
(Standard_Error
, " NO Warnings");
1393 Put
(Standard_Error
, Integer'Image (Number_Of_Warnings
));
1394 Put
(Standard_Error
, " Warning");
1396 if Number_Of_Warnings
> 1 then
1397 Put
(Standard_Error
, "s");
1400 New_Line
(Standard_Error
);
1403 if Number_Of_Errors
= 0 then
1404 Put_Line
(Standard_Error
, " NO Errors");
1407 Put
(Standard_Error
, Integer'Image (Number_Of_Errors
));
1408 Put
(Standard_Error
, " Error");
1410 if Number_Of_Errors
> 1 then
1411 Put
(Standard_Error
, "s");
1414 New_Line
(Standard_Error
);
1417 if Number_Of_Errors
/= 0 then
1418 Set_Exit_Status
(Failure
);
1420 Set_Exit_Status
(Success
);