1 ------------------------------------------------------------------------------
3 -- GNAT SYSTEM UTILITIES --
9 -- Copyright (C) 2002 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 2, or (at your option) any later ver- --
14 -- sion. GNAT is distributed in the hope that it will be useful, but WITH- --
15 -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY --
16 -- or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License --
17 -- for more details. You should have received a copy of the GNU General --
18 -- Public License distributed with GNAT; see file COPYING. If not, write --
19 -- to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, --
20 -- MA 02111-1307, USA. --
22 ------------------------------------------------------------------------------
24 -- This utility is used to process the source of gnat_ug.texi to make a
25 -- version suitable for running through standard Texinfo processor. It takes
26 -- three arguments. The first one is the target type of the manual, which
31 -- wnt Mirosoft Windows
32 -- vxworks Embedded Platforms
34 -- The second parameter is the file name of the Texinfo file to be
37 -- The third parameter is the name of the word list. 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 words 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 listed in the first column, separated by a single
44 -- space character. If multiple words are to be replaced, there has to be
45 -- replacement for each prefix.)
47 -- The fourth parameter is the name of the output file. It defaults to
48 -- gnat_ug_unx.texi, gnat_ug_vms.texi, gnat_ug_wnt.texi or gnat_ug_vxw.texi,
49 -- depending on the target.
51 -- The following steps are performed:
55 -- Any occurrences of ^alpha^beta^ are replaced by beta. The sequence
56 -- must fit on a single line, and there can only be one occurrence on a
59 -- Any occurrences of a word in the Ug_Words list are replaced by the
60 -- appropriate vms equivalents. Note that replacements do not occur
61 -- within ^alpha^beta^ sequences.
63 -- Any occurence of [filename].extension, where extension one of the
66 -- "o", "ads", "adb", "ali", "ada", "atb", "ats", "adc", "c"
69 -- replaced by the appropriate VMS names (all upper case with .o
70 -- replaced .OBJ). Note that replacements do not occur within
71 -- ^alpha^beta^ sequences.
73 -- In UNX, VXWORKS or WNT mode
75 -- Any occurrences of ^alpha^beta^ are replaced by alpha. The sequence
76 -- must fit on a single line.
80 -- The sequence ^^^ is replaced by a single ^. This escape sequence
81 -- must be used if the literal character ^ is to appear in the
82 -- output. A line containing this escape sequence may not also contain
83 -- a ^alpha^beta^ sequence.
85 -- Recognize @ifset and @ifclear (this is because we have menu problems
86 -- if we let makeinfo handle the ifset/ifclear pairs
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
.Text_IO
; use Ada
.Text_IO
;
95 with GNAT
.Spitbol
; use GNAT
.Spitbol
;
96 with GNAT
.Spitbol
.Table_VString
; use GNAT
.Spitbol
.Table_VString
;
101 -- Print usage information. Invoked if an invalid command line is
104 Output_File
: File_Type
;
105 -- The preprocessed output is written to this file.
107 type Input_File
is record
112 -- Records information on an input file. Name and Line are used
113 -- in error messages, Line is updated automatically by Get_Line.
115 function Get_Line
(Input
: access Input_File
) return String;
116 -- Returns a line from Input and performs the necessary
117 -- line-oriented checks (length, character set, trailing spaces).
119 Have_Errors
: Boolean := False;
122 At_Character
: Natural;
127 -- Prints a message reporting an error on line Input.Line. If
128 -- At_Character is not 0, indicate the exact character at which
133 At_Character
: Natural;
138 -- Like Error, but just print a warning message.
140 Dictionary_File
: aliased Input_File
;
141 procedure Read_Dictionary_File
;
142 -- Dictionary_File is opened using the name given on the command
143 -- line. It contains the replacements for the Ug_Words list.
144 -- Read_Dictionary_File reads Dictionary_File and fills the
147 Source_File
: aliased Input_File
;
148 procedure Process_Source_File
;
149 -- Source_File is opened using the name given on the command line.
150 -- It contains the Texinfo source code. Process_Source_File
151 -- performs the necessary replacements.
153 type Target_Type
is (VMS
, WNT
, UNX
, VXWORKS
);
154 Target
: Target_Type
;
155 -- The target for which preprocessing is performed: VMS, Windows,
156 -- GNU, and embedded platforms ("UNX" and "VXWORKS" are misnomers).
157 -- The Target avariable is initialized using the command line.
159 Valid_Characters
: constant Character_Set
160 := To_Set
(Span
=> (' ', '~'));
161 -- This array controls which characters are permitted in the input
162 -- file (after line breaks have been removed). Valid characters
163 -- are all printable ASCII characters and the space character.
165 Word_Characters
: constant Character_Set
166 := (To_Set
(Ranges
=> (('0', '9'), ('a', 'z'), ('A', 'Z')))
168 -- The characters which are permitted in words. Other (valid)
169 -- characters are assumed to be delimiters between words. Note that
170 -- this set has to include all characters of the source words of the
171 -- Ug_Words dictionary.
173 Reject_Trailing_Spaces
: constant Boolean := True;
174 -- Controls whether Xgnatug rejects superfluous space characters
175 -- at the end of lines.
177 Maximum_Line_Length
: constant Positive := 2000;
178 Fatal_Line_Length_Limit
: constant Positive := 5000;
179 Fatal_Line_Length
: exception;
180 -- If Maximum_Line_Length is exceeded in an input file, an error
181 -- message is printed. If Fatal_Line_Length is exceeded,
182 -- execution terminates with a Fatal_Line_Length exception.
184 VMS_Escape_Character
: constant Character := '^';
185 -- The character used to mark VMS alternatives (^alpha^beta^).
187 Extensions
: GNAT
.Spitbol
.Table_VString
.Table
(20);
188 procedure Initialize_Extensions
;
189 -- This table records extensions and their replacement for
190 -- rewriting filenames in the VMS version of the manual.
192 function Is_Extension
(Extension
: String) return Boolean;
193 function Get_Replacement_Extension
(Extension
: String) return String;
194 -- These functions query the replacement table. Is_Extension
195 -- checks if the given string is a known extension.
196 -- Get_Replacement returns the replacement extension.
198 Ug_Words
: GNAT
.Spitbol
.Table_VString
.Table
(200);
199 function Is_Known_Word
(Word
: String) return Boolean;
200 function Get_Replacement_Word
(Word
: String) return String;
201 -- The Ug_Words table lists replacement words for the VMS version
202 -- of the manual. Is_Known_Word and Get_Replacement_Word query
203 -- this table. The table is filled using Read_Dictionary_File.
205 function Rewrite_Source_Line
(Line
: String) return String;
206 -- This subprogram takes a line and rewrites it according to Target.
207 -- It relies on information in Source_File to generate error messages.
209 type Conditional
is (Set
, Clear
);
210 procedure Push_Conditional
(Cond
: Conditional
; Flag
: Target_Type
);
211 procedure Pop_Conditional
(Cond
: Conditional
);
212 -- These subprograms deal with conditional processing (@ifset/@ifclear).
213 -- They rely on information in Source_File to generate error messages.
215 function Currently_Excluding
return Boolean;
216 -- Returns true if conditional processing directives imply that the
217 -- current line should not be included in the output.
219 function VMS_Context_Determined
return Boolean;
220 -- Returns true if, in the current conditional preprocessing context, we
221 -- always have a VMS or a non-VMS version, regardless of the value of
224 procedure Check_No_Pending_Conditional
;
225 -- Checks that all preprocessing directives have been properly matched by
226 -- their @end counterpart. If this is not the case, print an error
229 -- The following definitions implement a stack to track the conditional
230 -- preprocessing context.
232 type Conditional_Context
is record
233 Starting_Line
: Positive;
239 Conditional_Stack_Depth
: constant := 3;
240 Conditional_Stack
: array (1 .. Conditional_Stack_Depth
)
241 of Conditional_Context
;
242 Conditional_TOS
: Natural := 0;
243 -- Pointer to the Top Of Stack for Conditional_Stack.
245 -----------------------------------
246 -- Implementation of Subprograms --
247 -----------------------------------
255 Put_Line
(Standard_Error
,
256 "usage: xgnatug TARGET SOURCE DICTIONARY [OUTFILE]");
258 Put_Line
(Standard_Error
, "TARGET is one of:");
259 for T
in Target_Type
'Range loop
260 Put_Line
(Standard_Error
, " " & Target_Type
'Image (T
));
263 Put_Line
(Standard_Error
, "SOURCE is the source file to process.");
265 Put_Line
(Standard_Error
, "DICTIONARY is the name of a file "
266 & "that contains word replacements");
267 Put_Line
(Standard_Error
, "for the VMS version.");
269 Put_Line
(Standard_Error
,
270 "OUT-FILE, if present, is the output file to be created;");
271 Put_Line
(Standard_Error
,
272 "If OUT-FILE is absent, the output file is one of " &
273 "gnat_ug_unx.texi, ");
274 Put_Line
(Standard_Error
,
275 "gnat_ug_vms.texi, gnat_ug_wnt.texi or gnat_ug_vxw.texi, " &
276 "depending on TARGET.");
283 function Get_Line
(Input
: access Input_File
) return String is
284 Line_Buffer
: String (1 .. Fatal_Line_Length_Limit
);
288 Input
.Line
:= Input
.Line
+ 1;
289 Get_Line
(Input
.Data
, Line_Buffer
, Last
);
290 if Last
= Line_Buffer
'Last then
291 Error
(Input
.all, "line exceeds fatal line length limit");
292 raise Fatal_Line_Length
;
296 Line
: String renames Line_Buffer
(Line_Buffer
'First .. Last
);
299 for J
in Line
'Range loop
300 if not Is_In
(Line
(J
), Valid_Characters
) then
301 Error
(Input
.all, J
, "invalid character");
306 if Line
'Length > Maximum_Line_Length
then
307 Warning
(Input
.all, Maximum_Line_Length
+ 1, "line too long");
310 if Reject_Trailing_Spaces
311 and then Line
'Length > 0
312 and then Line
(Line
'Last) = ' '
314 Error
(Input
.all, Line
'Last, "trailing space character");
317 return Trim
(Line
, Right
);
330 Error
(Input
, 0, Message
);
335 At_Character
: Natural;
338 Line_Image
: constant String := Integer'Image (Input
.Line
);
339 At_Character_Image
: constant String := Integer'Image (At_Character
);
340 -- These variables are required because we have to drop the leading
345 if At_Character
> 0 then
346 Put_Line
(Standard_Error
,
348 & Line_Image
(Line_Image
'First + 1 .. Line_Image
'Last) & ':'
349 & At_Character_Image
(At_Character_Image
'First + 1
350 .. At_Character_Image
'Last)
354 Put_Line
(Standard_Error
,
356 & Line_Image
(Line_Image
'First + 1 .. Line_Image
'Last)
371 Warning
(Input
, 0, Message
);
376 At_Character
: Natural;
379 Line_Image
: constant String := Integer'Image (Input
.Line
);
380 At_Character_Image
: constant String := Integer'Image (At_Character
);
381 -- These variables are required because we have to drop the leading
385 if At_Character
> 0 then
386 Put_Line
(Standard_Error
,
388 & Line_Image
(Line_Image
'First + 1 .. Line_Image
'Last) & ':'
389 & At_Character_Image
(At_Character_Image
'First + 1
390 .. At_Character_Image
'Last)
394 Put_Line
(Standard_Error
,
396 & Line_Image
(Line_Image
'First + 1 .. Line_Image
'Last)
402 --------------------------
403 -- Read_Dictionary_File --
404 --------------------------
406 procedure Read_Dictionary_File
is
408 while not End_Of_File
(Dictionary_File
.Data
) loop
410 Line
: String := Get_Line
(Dictionary_File
'Access);
411 Split
: Natural := Index
(Line
, (1 => VMS_Escape_Character
));
414 if Line
'Length = 0 then
415 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 Error
(Dictionary_File
, "line does not contain "
420 & VMS_Escape_Character
& " character");
423 Source
: constant String
424 := Trim
(Line
(1 .. Split
- 1), Both
);
425 Target
: constant String
426 := Trim
(Line
(Split
+ 1 .. Line
'Last), Both
);
427 Two_Spaces
: constant Natural
428 := Index
(Source
, " ");
429 Non_Word_Character
: constant Natural
430 := Index
(Source
, Word_Characters
or To_Set
(" "),
434 if Two_Spaces
/= 0 then
435 Error
(Dictionary_File
, Two_Spaces
,
436 "multiple space characters in source word");
439 if Non_Word_Character
/= 0 then
440 Error
(Dictionary_File
, Non_Word_Character
,
441 "illegal character in source word");
444 if Source
'Length = 0 then
445 Error
(Dictionary_File
, "source is empty");
446 elsif Target
'Length = 0 then
447 Error
(Dictionary_File
, "target is empty");
449 Set
(Ug_Words
, Source
, V
(Target
));
451 -- Ensure that if Source is a sequence of words
452 -- "WORD1 WORD2 ...", we already have a mapping for
455 for J
in Source
'Range loop
456 if Source
(J
) = ' ' then
458 Prefix
: String renames Source
(Source
'First
462 if not Is_Known_Word
(Prefix
) then
463 Error
(Dictionary_File
,
465 & "' not known at this point");
475 end Read_Dictionary_File
;
477 -------------------------
478 -- Process_Source_Line --
479 -------------------------
481 function Rewrite_Source_Line
(Line
: String) return String is
483 -- We use a simple lexer to split the line into tokens:
485 -- Word consisting entirely of Word_Characters
486 -- VMS_Alternative ^alpha^beta^ replacement (but not ^^^)
487 -- Space a space character
488 -- Other everything else (sequence of non-word characters)
489 -- VMS_Error incomplete VMS alternative
490 -- End_Of_Line no more characters on this line
492 -- A sequence of three VMS_Escape_Characters is automatically
493 -- collapsed to an Other token.
495 type Token_Span
is record
496 First
, Last
: Positive;
498 -- The character range covered by a token in Line.
500 type Token_Kind
is (End_Of_Line
, Word
, Other
,
501 VMS_Alternative
, VMS_Error
);
502 type Token_Record
(Kind
: Token_Kind
:= End_Of_Line
) is record
507 when VMS_Alternative
=>
508 Non_VMS
, VMS
: Token_Span
;
509 when VMS_Error | End_Of_Line
=>
514 Input_Position
: Positive := Line
'First;
515 Token
: Token_Record
;
516 -- The position of the next character to be processed by Next_Token.
518 procedure Next_Token
;
519 -- Returns the next token in Line, starting at Input_Position.
521 Rewritten_Line
: VString
;
522 -- Collects the line as it is rewritten.
524 procedure Rewrite_Word
;
525 -- The current token is assumed to be a Word. When processing the VMS
526 -- version of the manual, additional tokens are gathered to check if
527 -- we have a file name or a sequence of known words.
529 procedure Maybe_Rewrite_Extension
;
530 -- The current token is assumed to be Other. When processing the VMS
531 -- version of the manual and the token represents a single dot ".",
532 -- the following word is rewritten according to the rules for
535 VMS_Token_Seen
: Boolean := False;
536 -- This is set to true if a VMS_Alternative has been encountered, or a
539 procedure Next_Token
is
540 Remaining_Line
: String renames Line
(Input_Position
.. Line
'Last);
541 Last_Character
: Natural;
544 if Remaining_Line
'Length = 0 then
545 Token
:= (End_Of_Line
, Remaining_Line
'First);
549 -- ^alpha^beta^, the VMS_Alternative case.
551 if Remaining_Line
(Remaining_Line
'First) = VMS_Escape_Character
then
553 VMS_Second_Character
, VMS_Third_Character
: Natural;
556 if VMS_Token_Seen
then
557 Error
(Source_File
, Remaining_Line
'First,
558 "multiple " & VMS_Escape_Character
559 & " characters on a single line");
561 VMS_Token_Seen
:= True;
564 -- Find the second and third escape character. If one of
565 -- them is not present, generate an error token.
568 := Index
(Remaining_Line
(Remaining_Line
'First + 1
569 .. Remaining_Line
'Last),
570 (1 => VMS_Escape_Character
));
571 if VMS_Second_Character
= 0 then
572 Input_Position
:= Remaining_Line
'Last + 1;
573 Token
:= (VMS_Error
, Remaining_Line
'First);
578 := Index
(Remaining_Line
(VMS_Second_Character
+ 1
579 .. Remaining_Line
'Last),
580 (1 => VMS_Escape_Character
));
581 if VMS_Third_Character
= 0 then
582 Input_Position
:= Remaining_Line
'Last + 1;
583 Token
:= (VMS_Error
, Remaining_Line
'First);
587 -- Consume all the characters we are about to include in
590 Input_Position
:= VMS_Third_Character
+ 1;
592 -- Check if we are in a ^^^ situation, and return an Other
593 -- token in this case.
595 if Remaining_Line
'First + 1 = VMS_Second_Character
596 and then Remaining_Line
'First + 2 = VMS_Third_Character
598 Token
:= (Other
, Remaining_Line
'First,
599 (Remaining_Line
'First, Remaining_Line
'First));
603 Token
:= (VMS_Alternative
, Remaining_Line
'First,
604 (Remaining_Line
'First + 1, VMS_Second_Character
- 1),
605 (VMS_Second_Character
+ 1, VMS_Third_Character
- 1));
608 end if; -- VMS_Alternative
610 -- The Word case. Search for characters not in Word_Characters.
611 -- We have found a word if the first non-word character is not
612 -- the first character in Remaining_Line, i.e. if Remaining_Line
613 -- starts with a word character.
615 Last_Character
:= Index
(Remaining_Line
, Word_Characters
, Outside
);
616 if Last_Character
/= Remaining_Line
'First then
619 -- If we haven't found a character which is not in
620 -- Word_Characters, all remaining characters are part of the
621 -- current Word token.
623 if Last_Character
= 0 then
624 Last_Character
:= Remaining_Line
'Last + 1;
627 Input_Position
:= Last_Character
;
628 Token
:= (Word
, Remaining_Line
'First,
629 (Remaining_Line
'First, Last_Character
- 1));
633 -- Remaining characters are in the Other category. To speed
634 -- up processing, we collect them together if there are several
637 Input_Position
:= Last_Character
+ 1;
638 Token
:= (Other
, Remaining_Line
'First,
639 (Remaining_Line
'First, Last_Character
));
642 procedure Rewrite_Word
is
644 renames Line
(Token
.Span
.First
.. Token
.Span
.Last
);
647 -- We do not perform any error checking below, so we can just skip
648 -- all processing for the non-VMS version.
650 if Target
/= VMS
then
651 Append
(Rewritten_Line
, First_Word
);
656 if Is_Known_Word
(First_Word
) then
658 -- If we have a word from the dictionary, we look for the
659 -- longest possible sequence we can rewrite.
662 Seq
: Token_Span
:= Token
.Span
;
663 Lost_Space
: Boolean := False;
668 if Token
.Kind
= Other
669 and then Line
(Token
.Span
.First
.. Token
.Span
.Last
) = " "
672 if Token
.Kind
/= Word
673 or else not Is_Known_Word
(Line
(Seq
.First
676 -- When we reach this point, the following
677 -- conditions are true:
679 -- Seq is a known word.
680 -- The previous token was a space character.
681 -- Seq extended to the current token is not a
689 -- Extend Seq to cover the current (known) word.
691 Seq
.Last
:= Token
.Span
.Last
;
696 -- When we reach this point, the following conditions
699 -- Seq is a known word.
700 -- The previous token was a word.
701 -- The current token is not a space character.
707 -- Rewrite Seq, and add the lost space if necessary.
709 Append
(Rewritten_Line
,
710 Get_Replacement_Word
(Line
(Seq
.First
.. Seq
.Last
)));
712 Append
(Rewritten_Line
, ' ');
715 -- The unknown token will be processed during the
716 -- next iteration of the main loop.
722 if Token
.Kind
= Other
723 and then Line
(Token
.Span
.First
.. Token
.Span
.Last
) = "."
726 -- Deal with extensions.
730 and then Is_Extension
(Line
(Token
.Span
.First
733 -- We have discovered a file extension. Convert the file
734 -- name to upper case.
736 Append
(Rewritten_Line
,
737 Translate
(First_Word
, Upper_Case_Map
) & '.');
738 Append
(Rewritten_Line
,
739 Get_Replacement_Extension
740 (Line
(Token
.Span
.First
.. Token
.Span
.Last
)));
743 -- We already have: Word ".", followed by an unknown
746 Append
(Rewritten_Line
, First_Word
& '.');
748 -- The unknown token will be processed during the next
749 -- iteration of the main loop.
754 -- We have an unknown Word, followed by an unknown token.
755 -- The unknown token will be processed by the outer loop.
757 Append
(Rewritten_Line
, First_Word
);
761 procedure Maybe_Rewrite_Extension
is
763 -- Again, we need no special processing in the non-VMS case.
766 and then Line
(Token
.Span
.First
.. Token
.Span
.Last
) = "."
768 -- This extension is not preceded by a word, otherwise
769 -- Rewrite_Word would have handled it.
773 and then Is_Extension
(Line
(Token
.Span
.First
776 Append
(Rewritten_Line
, '.' & Get_Replacement_Extension
777 (Line
(Token
.Span
.First
.. Token
.Span
.Last
)));
780 Append
(Rewritten_Line
, '.');
783 Append
(Rewritten_Line
, Line
(Token
.Span
.First
784 .. Token
.Span
.Last
));
787 end Maybe_Rewrite_Extension
;
789 -- Start of processing for Process_Source_Line
792 -- The following parser recognizes the following special token
795 -- Word "." Word rewrite as file name if second word is extension
796 -- Word " " Word rewrite as a single word using Ug_Words table
808 Maybe_Rewrite_Extension
;
810 when VMS_Alternative
=>
811 if VMS_Context_Determined
then
812 Warning
(Source_File
, Token
.First
,
813 "VMS alternative already determined "
814 & "by conditionals");
817 Append
(Rewritten_Line
, Line
(Token
.VMS
.First
820 Append
(Rewritten_Line
, Line
(Token
.Non_VMS
.First
821 .. Token
.Non_VMS
.Last
));
826 Error
(Source_File
, Token
.First
, "invalid VMS alternative");
830 return S
(Rewritten_Line
);
831 end Rewrite_Source_Line
;
833 -------------------------
834 -- Process_Source_File --
835 -------------------------
837 procedure Process_Source_File
is
838 Ifset
: constant String := "@ifset ";
839 Ifclear
: constant String := "@ifclear ";
840 Endsetclear
: constant String := "@end ";
841 -- Strings to be recognized for conditional processing.
844 while not End_Of_File
(Source_File
.Data
) loop
846 Line
: constant String := Get_Line
(Source_File
'Access);
847 Rewritten
: constant String := Rewrite_Source_Line
(Line
);
848 -- We unconditionally rewrite the line so that we can check the
849 -- syntax of all lines, and not only those which are actually
850 -- included in the output.
852 Have_Conditional
: Boolean := False;
853 -- True if we have encountered a conditional preprocessing
856 -- The kind of the directive.
861 -- If the line starts with @ifset or @ifclear, we try to convert
862 -- the following flag to one of our target types. If we fail,
863 -- Have_Conditional remains False.
865 if Line
'Length >= Ifset
'Length
866 and then Line
(1 .. Ifset
'Length) = Ifset
870 Arg
: constant String
871 := Trim
(Line
(Ifset
'Length + 1 .. Line
'Last), Both
);
874 Flag
:= Target_Type
'Value (Arg
);
875 if Translate
(Target_Type
'Image (Flag
), Lower_Case_Map
)
878 Error
(Source_File
, "flag has to be lowercase");
880 Have_Conditional
:= True;
882 when Constraint_Error
=>
883 Error
(Source_File
, "unknown flag for '@ifset'");
886 elsif Line
'Length >= Ifclear
'Length
887 and then Line
(1 .. Ifclear
'Length) = Ifclear
891 Arg
: constant String
892 := Trim
(Line
(Ifclear
'Length + 1 .. Line
'Last), Both
);
895 Flag
:= Target_Type
'Value (Arg
);
896 if Translate
(Target_Type
'Image (Flag
), Lower_Case_Map
)
899 Error
(Source_File
, "flag has to be lowercase");
901 Have_Conditional
:= True;
903 when Constraint_Error
=>
904 Error
(Source_File
, "unknown flag for '@ifclear'");
908 if Have_Conditional
then
909 -- We create a new conditional context and suppress the
910 -- directive in the output.
912 Push_Conditional
(Cond
, Flag
);
914 elsif Line
'Length >= Endsetclear
'Length
915 and then Line
(1 .. Endsetclear
'Length) = Endsetclear
917 -- The '@end ifset'/'@end ifclear' case is handled here. We
918 -- have to pop the conditional context.
921 First
, Last
: Natural;
923 Find_Token
(Source
=> Line
(Endsetclear
'Length + 1
930 Error
(Source_File
, "'@end' without argument");
932 if Line
(First
.. Last
) = "ifset" then
933 Have_Conditional
:= True;
935 elsif Line
(First
.. Last
) = "ifclear" then
936 Have_Conditional
:= True;
940 if Have_Conditional
then
941 Pop_Conditional
(Cond
);
944 -- We fall through to the ordinary case for other @end
946 end if; -- @end without argument
948 end if; -- Have_Conditional
950 if not Have_Conditional
then
951 -- The ordinary case.
952 if not Currently_Excluding
then
953 Put_Line
(Output_File
, Rewritten
);
958 Check_No_Pending_Conditional
;
959 end Process_Source_File
;
961 ---------------------------
962 -- Initialize_Extensions --
963 ---------------------------
965 procedure Initialize_Extensions
is
967 procedure Add
(Extension
: String);
968 -- Adds an extension which is replaced with itself (in upper
971 procedure Add
(Extension
, Replacement
: String);
972 -- Adds an extension with a custom replacement.
974 procedure Add
(Extension
: String) is
976 Add
(Extension
, Translate
(Extension
, Upper_Case_Map
));
979 procedure Add
(Extension
, Replacement
: String) is
981 Set
(Extensions
, Extension
, V
(Replacement
));
984 -- Start of processing for Initialize_Extensions
987 -- To avoid performance degradation, increase the constant in the
988 -- definition of Extensions above if you add more extensions here.
999 end Initialize_Extensions
;
1005 function Is_Extension
(Extension
: String) return Boolean is
1007 return Present
(Extensions
, Extension
);
1010 -------------------------------
1011 -- Get_Replacement_Extension --
1012 -------------------------------
1014 function Get_Replacement_Extension
(Extension
: String) return String is
1016 return S
(Get
(Extensions
, Extension
));
1017 end Get_Replacement_Extension
;
1023 function Is_Known_Word
(Word
: String) return Boolean is
1025 return Present
(Ug_Words
, Word
);
1028 --------------------------
1029 -- Get_Replacement_Word --
1030 --------------------------
1032 function Get_Replacement_Word
(Word
: String) return String is
1034 return S
(Get
(Ug_Words
, Word
));
1035 end Get_Replacement_Word
;
1037 ----------------------
1038 -- Push_Conditional --
1039 ----------------------
1041 procedure Push_Conditional
(Cond
: Conditional
; Flag
: Target_Type
) is
1042 Will_Exclude
: Boolean;
1044 -- If we are already in an excluding context, inherit this property,
1045 -- otherwise calculate it from scratch.
1047 if Conditional_TOS
> 0
1048 and then Conditional_Stack
(Conditional_TOS
).Excluding
1050 Will_Exclude
:= True;
1054 Will_Exclude
:= Flag
/= Target
;
1056 Will_Exclude
:= Flag
= Target
;
1060 -- Check if the current directive is pointless because of a previous,
1061 -- enclosing directive.
1063 for J
in 1 .. Conditional_TOS
loop
1064 if Conditional_Stack
(J
).Flag
= Flag
then
1065 Warning
(Source_File
, "directive without effect because of line"
1066 & Integer'Image (Conditional_Stack
(J
).Starting_Line
));
1069 Conditional_TOS
:= Conditional_TOS
+ 1;
1070 Conditional_Stack
(Conditional_TOS
)
1071 := (Starting_Line
=> Source_File
.Line
,
1074 Excluding
=> Will_Exclude
);
1075 end Push_Conditional
;
1077 ---------------------
1078 -- Pop_Conditional --
1079 ---------------------
1081 procedure Pop_Conditional
(Cond
: Conditional
) is
1083 if Conditional_TOS
> 0 then
1086 if Conditional_Stack
(Conditional_TOS
).Cond
/= Set
then
1088 "'@end ifset' does not match '@ifclear' at line"
1089 & Integer'Image (Conditional_Stack
1090 (Conditional_TOS
).Starting_Line
));
1093 if Conditional_Stack
(Conditional_TOS
).Cond
/= Clear
then
1095 "'@end ifclear' does not match '@ifset' at line"
1096 & Integer'Image (Conditional_Stack
1097 (Conditional_TOS
).Starting_Line
));
1100 Conditional_TOS
:= Conditional_TOS
- 1;
1105 "'@end ifset' without corresponding '@ifset'");
1108 "'@end ifclear' without corresponding '@ifclear'");
1111 end Pop_Conditional
;
1113 -------------------------
1114 -- Currently_Excluding --
1115 -------------------------
1117 function Currently_Excluding
return Boolean is
1119 return Conditional_TOS
> 0
1120 and then Conditional_Stack
(Conditional_TOS
).Excluding
;
1121 end Currently_Excluding
;
1123 ----------------------------
1124 -- VMS_Context_Determined --
1125 ----------------------------
1127 function VMS_Context_Determined
return Boolean is
1129 for J
in 1 .. Conditional_TOS
loop
1130 if Conditional_Stack
(J
).Flag
= VMS
then
1135 end VMS_Context_Determined
;
1137 ----------------------------------
1138 -- Check_No_Pending_Conditional --
1139 ----------------------------------
1141 procedure Check_No_Pending_Conditional
is
1143 for J
in 1 .. Conditional_TOS
loop
1144 case Conditional_Stack
(J
).Cond
is
1146 Error
(Source_File
, "Missing '@end ifset' for '@ifset' at line"
1147 & Integer'Image (Conditional_Stack
(J
).Starting_Line
));
1150 "Missing '@end ifclear' for '@ifclear' at line"
1151 & Integer'Image (Conditional_Stack
(J
).Starting_Line
));
1154 end Check_No_Pending_Conditional
;
1160 Valid_Command_Line
: Boolean;
1161 Output_File_Name
: VString
;
1164 Initialize_Extensions
;
1166 Valid_Command_Line
:= Argument_Count
in 3 .. 4;
1168 -- First argument: Target.
1170 if Valid_Command_Line
then
1172 Target
:= Target_Type
'Value (Argument
(1));
1174 when Constraint_Error
=>
1175 Valid_Command_Line
:= False;
1179 -- Second argument: Source_File.
1181 if Valid_Command_Line
then
1183 Source_File
.Name
:= V
(Argument
(2));
1184 Open
(Source_File
.Data
, In_File
, Argument
(2));
1187 Valid_Command_Line
:= False;
1191 -- Third argument: Dictionary_File.
1193 if Valid_Command_Line
then
1195 Dictionary_File
.Name
:= V
(Argument
(3));
1196 Open
(Dictionary_File
.Data
, In_File
, Argument
(3));
1199 Valid_Command_Line
:= False;
1203 -- Fourth argument: Output_File.
1205 if Valid_Command_Line
then
1206 if Argument_Count
= 4 then
1207 Output_File_Name
:= V
(Argument
(4));
1211 Output_File_Name
:= V
("gnat_ug_vms.texi");
1213 Output_File_Name
:= V
("gnat_ug_wnt.texi");
1215 Output_File_Name
:= V
("gnat_ug_unx.texi");
1217 Output_File_Name
:= V
("gnat_ug_vxw.texi");
1222 Create
(Output_File
, Out_File
, S
(Output_File_Name
));
1224 when Name_Error | Use_Error
=>
1225 Valid_Command_Line
:= False;
1229 if not Valid_Command_Line
then
1231 Set_Exit_Status
(Failure
);
1233 Read_Dictionary_File
;
1234 Close
(Dictionary_File
.Data
);
1236 -- Main processing starts here.
1238 Process_Source_File
;
1239 Close
(Output_File
);
1240 Close
(Source_File
.Data
);
1242 Set_Exit_Status
(Failure
);
1244 Set_Exit_Status
(Success
);