This change file is for the VMS version of TeX.
(VMS means: VAX/VMS, OpenVMS(AXP) and OpenVMS(VAX))
Original version by David Fuchs Copyright 1983 David Fuchs
Changes by Brian {Hamilton Kelly} Copyright 1989,1990 Brian Hamilton Kelly
Changes by Don Hosek Copyright 1990 Don Hosek
DO NOT MODIFY THIS FILE. If you must make changes, use a different name
and alter the banner string so that it no longer has PD VMS in it.
This file may be freely distributed only if it is unmodified.

--Character Reference-------------------------------------------------
                        Upper case letters: ABCDEFGHIJKLMNOPQRSTUVWXYZ
                        Lower case letters: abcdefghijklmnopqrstuvwxyz
                                    Digits: 0123456789
  Square, curly, angle braces, parentheses: [] {} <> ()
            Backslash, slash, vertical bar: \ / |
                               Punctuation: . ? ! , : ;
           Underscore, hyphen, equals sign: _ - =
                 Quotes--right left double: ' ` "
 "at", "number" "dollar", "percent", "and": @ # $ % &
            "hat", "star", "plus", "tilde": ^ * + ~
-----------------------------------------------------------------------

10-JUN-1988 BHK/CNK <tex@uk.ac.cranfield.rmcs>
            Make TeX issue VAX/VMS exit status for use with LSEdit.
22-JUN-1988 BHK <tex@uk.ac.cranfield.rmcs>
            Extend save_size to 2000 (from 600).
21-NOV-1988 CNK <tex@uk.ac.cranfield.rmcs>
             Set |last_text_char| = 255 (from 127).
             See TeXhax vol. 88, no. 100
29-NOV-1988 thru
 6-DEC-1988 BHK <tex@uk.ac.cranfield.rmcs>
             Provide .CLD command-line interface for TeX, etc.
 6-DEC-1988 BHK <tex@uk.ac.cranfield.rmcs>
            Provide .DIA (diagnostics) file output for use with LSEdit
 9-DEC-1988 BHK <tex@uk.ac.cranfield.rmcs>
            Call Language-sensitive Editor when user interacts with `E'
17-FEB-1989 CNK <tex@uk.ac.cranfield.rmcs>
            Surround the above change with 'LSEdit' 'tidESL' delimiters like
            'init' and 'tini'.
20-APR-1989 BHK <tex@uk.ac.cranfield.rmcs>
            Build with larger hash table space, to permit more command names.
25-APR-1989 BHK <tex@uk.ac.cranfield.rmcs>
            Build with larger save and font tables, for Mainz compatibility
 4-MAY-1989 BHK <tex@uk.ac.cranfield.rmcs>
            Tidy up use of temp_file after prompting for different file name.
24-AUG-1989 BHK <tex@uk.ac.cranfield.rmcs> (V2.991-1)
            Revised for TeX V2.991
            Use $TRNLNM instead of $TRNLOG
25-AUG-1989 BHK <tex@uk.ac.cranfield.rmcs>
            General tidying up to improve WEAVEability.
29-AUG-1989 BHK <tex@uk.ac.cranfield.rmcs>
            Remove buggy definition of \\ from limbo material.
            Use LIB$FIND_IMAGE_SYMBOL instead of linking with LSESHR.
30-AUG-1989 BHK <tex@uk.ac.cranfield.rmcs> (V2.991-2)
            Invoke other (callable) editors, or command procedure
12-SEP-1989 BHK <tex@uk.ac.cranfield.rmcs> (V2.991-3)
            Cleanup the nauseous interface to |Edit|
14-SEP-1989 BHK <tex@uk.ac.cranfield.rmcs> (V2.991-4)
            Use VAX/VMS dynamic strings and string descriptors for |Edit|
19-SEP-1989 BHK <tex@uk.ac.cranfield.rmcs> (V2.991-5)
            Support for TECO
25-SEP-1989 BHK <tex@uk.ac.cranfield.rmcs> (V2.991-6)
            Use $CRELNM to pass command file to TECO$EDIT
26-SEP-1989 BHK <tex@uk.ac.cranfield.rmcs> (V2.991-7)
            Remove the byte-alignment for |str_pool|; was actually WORSE!
26-SEP-1989 BHK <tex@uk.ac.cranfield.rmcs> (V2.991-8)
            Make |wake_up_terminal| into procedure, thus saving 8kB in .PAS
            file! Cured \TeX's annoying habit of outputing empty records to
            the terminal even though running in batchmode.  This now prevents
            yards of empty lines in a true batch job .LOG file.
27-SEP-1989 BHK <tex@uk.ac.cranfield.rmcs> (V2.991-9)
            Capture exit status of subprocess spawned by LIB$SPAWN
28-SEP-1989 BHK <tex@uk.ac.cranfield.rmcs> (V2.991-10)
19-OCT-1989 CNK <tex@uk.ac.cranfield.rmcs> (V2.991-11)
            Fix bug which causedthe "Entering \batchmode..." message to be
            deferred until the image exit.
 6-NOV-1989 BHK <tex@uk.ac.cranfield.rmcs> (V2.992 [beta test of V3])
            Updated for this major revision of TeX
13-NOV-1989 BHK <tex@uk.ac.cranfield.rmcs>
            Minor cosmetic revisions.
            Replace some occurrences of so(...) on left of equation/assignment
            by si(...) on the right.
14-NOV-1989 BHK <tex@uk.ac.cranfield.rmcs>
            Don't bother to lowercase |command_line|; users need to quote
            multiple parameters anyway.
            Correct DEK's booboo when indexing \badness primitive
13-DEC-1989 BHK <tex@uk.ac.cranfield.rmcs>
            Added DEK's changes (bugfix 339 - see TeXhax V89 #110)  (V2.993-0)
10-JAN-1990 BHK <tex@uk.ac.cranfield.rmcs>
            Updated for definitive v2.993 of tex.web (dated 8-Jan at 6am PST)
 5-APR-1990 BHK <tex@uk.ac.cranfield.rmcs>
            Updated for canonical v3 of TeX.web [This would be PD VMS 3.0]
21-APR-1990 thru
27-APR-1990 Don Hosek <dhosek@ymir.claremont.edu>
            Assorted changes to BHK's original to form PD VMS 3.1:
            -modified formatting of history to conform to other HMC TeX change
             files.
            -changed format of banner line to include PD VMS version number;
             added brief notes on history of change file.
            -re-incorporated BigTeX changes into main change file: 64-bit TeX
             is still TeX according to DEK, and if there is a serious problem
             with memory usage, the TeX installer can simply change mem_max.
             (Brian's TEX-BIGTEX.CH file sometimes reduced the capacity of
             TeX!)
            -All instances of a logical name called TEX$something have been
             changed to TEX_something.
            -Changed the definition of aux_buf (sec 30) from 133 to
             size_input_line (defined in sec 11).
            -Having TeX continue after editing is WRONG!. See p. 32 of the
             TeXbook, item 9 if you don't believe me. The changes for
             section 84 have been fixed accordingly.
            -Removed test of ready_already; BHK's trick deals with it, _but_
             why bother? We don't preload in a fashion that it's ever true!
            -Split line in section 1394 which was over 80 characters.
            -Have editor selected by /EDITOR qualifier in CLD file. The default
             value will be TEX_EDIT. (two-pronged advantage: (1) user can,
             if they want actually use /EDITOR qualifier, and (2) if installer
             insists on breaking the rules, he can change to TEX$EDIT (or what
             I think would be classy, MAIL$EDIT) with little difficulty.
            -Added qualifiers /TEXINPUTS and /TEXFONTS specifying the name of
             the logicals to be used for the inputs search path and fonts
             search path. This is only used by INITEX since these names are
             read from the pool file by TeX. (As an interesting aside, this
             qualifier could conceivably be used to have different inputs and
             fonts search paths for different formats. I'm not sure that this
             would be useful but eliminating the LaTeX directories from the
             plain search path might speed things up a little in opening
             files.)
            -Added qualifier /TEXFORMATS with similar function to /TEXINPUTS
             and /TEXFONTS. This qualifier is used by both IniTeX and TeX (now
             wouldn't this be a silly idea: put the name of the format area
             into the format file :-). This actually has a neat application:
             suppose you have both TeX and smallTeX running; you define verbs
             for SMALLTEX, SMALLINITEX, SMALLLATEX, etc. and make a set of
             format files. Now the kludgey solution is to rename the format
             files to things like SMALLPLAIN.FMT &c, but I don't like kludges.
             A better plan is to define SMALL_TEX_FORMATS to be
             TEX_ROOT:[SMALLFORMATS] and then in all of the verb definitions
             put the default for TEXFORMATS as SMALL_TEX_FORMATS. Classy, eh?
            -Integrated INITeX and TeX into a single executable by defining
             init==if init_flag begin and tini==end; and changing behaviour of
             /INIT flag (incidentally, can't the INITIALIZE problem be dealt
             with by saying INIT :== INITIALIZE?). All conditionals (there
             are three) are outside the main loop, so the only problem would be
             with the bigger executable. INITeX is 10% larger than TeX (25K
             difference). Big deal. Since DCL insists on command verbs being
             unique in the first four letters (what kind of stupid restriction
             is that? Are they trying to pack identifiers into words? Wotta
             bunch of losers) INITEX should be defined with
                INITEX :== TEX/INIT/NOFORMAT
             (BHK's original let you get away with TEX/INIT, but required
             two executables.) This annoyance will go away in a future
             release of VMS (perhaps 5.4)
            -Minor change to handling of /EDITOR qual. If it ends with a ':',
             it is treated as a logical, otherwise it's treated as a literal.
             Also added the possibility of specifying /NOEDITOR (I use this
             under DECwindows since I have the file already called up in a
             DW EVE window.)
            -Allow TeX to print characters in the range x'a0'-x'fe' without
             using ^^ notation.
            -Added character table to beginning of change file to assist those
             who get it via e-mail in detecting errors in gateways etc.
            -Included TEX.CLD into the change file (my experience with CMS
             TeX was that front-end EXECs were frequently not distributed
             with the change file; including them inside the change file
             guaranteed (1) that someone receiving a copy would have a
             copy of the EXEC and (2) if the EXEC they received had been
             modified at all, they could compare it against the original in
             the change file--the same considerations should apply for
             VMS TeX and CLDs).
            -Changed definition of pool_name so that it uses value from
             /TEXFORMATS
            -Added fix for active definition of E so that TeX.WEB will WEAVE.
28-APR-1990 Don Hosek v3.1a (this was necessary due to the fact that some
            defective change files may have found their way out under the
            label 3.1; in general, I will be using a letter after the version
            number to indicate bug fixes).
            Changed the way that we handle setting the value for TEXINPUTS
            and TEXFONTS. We (obviously) cannot let INITEX write them into
            the format file, because that technique is highly dependent on
            the string pool mechanism (exercise for "real" TeXhackers: add
            a mechanism for allowing strings set at run time to be communicated
            between INITEX and TeX). Anyway, the fix for this is deliriously
            simple; figuring out the problem, on the other hand, took 1h40m
            of banging my head against my VAXstation. All we need to do is
            move the initialization of the appropriate strings into the
            main program loop at an appropriate location.
23-AUG-1990 Documentation changes. Fixed dropped-E bug, also fixed header
            in the WEAVE output.
30-AUG-1990 Don Hosek v3.2. Added /[NO]CONTINUE option to control whether
            TeX continues after a response of E at the error prompt; added
            /[NO]JOBNAME_SYMBOL=n option for controlling whether the specified
            DCL symbol is created at completion of the TeX run. Minor
            documentation changes.
12-SEP-1990 thru
13-SEP-1990 Brian Hamilton Kelly v3.3.  Added /EIGHT_BIT qualifier to control
            whether characters in the range 160--254 are printed as is, or are
            converted to ^^c0--^^fe; the default action of v3.2 (to print them)
            was less than helpful on 7-bit terminals!  If the command definition
            file is edited so that this qualifier is on by default, it should
            be explicitly negated in batch jobs, because often line-printers
            are also unable to print these characters.
            Removed code for, and description of, closing |termin|; this is
            unnecessary, and only inserted by BHK in mistaken belief that this
            was interfering with \TeX\ input taken from a DCL command procedure
            Ensured ALL command-line qualifiers extracted before TeX starts to
            run; subsequent invocation of an editor wipes out the information
            so that it isn't available to, e.g., set the name of /JOBNAME_SYMBOL
25-SEP-1990 Don Hosek (Still v3.3--look, it's a demonstration of what
            happens with a new version of TeX!) Installed BHK's changes to
            3.3, insignificant changes for TeX 3.1 (DEK used my fix for the
            dropped E! :-). Removed batch from /INIT qualifier. This was not
            harmful, but it perplexed Mary Deck.
09-JAN-1992 Don Hosek v3.3a. Increased string_vacancies to string_pool-24000
            and boosted string_pool and number of strings as well.
-------------------------------------------------------------------------------
The following changes from BHK did not find their way into the Don Hosek
version (on YMIR) of VMS TeX:
-------------------------------------------------------------------------------
24-SEP-1990 BHK <tex@uk.ac.cranfield.rmcs>
            Incorporated changes for TeX.web V3.1
26-SEP-1990 BHK <tex@uk.ac.cranfield.rmcs>
            Removed TEX.CLD from the change file; since this now exists in _
            and $ forms, and if they are to be included here then so too should
            the DESCRIP.MMS files!  But that amounts to 2540 lines of extra
            limbo comment.
27-SEP-1990 BHK <tex@uk.ac.cranfield.rmcs> [PD V3.4]
            -Send eight-bit printable characters to the terminal only, not
            the log file, and then only if /EIGHT_BIT qualifier specified.
            This change depends upon Knuth continuing to use |print_ASCII|
            on those occasions when he's printing a single character.
29-MAY-1991 BHK <tex@uk.ac.cranfield.rmcs> [PD V3.4]
            Updated for TeX.web V3.14
26-SEP-1991 BHK <tex@uk.ac.cranfield.rmcs> [PD V3.4]
            Updated for alpha-test 3.14a version
03-FEB-1992 BHK <tex@uk.ac.cranfield.rmcs> [PD V3.4]
            Updated for alpha-test 3.14b version
17-MAR-1992 BHK <tex@uk.ac.cranfield.rmcs> [PD V3.4]
            Updated for official (16-MAR-1992 05:10 PST) 3.141 version
-------------------------------------------------------------------------------
The following section of the change log documents the developement at
TH-Darmstadt, Germany. In this stream, it was tried to merge the
diverging verions from YMIR (Don Hosek) and RCMS (B.H.Kelly), again.
The BHK changes above have been checked and were incorporated (when not
already done).
Exception: Change (27-SEP-1990) of /EIGHT_BIT behaviour has not been used.
           This change is considered to have more drawbacks than advantages.
-------------------------------------------------------------------------------
01-Feb-1994 SPC Christian Spieler (still v3.3a).
            Increased more compile time constants. Allowed use of large
            german hyphenation patterns.
            Fixed WEAVE bug with /EIGHT_BIT qualifier. Added a (temporary)
            workaround for DEC Pascal VMS/AXP bug in a separate change file
            for Alpha VMS TeX.
25-Feb-1994 SPC Christian Spieler (still v3.3a).
            Removed TEX.CLD from the change file. There are now two
            versions (TEX.CLD_ and TEX.CLD$) to simplify installation
            on sites that prefer the "$" form for the TeX logical names.
            (See BHK change 26-SEP-1990)
03-Jun-1994 SPC Christian Spieler v3.4.
            Modified TeX's input file processing, to facilitate the use
            of modern macro packages like LaTeX2e.
            Now, \openin uses the same file search algorithm as \input.
15-Aug-1994 SPC Christian Spieler [PD V3.5].
            - Clarified Changelog. Cited all Changelog entries from
              previous official versions. Added remark about their
              present status (see above).
            - Changed most references to "VAX/VMS" into "VMS"
            - Enlarged space for hyphenation patterns, again.
            - Increased version number of port to [PD V3.5], to prevent
              clashes with BHK's V3.4.
            - Removed "SDALINAC THD". This version is now official.
09-SEP-1994 SPC Christian Spieler <spieler@linac.ikp.physik.th-darmstadt.de>
            - Really removed lowercasing of the command line parameter string,
              when using the full DCL command line parsing interface.
              There was a ``not'' missing in an if clause.
              (This should have been done since 14-Nov-1989 (BKH), but
              has been omitted somehow.)
            - Updated version number to [PD VMS 3.5a]
19-OCT-1994 SPC Christian Spieler <spieler@linac.ikp.physik.th-darmstadt.de>
            Added new features:
            - Revised the command line handling: Added the ability to
              parse the command line internally, when invoked as a foreign
              command.
            - Apply wildcard expansion to all input file specifications.
              This allows to use wildcards in the /TEXINPUTS and /TEXFONTS
              search list and implements the "subdirectory searching" facility,
              recommended by the TDS (standard TeX Directory Structure)
              proposal.
            - Changed the AXP workaround for the missing "undefined"
              function. The new solution does not depend on an undocumented
              RTL routine, this allows its use for the VAX version, too.
              In consequence, VAX and AXP change files have been re-unified.
            - Incremented version number to [PD VMS 3.6]
24-OCT-1994 SPC Christian Spieler <spieler@linac.ikp.physik.th-darmstadt.de>
            - Repaired bug introduced into |more_name| by 19-OCT-1994 change.
            - Removed bug in logical name translation (|end_name|).
            - Do not abort |user_reset| when |VAX_rms_search| failed.
23-NOV-1994 SPC Christian Spieler <spieler@linac.ikp.physik.th-darmstadt.de>
            - |default_name| gets cleared regardless if used or not. This
              is needed to allow \openout of filenames with empty name body.
14-FEB-1995 SPC Christian Spieler <spieler@linac.ikp.physik.th-darmstadt.de>
            Small cleanups of [PD VMS 3.6]:
            - Removed unneccessary change of |max_halfword| value check.
            - Minimized differences between production and triptest versions.
06-APR-1995 SPC Christian Spieler <spieler@linac.ikp.physik.th-darmstadt.de>
            - Changed |editor_ident| record to` unpacked' and resorted its
              components for better alignment.
            - Removed some unused variables.
            - Use |slow_print| to write log file name to terminal.
            - Modified changefile PD VMS 3.6 to match TeX 3.14159 (Knuth).
10-APR-1995 SPC Christian Spieler <spieler@linac.ikp.physik.th-darmstadt.de>
            - Force byte alignment for |memory_word| structure by declaring
              it as a |packed record|. This change removes the problematic
              neccessity to specify /ALIGN=VAX for the AXP Pascal compiler.
29-JUL-1995 SPC Christian Spieler <spieler@linac.ikp.physik.th-darmstadt.de>
            - Reorganized (splitted and shortened) some change entries to
              archive compatibility with the NTS e-tex.ch system independent
              change file. Unfortunately, the VMS change of Section 313
              interferes with an e-tex change, so full compatibility cannot
              be archived.
31-JUL-1995 SPC Christian Spieler <spieler@linac.ikp.physik.th-darmstadt.de>
            - More reorganization to comply with parallel developement of
              e-TeX change file.
03-AUG-1995 SPC Christian Spieler <spieler@linac.ikp.physik.th-darmstadt.de>
            - Changed editor command file handling: Use the same solution as
              found in the VMS MF implementation, which works even when all
              16 of TeX's output streams are in use.
1995--1996 PT No changes, but renamed to TeX.Vms_Changes (underscore v. hyphen)
April 1996 PT Long lines (> 80 chars) wrapped to 80.
Oct 1996   PT No changes, but renamed to "tex_vms.ch"
{Section 0}
>>>>>Removed 25-SEP-1990 by DAH>>>>>> Dropped-E fix no longer necessary.

{Section 0}
<<<<<Modified 10-JAN-1990 by BHK>>>>>
>>>>>Modified 30-AUG-1990 by DAH>>>>>
<<<<<Modified 12-SEP-1990 by BHK>>>>>
>>>>>Modified 10-APR-1991 by SPC : DEK's 3.14, local signature>>>>>
>>>>>Modified 10-APR-1992 by SPC : updated to DEK's 3.141>>>>>
>>>>>Modified 13-SEP-1993 by SPC : updated to DEK's 3.1415>>>>>
>>>>>Modified 15-AUG-1994 by SPC : PD V 3.5, no local sign.>>>>>
>>>>>Modified 09-SEP-1994 by SPC : PD V 3.5a.>>>>>
>>>>>Modified 19-OCT-1994 by SPC : PD V 3.6.>>>>>
>>>>>Modified 06-APR-1995 by SPC : updated to DEK's 3.14159>>>>>
>>>>>Modified 29-JUL-1995 by SPC : compatibility with e-tex>>>>>
@x
\pageno=3
@y
\pageno=3
\let\maybe=\iffalse %%% PT   8-DEC-1997 17:49:48 
\toks0=\expandafter{\title}
\maybe
\edef\title{\the\toks0\ 3.14159 [PD VMS 3.6]}
\else
\edef\title{\the\toks0\ 3.14159 [PD VMS 3.6] changes}
\fi
\def\LaTeX{{\rm L\kern-.3em\raise.33ex\hbox{\sc A}\kern-.15em\TeX}}
@z

{Section 2}
<<<<<Modified 10-JAN-1990 by BHK>>>>>
<<<<<Modified 05-APR-1990 by BHK>>>>>
>>>>>Modified 30-AUG-1990 by DAH>>>>>
<<<<<Modified 12-SEP-1990 by BHK>>>>>
>>>>>Modified 10-APR-1991 by SPC : DEK's 3.14, local signature>>>>>
>>>>>Modified 10-APR-1992 by SPC : updated to DEK's 3.141>>>>>
>>>>>Modified 13-SEP-1993 by SPC : updated to DEK's 3.1415>>>>>
>>>>>Modified 15-AUG-1994 by SPC : PD V 3.5, no local sign.>>>>>
>>>>>Modified 09-SEP-1994 by SPC : PD V 3.5a.>>>>>
>>>>>Modified 19-OCT-1994 by SPC : PD V 3.6.>>>>>
>>>>>Modified 06-APR-1995 by SPC : updated to DEK's 3.14159>>>>>
@x
@d banner=='This is TeX, Version 3.14159' {printed when \TeX\ starts}
@y
This change file is the result of a long odyssey of change files beginning with
the original change files created in 1984 by David Fuchs; many people have made
significant contributions since then, the most notable of whom have been Brian
Hamilton Kelly, Niel Kempson, and Adrian Clark

@d banner=='This is TeX, Version 3.14159 [PD VMS 3.6]'
@z

<<<<<Modified 25-AUG-1989 by BHK : tidy up indentation in WEAVE>>>>>
{Section 4}
@x
procedure initialize; {this procedure gets things started properly}
@y
@t\4@>@<VMS procedures@>@/
procedure initialize; {this procedure gets things started properly}
@z

<<<<<Modified 25-AUG-1989 by BHK : tidy up indentation in WEAVE>>>>>
{Section 7}
@x
@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
@y
@d debug==@{
@d gubed==@t\2@>@}
@z

<<<<<Modified 25-AUG-1989 by BHK : tidy up indentation in WEAVE>>>>>
{Section 7}
@x
@d stat==@{ {change this to `$\\{stat}\equiv\null$' when gathering
  usage statistics}
@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$' when gathering
  usage statistics}
@y
@d stat==
@d tats==@t\2@>
@z

<<<<<Added 20-FEB-1989 by CNK>>>>>
<<<<<Modified 25-AUG-1989 by BHK : tidy up indentation in WEAVE>>>>>
<<<<<Modified 29-AUG-1989 by BHK : LSEdit-tidESL not necessary>>>>>
<<<<<Modified 25-MAY-1990 by DAH : init...tini is now a conditional!>>>>>
<<<<<Modified 29-NOV-1997 by PEB : init...tini for declarations>>>>>
<<<<<Modified 29-NOV-1997 by PEB : Init...Tini for executable code>>>>>
{Section 8}
@x
the codewords `$|init|\ldots|tini|$'.

@d init== {change this to `$\\{init}\equiv\.{@@\{}$' in the production version}
@d tini== {change this to `$\\{tini}\equiv\.{@@\}}$' in the production version}
@y
the codewords `$|init|\ldots|tini|$' for declarations and by the codewords
`$|Init|\ldots|Tini|$' for executable code.  This distinction is helpful for
implementations where a run-time switch differentiates between the two
versions of the program.

@d init==
@d tini==
@d Init==init if init_flag then begin
@d Tini==end;@+tini
@f Init==begin
@f Tini==end
@z

@x [1.8] l.319 - init...tini is dynamic
@!init @<Initialize table entries (done by \.{INITEX} only)@>@;@+tini
@y  318
@!Init @<Initialize table entries (done by \.{INITEX} only)@>@;@+Tini
@z

<<<<<Modified 29-NOV-1988, 5-DEC-1988 & 24-AUG-1989 by BHK>>>>>
<<<<<Modified 15-SEP-1989 by BHK : move some @d's nearer to use>>>>>
{Section 9}
@x compiler directives== starlet and vax reserved words
@ If the first character of a \PASCAL\ comment is a dollar sign,
\ph\ treats the comment as a list of ``compiler directives'' that will
affect the translation of this program into machine language.  The
directives shown below specify full checking and inclusion of the \PASCAL\
debugger when \TeX\ is being debugged, but they cause range checking and other
redundant code to be eliminated when the production system is being generated.
Arithmetic overflow will be detected in all cases.
@^system dependencies@>
@^Overflow in arithmetic@>

@<Compiler directives@>=
@{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead}
@!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging}
@y
@ When the \PASCAL\ program generated as a result of `tangling' the \.{WEB} with
the change file is compiled under VMS, command line qualifiers should be
included to specify full checking and inclusion of debugger symbol records
whilst \TeX\ is being debugged, but eliminate range checking and other
redundant code when the production system is being generated.
Arithmetic overflow should be detected in all cases.
@^system dependencies@>
@^Overflow in arithmetic@>

Under VMS, we arrange to `inherit' the descriptions of standard system
services and named constants from the precompiled \PASCAL\ environment held in
|'SYS$LIBRARY:STARLET.PEN'|---we do \&{not} specify whether or not any specific
level of run-time checks shall be included, because any such attribute applied
within the source code cannot be overridden by a command line qualifier when
\TeX\ is compiled.

This library does not include \&{all} the library routines that are used by
\TeX82 under VMS, so other routines are declared as required using \PASCAL's
syntax for |extern| routines.

@f extern==forward

@<Compiler directives@>=
@/@=[inherit('sys$library:starlet')]@>@\
 {allows us to use system symbols and routines}
@z

{Section 10}
@x
@d othercases == others: {default for cases not listed explicitly}
@y

Fortunately for us, VAX-\PASCAL\ \&{does} support this default mechanism.

@d othercases == otherwise {default for cases not listed explicitly}
@z

{Section 11}
>>>>>Modified 26-MAY-1990 by DAH : fix handling of |pool_name|>>>>>
<<<<<Modified 06-NOV-1989 by BHK : update for V3 beta test>>>>>
<<<<<Modified 25-AUG-1989 by BHK : |file_name_size| extended>>>>>
>>>>>Modified 05-JUN-1991 by SPC :
        increased |trie_size| and |trie_op_size|
        to allow loading of multi-language hyphenation patterns>>>>>
>>>>>Modified (10-APR-1992/01-Feb-1994) by SPC :
        enlarged some other parameters:
        |max_in_open|      :     6 ->     12
        |font_max|         :   150 ->    255
        |font_mem_size|    : 50000 ->  81920
        |save_size|        :  1500 ->   2000 >>>>>
>>>>>Modified 15-AUG-1994 by SPC :
        increased |trie_size| and |trie_op_size| again,
        on request of J.L.Braahms>>>>>
>>>>>Modified 03-AUG-1995 by SPC :
        increased difference between |pool_size| and |string_vacancies| to
        26000, to leave enough space for TeX's (ca. 23800) as well as e-TeX's
        (24550) own string characters; enlarged |buf_size| to 2048.>>>>>
@x [1] compile-time constants
@^system dependencies@>

@<Constants...@>=
@!mem_max=30000; {greatest index in \TeX's internal |mem| array;
  must be strictly less than |max_halfword|;
  must be equal to |mem_top| in \.{INITEX}, otherwise |>=mem_top|}
@!mem_min=0; {smallest index in \TeX's internal |mem| array;
  must be |min_halfword| or more;
  must be equal to |mem_bot| in \.{INITEX}, otherwise |<=mem_bot|}
@!buf_size=500; {maximum number of characters simultaneously present in
  current lines of open files and in control sequences between
  \.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword|}
@!error_line=72; {width of context lines on terminal error messages}
@!half_error_line=42; {width of first lines of contexts in terminal
  error messages; should be between 30 and |error_line-15|}
@!max_print_line=79; {width of longest text lines output; should be at least 60}
@!stack_size=200; {maximum number of simultaneous input sources}
@!max_in_open=6; {maximum number of input files and error insertions that
  can be going on simultaneously}
@!font_max=75; {maximum internal font number; must not exceed |max_quarterword|
  and must be at most |font_base+256|}
@!font_mem_size=20000; {number of words of |font_info| for all fonts}
@!param_size=60; {maximum number of simultaneous macro parameters}
@!nest_size=40; {maximum number of semantic levels simultaneously active}
@!max_strings=3000; {maximum number of strings; must not exceed |max_halfword|}
@!string_vacancies=8000; {the minimum number of characters that should be
  available for the user's control sequences and font names,
  after \TeX's own error messages are stored}
@!pool_size=32000; {maximum number of characters in strings, including all
  error messages and help texts, and the names of all fonts and
  control sequences; must exceed |string_vacancies| by the total
  length of \TeX's own strings, which is currently about 23000}
@!save_size=600; {space for saving values outside of current group; must be
  at most |max_halfword|}
@!trie_size=8000; {space for hyphenation patterns; should be larger for
  \.{INITEX} than it is in production versions of \TeX}
@!trie_op_size=500; {space for ``opcodes'' in the hyphenation patterns}
@!dvi_buf_size=800; {size of the output buffer; must be a multiple of 8}
@!file_name_size=40; {file names shouldn't be longer than this}
@!pool_name='TeXformats:TEX.POOL                     ';
  {string of length |file_name_size|; tells where the string pool appears}
@y

Since a number of arrays of |file_name_size| are used in this program to receive
the full file specification of files when they are opened, it is necessary to
extend this constant to 255, which is the maximum possible size that VAX/RMS can
@.RMS@>
@^Record Management Services@>
return.  It is not necessary, however, to pad out |pool_name| to this size
(which would in any case prove ``difficult'' in WEB, because VAX-\PASCAL\
automatically pads short strings with spaces when assigned into longer
variables. Furthermore, because the area where the pool file resides is
specified at run time, we need to make |pool_name| into a variable which
will be set shortly after we determine the value of the name of the format
area. Here, we define the constant |pool_f_name| instead, which holds
the name of the pool file without any area specification.
@^system dependencies@>

@<Constants...@>=
@!mem_max=327144; {greatest index in \TeX's internal |mem| array;
  must be strictly less than |max_halfword|;
  must be equal to |mem_top| in \.{INITEX}, otherwise |>=mem_top|}
@!mem_min=0; {smallest index in \TeX's internal |mem| array;
  must be |min_halfword| or more;
  must be equal to |mem_bot| in \.{INITEX}, otherwise |<=mem_bot|}
@!buf_size=2048; {maximum number of characters simultaneously present in
  current lines of open files and in control sequences between
  \.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword|}
@!size_input_line=133; {maximum size of an input line}
@!error_line=79; {width of context lines on terminal error messages}
@!half_error_line=50; {width of first lines of contexts in terminal
  error messages; should be between 30 and |error_line-15|}
@!max_print_line=79; {width of longest text lines output; should be at least 60}
@!stack_size=200; {maximum number of simultaneous input sources}
@!max_in_open=12; {maximum number of input files and error insertions that
  can be going on simultaneously}
@!font_max=255; {maximum internal font number; must not exceed |max_quarterword|
  and must be at most |font_base+256|}
@!font_mem_size=81920; {number of words of |font_info| for all fonts}
@!param_size=60; {maximum number of simultaneous macro parameters}
@!nest_size=40; {maximum number of semantic levels simultaneously active}
@!max_strings=15400; {maximum number of strings; must not exceed |max_halfword|}
@!string_vacancies=160000; {the minimum number of characters that should be
  available for the user's control sequences and font names,
  after \TeX's own error messages are stored}
@!pool_size=186000; {maximum number of characters in strings, including all
  error messages and help texts, and the names of all fonts and
  control sequences; must exceed |string_vacancies| by the total
  length of \TeX's own strings, which is currently about 23800 for VMS \TeX}
@!save_size=2000; {space for saving values outside of current group; must be
  at most |max_halfword|}
@!trie_size=45000; {space for hyphenation patterns; should be larger for
  \.{INITEX} than it is in production versions of \TeX}
@#
@!trie_op_size=1000; {space for ``opcodes'' in the hyphenation patterns}
@!dvi_buf_size=1024; {size of the output buffer; must be a multiple of 8}
@!VAX_block_length=512; {must be half |dvi_buf_size| on VMS}
@!file_name_size=255; {file names shouldn't be longer than this}
@#
@!pool_f_name='TEX.POOL';
  {string \&{not} of length |file_name_size|; tells the name of the string
  pool}
@z

{Section 12}
<<<<<Modified 10-JAN-1990 by BHK>>>>>
>>>>>Modified (01-Feb-1994) by SPC :
        enlarged some other parameters:      (alternate values)
        |hash_size|        :  5000 ->  10000 (9500)
        |hash_prime|       :  4253 ->   8501 (7919)
       {|hyph_size|        :   503 ->    607 (probably not needed)} >>>>>
@x [1] Tangle-time constants
@d mem_bot=0 {smallest index in the |mem| array dumped by \.{INITEX};
  must not be less than |mem_min|}
@d mem_top==30000 {largest index in the |mem| array dumped by \.{INITEX};
  must be substantially larger than |mem_bot|
  and not greater than |mem_max|}
@d font_base=0 {smallest internal font number; must not be less
  than |min_quarterword|}
@d hash_size=2100 {maximum number of control sequences; it should be at most
  about |(mem_max-mem_min)/10|}
@d hash_prime=1777 {a prime number equal to about 85\pct! of |hash_size|}
@d hyph_size=307 {another prime; the number of \.{\\hyphenation} exceptions}
@y
@d mem_bot=0 {smallest index in the |mem| array dumped by \.{INITEX};
  must not be less than |mem_min|}
@d mem_top==327144 {largest index in the |mem| array dumped by \.{INITEX};
  must be substantially larger than |mem_bot|
  and not greater than |mem_max|}
@d font_base=0 {smallest internal font number; must not be less
  than |min_quarterword|}
@d hash_size=10000 {maximum number of control sequences; it should be at most
  about |(mem_max-mem_min)/10|}
@d hash_prime=8501 {a prime number equal to about 85\pct! of |hash_size|}
@d hyph_size=503 {another prime; the number of \.{\\hyphenation} exceptions}
@z

{Section 23}
<<<<<Modified 06-NOV-1989 by BHK : update for V3 beta test>>>>>
@x [2] System-dependent character set changes:
@^character set dependencies@>
@^system dependencies@>

@<Set init...@>=
for i:=0 to @'37 do xchr[i]:=' ';
for i:=@'177 to @'377 do xchr[i]:=' ';
@y
@^character set dependencies@>
@^system dependencies@>

The code shown here is intended to be used on VMS systems,
and at other installations where only the printable ASCII set, plus
|carriage_return|, |tab|, and |form_feed| will show up in text files.
All |line_feed| and |null| characters are skipped.

We also permit characters taken from columns 10--15 of the extended ISO
character set; macro packages can then utilize these character codes for
multilingual support.

@d form_feed=@'14 {ASCII code used at end of a page}
@d tab=@'11

@<Set initial values...@>=
xchr[0]:=' ';
for i:=1 to @'37 do xchr[i]:=chr(i);
xchr[form_feed]:=chr(form_feed);
xchr[tab]:=chr(tab);
for i:=@'177 to @'237 do xchr[i]:=' ';
for i:=@'240 to @'377 do xchr[i]:=chr(i);
@z

{Section 25}
<<<<<Added on 28-SEP-1989 by BHK>>>>>
@x
The program actually makes use also of a third kind of file, called a
|word_file|, when dumping and reloading base information for its own
initialization.  We shall define a word file later; but it will be possible
for us to specify simple operations on word files before they are defined.
@y
The program actually makes use also of a third kind of file, called a
|word_file|, when dumping and reloading base information for its own
initialization.  We shall define a word file later; but it will be possible
for us to specify simple operations on word files before they are defined.

Since the \.{WEB} already uses the name |text| for its own purposes, we have to
define a macro to permit access to this VAX-\PASCAL\ file type identifier.

@d VAX_text==@= text @>
@z

{Section 25}
@x [3] alpha files are text and byte files are blocks
@!alpha_file=packed file of text_char; {files that contain textual data}
@!byte_file=packed file of eight_bits; {files that contain binary data}
@y
@!alpha_file=VAX_text; {files that contain textual data}
@!byte_block=packed array[0..VAX_block_length-1] of eight_bits;
@!byte_file=packed file of byte_block; {files that contain binary data}
@z

{Section 26}
@x  <<<<<Added 5-DEC-1988 by BHK>>>>>
implement \TeX\ can open a file whose external name is specified by
|name_of_file|.
@^system dependencies@>

@<Glob...@>=
@!name_of_file:packed array[1..file_name_size] of char;@;@/
  {on some systems this may be a \&{record} variable}
@!name_length:0..file_name_size;@/{this many characters are actually
  relevant in |name_of_file| (the rest are blank)}
@y
implement \TeX\ can open a file whose external name is specified by
|name_of_file|.  Any VAX-\PASCAL\ defaults may be supplied in |default_name|;
this is used to expand partial file specifications given on such qualifiers as
\.{/LOG}, in combination with other parts taken from the file specification of
the \.{.TeX} file.
@^system dependencies@>

@<Glob...@>=
@!name_of_file, @!default_name:packed array[1..file_name_size] of char;@;@/
  {on some systems this may be a \&{record} variable}
@!name_length, @!deflt_length : file_size;@/
  {this many characters are actually relevant in |name_of_file|
   (the rest are blank)}
@z


{Section 27}
<<<<<Modified 25-AUG-1989 by BHK : describe VAX file opening>>>>>
@x
@ The \ph\ compiler with which the present version of \TeX\ was prepared has
extended the rules of \PASCAL\ in a very convenient way. To open file~|f|,
we can write
$$\vbox{\halign{#\hfil\qquad&#\hfil\cr
|reset(f,@t\\{name}@>,'/O')|&for input;\cr
|rewrite(f,@t\\{name}@>,'/O')|&for output.\cr}}$$
The `\\{name}' parameter, which is of type `{\bf packed array
$[\langle\\{any}\rangle]$ of \\{char}}', stands for the name of
the external file that is being opened for input or output.
Blank spaces that might appear in \\{name} are ignored.

The `\.{/O}' parameter tells the operating system not to issue its own
error messages if something goes wrong. If a file of the specified name
cannot be found, or if such a file cannot be opened for some other reason
(e.g., someone may already be trying to write the same file), we will have
|@!erstat(f)<>0| after an unsuccessful |reset| or |rewrite|.  This allows
\TeX\ to undertake appropriate corrective action.
@:PASCAL H}{\ph@>
@^system dependencies@>

\TeX's file-opening procedures return |false| if no file identified by
|name_of_file| could be opened.

@d reset_OK(#)==erstat(#)=0
@d rewrite_OK(#)==erstat(#)=0

@p function a_open_in(var f:alpha_file):boolean;
@y
@ Under VAX-\PASCAL, we can open files with names that are not known at compile
time through use of the VAX-specific procedure |open|, which takes too many
varied parameters to describe here: for example, the third parameter controls
whether a new file shall be generated, or can enforce that an existing file
cannot possibly be altered.
@^system dependencies@>
However, one in particular deserves special mention: the |user_action| parameter
when included causes execution of a user-supplied routine which can
manipulate the data structures used by RMS (Record Management Services) and thus
@.RMS@>
@^Record Management Services@>
permit finer control over the actions undertaken during the opening or creation
of files.

All file manipulation procedures in VAX-\PASCAL\ (including |open|/|close|,
|read|/|write|, etc.)\ can take an optional parameter which specifies whether
or no the program shall continue execution after an error.  Since we code to
detect such errors, we nearly always make use of this facility.

\TeX's file-opening procedures return |false| if no file identified by
|name_of_file| could be opened: note that VAX-\PASCAL's |status| function
returns zero if the previous file operation was successfully completed, |-1| if
|eof| would be |true|, and a positive integer if any error was detected.

When a |new| file is opened, we specify that it shall be deleted when the
program exits; this ensures that output files are correctly discarded if \TeX\
is interrupted in its work.  Later, when the files are closed, we can arrange to
keep the files instead.  VAX-\PASCAL's |open| procedure also allows us to
specify a `default' file specification, which is used to supply defaults for
those parts of the specification of the file being created that have not
otherwise been provided by the user.

Whenever a |word_file| is opened, the variable |fmt_count| is reset to zero to
ensure that the first byte of the VAX block is that first accessed.

@d VAX_user_action==@=user_action@>
@#
@d VAX_new==@= new @>
@d VAX_readonly==@= readonly @>
@#
@d VAX_default==@= default @>
@#
@d VAX_disposition_delete==@=disposition:=delete@>
@d VAX_ignore_error==@=error:=continue@>

@p function a_open_in(var f:alpha_file):boolean;
@z

{Section 27}
@x [3] file opening
begin reset(f,name_of_file,'/O'); a_open_in:=reset_OK(f);
@y
begin
open(f,name_of_file,VAX_readonly,VAX_user_action:=user_reset,
        VAX_ignore_error);
if status(f)>0 then a_open_in:=false
else begin
 reset(f,VAX_ignore_error);
 a_open_in:=status(f)<=0;
 end;
@z

{Section 27}
@x  <<<<<Added 5-DEC-1988 by BHK>>>>>
begin rewrite(f,name_of_file,'/O'); a_open_out:=rewrite_OK(f);
@y
begin
open(f,name_of_file,VAX_new,16383,VAX_disposition_delete,
        VAX_default:=default_name,
        VAX_user_action:=user_rewrite,VAX_ignore_error);
if status(f)>0 then a_open_out:=false
else begin
 linelimit(f,maxint);
 rewrite(f,VAX_ignore_error);
 a_open_out:=status(f)<=0;
 end;
@z

{Section 27}
@x
begin reset(f,name_of_file,'/O'); b_open_in:=reset_OK(f);
@y
begin
open(f,name_of_file,VAX_readonly,VAX_user_action:=user_reset,
        VAX_ignore_error);
if status(f)>0 then b_open_in:=false
else begin
 reset(f,VAX_ignore_error);
 b_open_in:=status(f)<=0;
 end;
@z

{Section 27}
@x
begin rewrite(f,name_of_file,'/O'); b_open_out:=rewrite_OK(f);
@y
begin
open(f,name_of_file,VAX_new,VAX_disposition_delete,
        VAX_default:=default_name,
        VAX_user_action:=user_rewrite,VAX_ignore_error);
if status(f)>0 then b_open_out:=false
else begin
 rewrite(f,VAX_ignore_error);
 b_open_out:=status(f)<=0;
 end;
@z

{Section 27}
@x
begin reset(f,name_of_file,'/O'); w_open_in:=reset_OK(f);
@y
begin
open(f,name_of_file,VAX_readonly,VAX_user_action:=user_reset,
        VAX_ignore_error);
if status(f)>0 then w_open_in:=false
else begin
 reset(f,VAX_ignore_error);
 w_open_in:=status(f)<=0;
 end;
fmt_count:=0;
@z

{Section 27}
@x
begin rewrite(f,name_of_file,'/O'); w_open_out:=rewrite_OK(f);
@y
begin
open(f,name_of_file,VAX_new,VAX_disposition_delete,
        VAX_user_action:=user_rewrite,VAX_ignore_error);
if status(f)>0 then w_open_out:=false
else begin
 rewrite(f,VAX_ignore_error);
 w_open_out:=status(f)<=0;
 end;
fmt_count:=0;
@z

<<<<<Added 25-AUG-1989 by BHK : describe file closure>>>>>
{Section 28}
@x
@ Files can be closed with the \ph\ routine `|close(f)|', which
@^system dependencies@>
should be used when all input or output with respect to |f| has been completed.
This makes |f| available to be opened again, if desired; and if |f| was used for
output, the |close| operation makes the corresponding external file appear
on the user's area, ready to be read.

These procedures should not generate error messages if a file is
being closed before it has been successfully opened.

@p procedure a_close(var f:alpha_file); {close a text file}
begin close(f);
@y
@ Files can be closed with the VAX-\PASCAL\ routine
|close(f,disposition,error)|, which
@^system dependencies@>
should be used when all input or output with respect to |f| has been completed.
This makes |f| available to be opened again, if desired; and if |f| was used for
output, the |close| operation can make the corresponding external file appear
in the user's directory, ready to be read: this depends upon the value of the
|disposition| parameter, which can (\\{inter alia}) control whether the file is
kept or discarded.  If this parameter is not specified, then disposition of the
file is determined by the corresponding parameter of the |open| routine.

It is through this mechanism that we are able to ensure that all output files
are discarded if the operation of \TeX\ is aborted by the user, and yet are kept
if the program terminates correctly.

These procedures should not generate error messages if a file is
being closed before it has been successfully opened; the |error| parameter is
used here to ensure that any such errors do not cause run-time failures.

@d VAX_disposition_save==@=disposition:=save@>

@p procedure a_close(var f:alpha_file); {close a text file}
begin close(f,VAX_disposition_save,VAX_ignore_error);
@z

{Section 28}
@x
begin close(f);
@y
begin close(f,VAX_disposition_save,VAX_ignore_error);
@z

{Section 28}
@x
begin close(f);
@y
begin close(f,VAX_disposition_save,VAX_ignore_error);
@z

{Section 30}
>>>DAH changed size of aux_buf from 133 to size_input_line.
@x [3] read into auxiliary buffer first
representing the beginning and ending of a line of text.

@<Glob...@>=
@y
representing the beginning and ending of a line of text.

On VMS, we will read the lines first into an auxiliary buffer, in
order to save the running time of procedure-call overhead.  We have
to be very careful to handle lines longer than the arbitrarily chosen
length of the |aux_buf|.  This buffer is declared using a VAX-\PASCAL\ extension
for variable length strings, namely the |varying| array type.  Such arrays
actually appear as if they were a record declared with two fields, thus:
|varying [max_size] of = record length: 0..max_size; body: packed array
[1..max_size] of|.

@d VAX_length(#)==#.@=length@>
@d VAX_body(#)==#.@=body@>
@f varying==array

@<Glob...@>=
@!aux_buf:varying [size_input_line] of char; {where the characters go first}
@z

{Section 31}
<<<<<Modified 06-NOV-1989 by BHK : update for V3 beta test>>>>>
@x [3] ditto
of characters at once, if such routines are available. The following
code uses standard \PASCAL\ to illustrate what needs to be done, but
finer tuning is often possible at well-developed \PASCAL\ sites.
@^inner loop@>

@p function input_ln(var f:alpha_file;@!bypass_eoln:boolean):boolean;
  {inputs the next line or returns |false|}
var last_nonblank:0..buf_size; {|last| with trailing blanks removed}
begin if bypass_eoln then if not eof(f) then get(f);
  {input the first character of the line into |f^|}
last:=first; {cf.\ Matthew 19\thinspace:\thinspace30}
if eof(f) then input_ln:=false
else  begin last_nonblank:=first;
  while not eoln(f) do
    begin if last>=max_buf_stack then
      begin max_buf_stack:=last+1;
      if max_buf_stack=buf_size then
        @<Report overflow of the input buffer, and abort@>;
      end;
    buffer[last]:=xord[f^]; get(f); incr(last);
    if buffer[last-1]<>" " then last_nonblank:=last;
    end;
  last:=last_nonblank; input_ln:=true;
  end;
end;
@y
of characters at once, if such routines are available. The following
code uses VAX-\PASCAL\ extensions, such as |varying| strings to perform input of
larger amounts of characters with a single input instruction.
@^inner loop@>

Under VAX-\PASCAL, it is not necessary to take special action to |bypass_eoln|,
since the terminator character will be included in those read into the
|aux_buf|.

@p function input_ln(var f:alpha_file;@!bypass_eoln:boolean):boolean;
  {inputs the next line or returns |false|}
label found;
var @!len:integer; {length of line input}
@!k:0..buf_size; {index into |buffer|}
begin
last:=first; {cf.\ Matthew 19\thinspace:\thinspace30}
if status(f)<>0 then input_ln:=false
else  begin
  while not eoln(f) do
    begin read(f,aux_buf,VAX_ignore_error);
    len:=VAX_length(aux_buf);
    if last+len>=max_buf_stack then
      begin
      if last+len<buf_size then max_buf_stack:=last+len
      else
        @<Report overflow of the input buffer, and abort@>;
      end;
    for k:=last to last+len-1 do buffer[k]:=xord[aux_buf[k-last+1]];
    last:=last+len;
    end;
  found: if last>first then if buffer[last-1]=" " then begin
        decr(last); goto found; end;
  input_ln:=true;
  read_ln(f,VAX_ignore_error);
  end;
end;
@z

<<<<<Modified on 24-AUG-1989 by BHK>>>>>
{Section 33}
@x [3] terminal file opening
@ Here is how to open the terminal files
in \ph. The `\.{/I}' switch suppresses the first |get|.
@^system dependencies@>

@d t_open_in==reset(term_in,'TTY:','/O/I') {open the terminal for text input}
@d t_open_out==rewrite(term_out,'TTY:','/O') {open the terminal for text output}
@y
@ Here is how to open the terminal files
under VMS.
@^system dependencies@>
The standard input and output proces-permanent files \.{SYS\$INPUT} and
\.{SYS\$OUTPUT}
@.SYS{\$}INPUT@>
@.SYS{\$}OUTPUT@>
are opened, and the addresses of the associated |FAB| and |RAB| noted so that
special actions (such as flushing the input buffer) can be coded.

Output occurs without any implicit carriage-control: this
permits the output buffer to be flushed to the terminal without terminating the
line of output; it is necessary to output the carriage-return, line-feed
character pair explicitly when the line is to be terminated.

@d VAX_sys_input==@= 'SYS$INPUT' @>
@d VAX_sys_output==@= 'SYS$OUTPUT' @>
@d VAX_PAS_FAB==@= PAS$FAB@>
@d VAX_PAS_RAB==@= PAS$RAB@>
@d VAX_carriage_control==@= carriage_control @>
@d VAX_none==@= none @>
@#
@d t_open_in==begin open(term_in,VAX_sys_input);
 reset(term_in);
 in_FAB:=VAX_PAS_FAB(term_in);
 in_RAB:=VAX_PAS_RAB(term_in);
 end {open the terminal for text input}
@d t_open_out==begin
 open(term_out,VAX_sys_output,VAX_carriage_control:=VAX_none);
 linelimit(term_out,maxint);
 rewrite(term_out);
 out_FAB:=VAX_PAS_FAB(term_out);
 out_RAB:=VAX_PAS_RAB(term_out);
 end {open the terminal for text output}
@z

<<<<<Modified on 26-SEP-1989 by BHK>>>>>
{Section 34}
@x [3] terminal hacks: clear and update
these operations can be specified in \ph:
@^system dependencies@>

@d update_terminal == break(term_out) {empty the terminal output buffer}
@d clear_terminal == break_in(term_in,true) {clear the terminal input buffer}
@d wake_up_terminal == do_nothing {cancel the user's cancellation of output}
@y
these operations can be specified on VMS in VAX-\PASCAL\ or DEC-\PASCAL,
through manipulation of the data structures maintained in the |RAB|
(Record Access Block)
@^Record Access Block@>
by RMS.
@^RMS@>
@^Record Management Services@>

Since |wake_up_terminal|
is only called just before output of an error message, there's no significant
overhead in its being a procedure, and this saves 8k bytes of \PASCAL\ source
compared with having it as a \.{WEB} definition.
@^system dependencies@>

To prevent spurious empty writes to the terminal in |batch_mode|, we apply a
condition to |update_terminal|.

@d VAX_RAB_purge_typeahead== @=RAB$V_PTA@>
@d VAX_RAB_cancel_ctrlO== @=RAB$V_CCO@>
@#
@d update_terminal == if odd(selector) then write_ln(term_out)
        {empty the terminal output buffer}
@d clear_terminal == in_RAB^.VAX_RAB_purge_typeahead:=true
        {clear the terminal input buffer}
@.PTA@>
@d crlf == chr(13),chr(10)
@#
@<VMS proc...@>=
procedure wake_up_terminal;
begin
  out_RAB^.VAX_RAB_cancel_ctrlO:=true;
  write_ln(term_out);
  out_RAB^.VAX_RAB_cancel_ctrlO:=false;
end; {cancel the user's cancellation of output}
@.CCO@>
@z

<<<<<Modified on 1-DEC-1988 & 24-AUG-1989 by BHK>>>>>
<<<<<Modified 14-NOV-1989 by BHK>>>>>
<<<<<Modified 09-SEP-1994 by SPC>>>>>
<<<<<Modified 19-OCT-1994 by SPC>>>>>
{Section 37}
@x [3] terminal initialization
@ The following program does the required initialization
without retrieving a possible command line.
It should be clear how to modify this routine to deal with command lines,
if the system permits them.
@^system dependencies@>

@p function init_terminal:boolean; {gets the terminal input started}
label exit;
begin t_open_in;
loop@+begin wake_up_terminal; write(term_out,'**'); update_terminal;
@.**@>
  if not input_ln(term_in,true) then {this shouldn't happen}
    begin write_ln(term_out);
    write(term_out,'! End of file on the terminal... why?');
@.End of file on the terminal@>
    init_terminal:=false; return;
    end;
  loc:=first;
  while (loc<last)and(buffer[loc]=" ") do incr(loc);
  if loc<last then
    begin init_terminal:=true;
    return; {return unless the line was all blank}
    end;
  write_ln(term_out,'Please type the name of your input file.');
  end;
exit:end;
@y
@ The following program does the required initialization
by retrieving a possible command line, and if none exists,
prompting the user for the first line of input.

VMS standard library routines are used to ``input'' an initial command line
from the pseudo-qualifier \.{COMMAND\_LINE} of the Command Line Interface.
@.CLI{\$}GET_VALUE@>
@^system dependencies@>

Since any command line passed to \TeX\ from DCL via \.{LIB\$GET\_VALUE} will
have been ``up-cased'', we might consider converting everything to lower-case,
so that all (usually lowercase) \TeX\ commands therein can be recognized;
of course, any such commands which are named with upper-case letters would be
``ruined''.
But, since we are using the full DCL command line interpreter interface,
commands consisting of more than one word have to be enclosed in
`\.{\char'042}' quotation marks, anyway. The correct case is thus
preserved; and file names (which are by far the most common single word
arguments) are case insensitive on VMS.
Therefore, lower case conversion has not been implemented.

@d VAX_cli_present==@= cli$present@>
@d VAX_cli_get_value==@= cli$get_value@>

@p function init_terminal:boolean; {gets the terminal input started}
label exit;
var command_line: packed array[1..256] of char;
@!len: sixteen_bits;
@!i: integer;
begin t_open_in;
if cmd_line_present then begin
  VAX_cli_get_value('COMMAND_LINE',command_line,len);
  i:=1; while (i<=len) and (command_line[i]=' ') do incr(i);
  if i<=len then begin
    loc:=first;
    last:=first;
    while i<=len do begin
        buffer[last]:=xord[command_line[i]];
        incr(last); incr(i);
        end;
    init_terminal:=true; return;
    end;
  end;
loop@+begin wake_up_terminal; write(term_out,'**'); update_terminal;
@.**@>
  if not input_ln(term_in,true) then {this shouldn't happen}
    begin write(term_out,crlf);
    write_ln(term_out,'! End of file on the terminal... why?',crlf);
@.End of file on the terminal@>
    init_terminal:=false; return;
    end;
  loc:=first;
  while (loc<last)and(buffer[loc]=" ") do incr(loc);
  if loc<last then
    begin init_terminal:=true;
    return; {return unless the line was all blank}
    end;
  write_ln(term_out,'Please type the name of your input file.',crlf);
  end;
exit:end;

@z

{Section 49}
>>>>>Added 26-May-1990 DAH--print ASCII characters in [x'a0',x'fe'].
<<<<<Modified 12-Sep-1990 BHK---only print if /EIGHT_BIT qualifier present
@x
Unprintable characters of codes 128--255 are, similarly, rendered
\.{\^\^80}--\.{\^\^ff}.
@y
Unprintable characters of codes 128--159 and 255 are, similarly, rendered
\.{\^\^80}--\.{\^\^9f} and \.{\^\^ff}. We will print characters in the range
160--254 if the qualifier \.{/EIGHT\_BIT} is used.
@./EIGHT_BIT@>
@z

{Section 49}
>>>>>Added 26-May-1990 DAH--print ASCII characters in [x'a0',x'fe'].
<<<<<Modified 12-Sep-1990 BHK---only print if /EIGHT_BIT qualifier present
@x
  (k<" ")or(k>"~")
@y
  (k<" ")or((k>"~")and (k<160))or(k=255)or((k>=160)and not eight_qual)
@z

{Section 51}
@x [4] bad_pool needs real crlf
@ @d bad_pool(#)==begin wake_up_terminal; write_ln(term_out,#);
@y
@ As noted before, it is not necessary for the string |pool_name| to have the
same length as the |array name_of_file|, because VAX-\PASCAL\ automatically
pads such shorter strings with spaces when an assignment is made into a longer
string variable.

@d bad_pool(#)==begin wake_up_terminal; write_ln(term_out,#,crlf);
@z

{Section 54}
<<<<<Added 03-AUG-1995 by SPC: Added item for |edcmd_write|.>>>>>
@x
\hang |new_string|, appends the output to the current string in the
  string pool.
@y
\hang |new_string|, appends the output to the current string in the
  string pool.

\hang |edcmd_write|, prints the characters into the |edcmd_file| only.
@z

{Section 54}
<<<<<Added 03-AUG-1995 by SPC: Added explanation for |edcmd_...|.>>>>>
@x
\noindent The symbolic names `|term_and_log|', etc., have been assigned
numeric codes that satisfy the convenient relations |no_print+1=term_only|,
|no_print+2=log_only|, |term_only+2=log_only+1=term_and_log|.
@y
\noindent The symbolic names `|term_and_log|', etc., have been assigned
numeric codes that satisfy the convenient relations |no_print+1=term_only|,
|no_print+2=log_only|, |term_only+2=log_only+1=term_and_log|.

The interface to various ``callable editors'', described toward the end of
this program, necessitates the use of a separate output file (in addition
to \TeX's 16 auxiliary output streams), |edcmd_file|, which is declared here,
together with the associated value |edcmd_write| of the global |selector|
variable.
@z

{Section 54}
<<<<<Added 03-AUG-1995 by SPC: Added |edcmd_...| declarations.>>>>>
@x
@d max_selector=21 {highest selector setting}

@<Glob...@>=
@!log_file : alpha_file; {transcript of \TeX\ session}
@y
@d edcmd_write=22 {printing is deflected to the |edcmd_file|}
@d max_selector=22 {highest selector setting}

@<Glob...@>=
@!log_file : alpha_file; {transcript of \TeX\ session}
@!edcmd_file : alpha_file; {command file used when invoking some editors}
@z

{Section 56}
<<<<<Modified  5-DEC-1988 by BHK, 17-FEB-1989 by CNK>>>>>
@x [5] real crlf for terminal
by changing |wterm|, |wterm_ln|, and |wterm_cr| in this section.
@^system dependencies@>

@d wterm(#)==write(term_out,#)
@d wterm_ln(#)==write_ln(term_out,#)
@d wterm_cr==write_ln(term_out)
@d wlog(#)==write(log_file,#)
@d wlog_ln(#)==write_ln(log_file,#)
@d wlog_cr==write_ln(log_file)
@y
by changing |wterm|, |wterm_ln|, and |wterm_cr| in this section.
@^system dependencies@>

We also introduce here analogous macros for writing to the |diag_file|, which
is used to generate diagnostic messages for use in conjunction with DEC's
Language-sensitive editor (LSEdit).
@^Language-sensitive editor@>
@^LSE@>

Yet another set of macros is concerned with writing to |temp_file|, which is a
purely internal file, used to concatenate the various elements of \TeX's error
messages for use in diagnostic and other files.

@d wterm(#)==write(term_out,#)
@d wterm_ln(#)==write_ln(term_out,#,crlf)
@d wterm_cr==write_ln(term_out,crlf)
@d wlog(#)==if log_qual then write(log_file,#)
@d wlog_ln(#)==if log_qual then write_ln(log_file,#)
@d wlog_cr==if log_qual then write_ln(log_file)
@d wdiag(#)==if diag_qual then write(diag_file,#)
@d wdiag_ln(#)==if diag_qual then write_ln(diag_file,#)
@d wdiag_cr==if diag_qual then write_ln(diag_file)
@d wtemp(#)==write(temp_file,#)
@d wtemp_ln(#)==write_ln(temp_file,#)
@d wtemp_cr==write_ln(temp_file)

@<Basic print...@>=
procedure diag_char(@!s:ASCII_code);
  var ch : char;
begin
  ch := xchr[s];
  wdiag(ch); if ch='"' then wdiag(ch)
end;

@# procedure temp_char(@!s:ASCII_code);
  var ch : char;
begin
  ch := xchr[s];
  wtemp(ch); if ch='"' then wtemp(ch)
end;

@# procedure diag_print( s : integer);
  var j : pool_pointer;
begin
  j:=str_start[s];
  while j < str_start[s+1] do
  begin diag_char(so(str_pool[j]));
    incr(j)
  end;
end;
@z

{Section 57}
<<<<<Added 03-AUG-1995 by SPC: Added |edcmd_write| clause.>>>>>
@x
no_print,pseudo,new_string: do_nothing;
@y
no_print,pseudo,new_string: do_nothing;
edcmd_write: write_ln(edcmd_file);
@z

{Section 58}
<<<<<Added 7-DEC-1988 by BHK>>>>>
<<<<<Modified 03-AUG-1995 by SPC: Added |edcmd_write| clause.>>>>>
@x
@ The |print_char| procedure sends one character to the desired destination,
using the |xchr| array to map it into an external character compatible with
|input_ln|. All printing comes through |print_ln| or |print_char|.

@<Basic printing...@>=
procedure print_char(@!s:ASCII_code); {prints a single character}
label exit;
begin if @<Character |s| is the current new-line character@> then
 if selector<pseudo then
  begin print_ln; return;
  end;
case selector of
term_and_log: begin wterm(xchr[s]); wlog(xchr[s]);
  incr(term_offset); incr(file_offset);
  if term_offset=max_print_line then
    begin wterm_cr; term_offset:=0;
    end;
  if file_offset=max_print_line then
    begin wlog_cr; file_offset:=0;
    end;
  end;
log_only: begin wlog(xchr[s]); incr(file_offset);
  if file_offset=max_print_line then print_ln;
  end;
term_only: begin wterm(xchr[s]); incr(term_offset);
  if term_offset=max_print_line then print_ln;
  end;
no_print: do_nothing;
pseudo: if tally<trick_count then trick_buf[tally mod error_line]:=s;
new_string: begin if pool_ptr<pool_size then append_char(s);
  end; {we drop characters if the string space is full}
othercases write(write_file[selector],xchr[s])
endcases;@/
incr(tally);
exit:end;
@y
@ The |print_char| procedure sends one character to the desired destination,
using the |xchr| array to map it into an external character compatible with
|input_ln|. All printing comes through |print_ln| or |print_char|.

@<Basic printing...@>=
procedure print_char(@!s:ASCII_code); {prints a single character}
label exit;
begin if @<Character |s| is the current new-line character@> then
 if selector<pseudo then
  begin print_ln; return;
  end;
@<Save printed character for diagnostic messages@>;
case selector of
term_and_log: begin wterm(xchr[s]); wlog(xchr[s]);
  incr(term_offset); incr(file_offset);
  if term_offset=max_print_line then
    begin wterm_cr; term_offset:=0;
    end;
  if file_offset=max_print_line then
    begin wlog_cr; file_offset:=0;
    end;
  end;
log_only: begin wlog(xchr[s]); incr(file_offset);
  if file_offset=max_print_line then print_ln;
  end;
term_only: begin wterm(xchr[s]); incr(term_offset);
  if term_offset=max_print_line then print_ln;
  end;
no_print: do_nothing;
pseudo: if tally<trick_count then trick_buf[tally mod error_line]:=s;
new_string: begin if pool_ptr<pool_size then append_char(s);
  end; {we drop characters if the string space is full}
edcmd_write: write(edcmd_file,xchr[s]); {Copy character to editor command file}
othercases write(write_file[selector],xchr[s])
endcases;@/
incr(tally);
exit:end;
@z

{Section 71}
@x [5] Turn off clearing typeahead after terminal input
term_offset:=0; {the user's line ended with \<\rm return>}
@y
in_RAB^.VAX_RAB_purge_typeahead:=false; {turn off purging of typeahead}
@.PTA@>
term_offset:=0; {the user's line ended with \<\rm return>}
@z

{Section 73}
@x
@ The global variable |interaction| has four settings, representing increasing
amounts of user interaction:
@y
@ The global variable |interaction| has four settings, representing increasing
amounts of user interaction:

This version of \TeX\ can generate a diagnostics file for use with the
\.{REVIEW} mode of DEC's Language-sensitive editor (LSEdit).
@^Language-sensitive editor@>
So whenever \TeX\ starts to generate an error message, we arrange for the text
which is sent to the terminal and/or the transcript file to be copied also into
our |temp_file|.
@z

{Section 73}
@x  <<<<<Added 6-DEC-1988 by BHK>>>>>
@d print_err(#)==begin if interaction=error_stop_mode then wake_up_terminal;
  print_nl("! "); print(#);
  end

@<Glob...@>=
@!interaction:batch_mode..error_stop_mode; {current level of interaction}
@y
@d print_err(#)==begin if interaction=error_stop_mode then wake_up_terminal;
  print_nl("! ");
  copy_err:=save_it; rewrite(temp_file);
  print(#);  {Other |print|s will add to |temp_file|}
  end

@<Glob...@>=
@!interaction:batch_mode..error_stop_mode; {current level of interaction}
@z

{Section 82}
@x <<<<<Added 25-AUG-1989 by BHK>>>>>
@ Here now is the general |error| routine.
@y
@ Here now is the general |error| routine, which completes output of the error
message.
@z

{Section 82}
@x <<<<<Added 9-DEC-1988 by BHK>>>>>
begin if history<error_message_issued then history:=error_message_issued;
print_char("."); show_context;
if interaction=error_stop_mode then @<Get user's advice and |return|@>;
@y
begin if history<error_message_issued then history:=error_message_issued;
print_char(".");
@<Ensure |temp_file| not in use@>;
show_context;
if interaction=error_stop_mode then @<Get user's advice and |return|@>;
@z

{Section 84}
>>>>>Modified 24-MAY-1990 by DAH>>>>>
<<<<<Modified 10-JAN-1990 by BHK>>>>>
@x <<<<<Added 25-AUG-1989 by BHK>>>>>
@ It is desirable to provide an `\.E' option here that gives the user
an easy way to return from \TeX\ to the system editor, with the offending
line ready to be edited. But such an extension requires some system
wizardry, so the present implementation simply types out the name of the
file that should be
edited and the relevant line number.
@^system dependencies@>
@y
@ It is desirable to provide an `\.E' option here that gives the user
an easy way to return from \TeX\ to the system editor, with the offending
line ready to be edited.  This version of \TeX\ invokes callable versions of
various DEC editors, depending upon the value of the
\.{/EDITOR} switch (normally set to \.{TEX\_EDIT}),
@./EDITOR@>
@.TEX_EDIT@>
including \.{EDT}, \.{TPU}, DEC's Language-sensitive editor (LSEdit), and even
@^EDT@>
@^TPU@>
@^Language-sensitive editor@>
@^LSE@>
\.{TECO}.
@^TECO@>
Other editors may be run in a sub-process by setting \.{/EDITOR}
to any DCL command, including activating a command procedure.
In addition, if the \.{/CONTINUE} qualifier is present on the command line,
@./CONTINUE@>
\TeX\ will continue processing after returning from the editor.
@^system dependencies@>
@z

{Section 84}
<<<<<Added 9-DEC-1988 by BHK; Changed 17-FEB-1989 by CNK>>>>>
>>>>>Modified 30-JUN-1990 by DAH>>>>>
<<<<<Modified 26-SEP-1991 by BHK [DEK's now using slow_print]>>>>>
@x
"E": if base_ptr>0 then
  begin print_nl("You want to edit file ");
@.You want to edit file x@>
  slow_print(input_stack[base_ptr].name_field);
  print(" at line "); print_int(line);
  interaction:=scroll_mode; jump_out;
  end;
@y
"E": if base_ptr>0 then
  begin
    if edit_file(input_stack[base_ptr],line) then
    begin
      if continue_qual then
      begin show_context; goto continue;
      end
      else begin interaction:=scroll_mode; jump_out;
      end
    end
    else
    begin
      print_nl("You want to edit file ");
@.You want to edit file x@>
      slow_print(input_stack[base_ptr].name_field);
      print(" at line "); print_int(line);
      interaction:=scroll_mode; jump_out;
    end
  end;
@z

{Section 86}
<<<<<Added 19-OCT-1989 by CNK>>>>>
@x
@<Change the interaction...@>=
begin error_count:=0; interaction:=batch_mode+c-"Q";
print("OK, entering ");
case c of
"Q":begin print_esc("batchmode"); decr(selector);
  end;
"R":print_esc("nonstopmode");
"S":print_esc("scrollmode");
end; {there are no other cases}
print("..."); print_ln; update_terminal; return;
end
@y
@<Change the interaction...@>=
begin error_count:=0; interaction:=batch_mode+c-"Q";
print("OK, entering ");
case c of
"Q":print_esc("batchmode");
"R":print_esc("nonstopmode");
"S":print_esc("scrollmode");
end; {there are no other cases}
print("..."); print_ln; update_terminal;
if c = "Q" then
    decr (selector);
return;
end
@z


{Section 96}
@x
@ Users occasionally want to interrupt \TeX\ while it's running.
If the \PASCAL\ runtime system allows this, one can implement
a routine that sets the global variable |interrupt| to some nonzero value
when such an interrupt is signalled. Otherwise there is probably at least
a way to make |interrupt| nonzero using the \PASCAL\ debugger.
@^system dependencies@>
@^debugging@>
@y
@ Users occasionally want to interrupt \TeX\ while it's running.
By using a VAX system service, we can declare an Asynchronous System Trap (AST)
handler which will be called when the user types \.{Ctrl-C}.  The AST handler
then sets the global variable |interrupt| to some nonzero value
when such an interrupt is signalled.
@^system dependencies@>
Since this variable may be changed at any time, we must prevent the compiler
from applying optimizations to the code related to this variable (for example,
it would not do for it to be held in a machine register), so we give it the
VAX-\PASCAL\ `attribute' \.{volatile}, which is defined at this point.  We also
define a couple of other attributes that may be applied to affect the placement
of variables under VAX-\PASCAL.

Assuming that it's possible to assign an I/O channel to device \.{SYS\$COMMAND},
@.SYS{\$}COMMAND@>
which should be the case provided the program is being run interactively, then
the Control-C handler is declared by a call of the \.{\$QIOW} system service.
@.{\$}QIOW@>
Some parameters for this system service have to passed by the `immediate'
parameter-passing mechanism; we take this opportunity to define all the means
used in \TeX\ to override VAX-\PASCAL's default parameter-passing mechanisms.

@d VAX_volatile==@= volatile @>
@d VAX_unsafe==@= unsafe @>
@d VAX_aligned==@= aligned @>
@d VAX_static==@= static @>
@#
@d VAX_immed==@= %immed @>
@d VAX_stdescr==@= %stdescr @>
@d VAX_ref==@= %ref @>
@#
@d VAX_io_setmode==@= io$_setmode @>
@d VAX_iom_ctrlcast==@= io$m_ctrlcast @>
@#
@d VAX_qiow==@= $qiow@>
@d VAX_assign==@= $assign@>
@#
@z

{Section 96}
@x [6] interrupts
@d check_interrupt==begin if interrupt<>0 then pause_for_instructions;
  end

@<Global...@>=
@!interrupt:integer; {should \TeX\ pause for instructions?}
@y
@d check_interrupt==begin if interrupt<>0 then pause_for_instructions;
  end
@d enable_control_C==
VAX_qiow(,tt_chan,VAX_io_setmode+VAX_iom_ctrlcast,,,,
        VAX_immed ctrlc_rout,,VAX_immed 3,,,);

@<Global...@>=
@!interrupt: [VAX_volatile] integer; {should \TeX\ pause for instruction?}
@z

{Section 97}
@x
interrupt:=0; OK_to_interrupt:=true;
@y
interrupt:=0; OK_to_interrupt:=true;
if VAX_assign('SYS$COMMAND',tt_chan,,)=VAX_ss_normal then enable_control_C;
@z

{Section 109}
@x [7] double precision reals
@d set_glue_ratio_zero(#) == #:=0.0 {store the representation of zero ratio}
@d set_glue_ratio_one(#) == #:=1.0 {store the representation of unit ratio}
@d float(#) == # {convert from |glue_ratio| to type |real|}
@d unfloat(#) == # {convert from |real| to type |glue_ratio|}
@d float_constant(#) == #.0 {convert |integer| constant to |real|}

@<Types...@>=
@!glue_ratio=real; {one-word representation of a glue expansion factor}
@y
On VMS, we use some hackery to cause floating point numbers stored in
|mem| to be |single|, but other |real| variables and expressions are
done as |double| length reals.

@d set_glue_ratio_zero(#) == #:=0.0 {store the representation of zero ratio}
@d set_glue_ratio_one(#) == #:=1.0 {store the representation of unit ratio}
@d real == double {use double precision reals for computation}
@d float(#) == dble(#) {convert from |glue_ratio| to type |real|}
{FIX ME}
@d unfloat(#) == sngl(1.0@&D0 * #) {convert from |real| to type |glue_ratio|}
@d float_constant(#) == #.0@&D0 {convert |integer| constant to |real|}

@<Types...@>=
@!glue_ratio=r@&e@&a@&l; {one-word representation of a glue expansion factor}
@z

{Section 110}
<<<<<Added 05-SEP-1989 by BHK <tex@cran.rmcs>: BIGTeX changes to match Adrian's
>>>>>Modified 05-JUN-1991 by SPC :
     set |max_quarterword| to $2^9-1$ to allow the big german hyphenation
     patterns in GHYPHEN.MAX to be used (more than 255 ops)>>>>>
@x
@d min_quarterword=0 {smallest allowable value in a |quarterword|}
@d max_quarterword=255 {largest allowable value in a |quarterword|}
@d min_halfword==0 {smallest allowable value in a |halfword|}
@d max_halfword==65535 {largest allowable value in a |halfword|}
@y
@d min_quarterword=0 {smallest allowable value in a |quarterword|}
@d max_quarterword=511 {largest allowable value in a |quarterword|}
@d min_halfword==0 {smallest allowable value in a |halfword|}
@d max_halfword==327144+1 {largest allowable value in a |halfword|}
@z

{Section 112}
@x
@d qi(#)==#+min_quarterword
  {to put an |eight_bits| item into a quarterword}
@d qo(#)==#-min_quarterword
  {to take an |eight_bits| item out of a quarterword}
@d hi(#)==#+min_halfword
  {to put a sixteen-bit item into a halfword}
@d ho(#)==#-min_halfword
  {to take a sixteen-bit item from a halfword}
@y
@d qi(#)==#
@d qo(#)==#
@d hi(#)==#
@d ho(#)==#
@z

{Section 113}
<<<<<Added 10-APR-1995 by SPC: pack |memory_word| record.>>>>>
@x [8] Force byte alignment of |memory_word| arrays, even on AXP.
@!memory_word = record@;@/
  case four_choices of
  1: (@!int:integer);
  2: (@!gr:glue_ratio);
  3: (@!hh:two_halves);
  4: (@!qqqq:four_quarters);
  end;
@y
@!memory_word = packed record@;@/
  case four_choices of
  1: (@!int:integer);
  2: (@!gr:glue_ratio);
  3: (@!hh:two_halves);
  4: (@!qqqq:four_quarters);
  end;
@z

{Section 113}
@x [8] block up word files
@!word_file = file of memory_word;
@y
@!word_block = packed array[0..VAX_block_length-1] of memory_word;
@!word_file = packed file of word_block;
@z

{Section 186}
@x [12] check glue ratio for REALness
arbitrary random value. The following code assumes that a properly
formed nonzero |real| number has absolute value $2^{20}$ or more when
it is regarded as an integer; this precaution was adequate to prevent
floating point underflow on the author's computer.
@y
arbitrary random value. The following WEB macro simulates VAX/DEC-\PASCAL's
predeclared routine |undefined|, which returns |true| if its argument
is not a properly constituted |real| number. (The real function cannot
be used, because it is currently unsupported on DEC-\PASCAL\ for AXP.)

@d VAX_undefined(#)== (#::VAX_F_float.Sign and (#::VAX_F_float.Expo = 0))
@z
<<<<<
===============================================================================
This section was originally changed into
>>>
arbitrary random value. The following code uses the VMS predeclared
routine |undefined|, which returns |true| if its argument is not a properly
constituted |real| number.

@d VAX_undefined==@= undefined@>
<<<
but the AXP DEC PASCAL compilers up to now (Sep 1994) do not support
the predeclared "undefined" function. (Early versions of DEC PASCAL for
OpenVMS(AXP) aborted with a fatal error when encountering the
"undefined" predeclared function!). This is a documented compiler bug!!
Therefore, the check for a reserved floating point operand is done
"by hand":

  The real number is casted to a packed record type made
  up by the functional bit fields representig f_floating data in memory.
  The data fields are then checked for the "reserved operand" pattern.

  The type declaration of the VAX_F_float record has been inserted into
  the first of the @<Types...> sections in the system dependant part at
  the end of the WEB source. Look for the copy of:
>>>
@#
@!VAX_F_float = packed record    {Bit pattern layout of F-Floating Reals}
                @!Frac1 : 0..127;     {the 7 MSBits of the mantissa}
                @!Expo  : 0..255;     {8 bit exponent}
                @!Sign  : boolean;    {1 sign bit}
                @!Frac  : 0..65535;   {the 16 lower bits of the mantissa}
                end;
<<<

BEWARE: This macro is specific to the F-Floating format for REAL numbers!
  This implies that TeX must not be compiled in IEEE_FLOAT mode on AXP !!!

-------------------------------------------------------------------------------
An alternative would be to issue an explicit call to the undocumented
specific runtime library function "pas$undefined_f_float":

  To archive this, replace the definition of VAX_undefined with:

  "@d VAX_undefined==@= pas$undefined_f_float@>"

  Additionally, the RTL routine has to be declared as external function.
  The declaration should be added to the first occurrence of the
  <VMS procedures> module in the "system dependencies" section (below, at the
  end of the change file):

[VAX_external] function VAX_undefined(f_num:single) : boolean;
        @/extern;@;@t\2@>@#

This workaround should be removed as soon as DEC fixes the PASCAL compiler!
===============================================================================
>>>>>


{Section 186}
@x
  if abs(mem[p+glue_offset].int)<@'4000000 then print("?.?")
@y
  if VAX_undefined(glue_set(p)) then print("?.?")
@z

{Section 232}
@x [17] Special form_feed initialization:
cat_code("\"):=escape; cat_code("%"):=comment;
@y
cat_code("\"):=escape; cat_code("%"):=comment;
cat_code(form_feed):=car_ret;
@z

{Section 241}
@x [17] date/time
Since standard \PASCAL\ cannot provide such information, something special
is needed. The program here simply specifies July 4, 1776, at noon; but
users probably want a better approximation to the truth.

@p procedure fix_date_and_time;
begin time:=12*60; {minutes since midnight}
day:=4; {fourth day of the month}
month:=7; {seventh month of the year}
year:=1776; {Anno Domini}
@y
The requisite information is obtained through a call of the \.{\$NUMTIM} system
@.{\$}NUMTIM@>
service.

@d VAX_numtim==@= $numtim@>

@p procedure fix_date_and_time;
var t:array[1..7] of signed_halfword; {raw year, month, day and time}
begin VAX_numtim(t);
year:=t[1]; month:=t[2]; day:=t[3];
time:=t[4]*60+t[5]; {minutes since midnight}
@z

{Section 311}
@x  <<<<<Added 7-DEC-1988 by BHK>>>>>
@<Local variables for formatting calculations@>@/
begin base_ptr:=input_ptr; input_stack[base_ptr]:=cur_input;
  {store current state}
@y
@<Local variables for formatting calculations@>@/
begin base_ptr:=input_ptr; input_stack[base_ptr]:=cur_input;
  {store current state}
  @<Commence an LSE diagnostic report@>;
@z

{Section 311}
@x  <<<<<Added 6-DEC-1988 by BHK>>>>>
done: cur_input:=input_stack[input_ptr]; {restore original state}
end;
@y
done: cur_input:=input_stack[input_ptr]; {restore original state}
  @<Terminate an LSE diagnostic report@>;
end;
@z

{Section 313}
@x  <<<<<Added 7-DEC-1988 by BHK>>>>>
@<Print location of current line@>=
if name<=17 then
  if terminal_input then
    if base_ptr=0 then print_nl("<*>") else print_nl("<insert> ")
  else  begin print_nl("<read ");
    if name=17 then print_char("*")@+else print_int(name-1);
@.*\relax@>
    print_char(">");
    end
@y
@<Print location of current line@>=
if name<=17 then
begin
  @<Output the location to the diagnostics file@>;
  if terminal_input then
    if base_ptr=0 then print_nl("<*>") else print_nl("<insert> ")
  else  begin print_nl("<read ");
    if name=17 then print_char("*")@+else print_int(name-1);
@.*\relax@>
    print_char(">");
    end;
  @<Prepare to copy tokens to the diagnostic region@>;
end
@z

{Section 313}
@x  <<<<<Added 7-DEC-1988 by BHK>>>>>
else  begin print_nl("l."); print_int(line);
  end;
@y
else  begin print_nl("l."); print_int(line);
  @<Report location within source file to diagnostics@>;
  end;
@z

{Section 314}
@x  <<<<<Added 7-DEC-1988 by BHK>>>>>
@ @<Print type of token list@>=
case token_type of
parameter: print_nl("<argument> ");
@y
@ @<Print type of token list@>=
@<Include token report within diagnostics file@>;
case token_type of
parameter: print_nl("<argument> ");
@z

{Section 314}
@x  <<<<<Added 7-DEC-1988 by BHK>>>>>
mark_text: print_nl("<mark> ");
write_text: print_nl("<write> ");
othercases print_nl("?") {this should never happen}
endcases
@y
mark_text: print_nl("<mark> ");
write_text: print_nl("<write> ");
othercases print_nl("?") {this should never happen}
endcases;
@<Prepare to copy tokens to the diagnostic region@>
@z

{Section 317}
@x  <<<<<Added 7-DEC-1988 by BHK>>>>>
@<Print two lines using the tricky pseudoprinted information@>=
if trick_count=1000000 then set_trick_count;
  {|set_trick_count| must be performed}
if tally<trick_count then m:=tally-first_count
else m:=trick_count-first_count; {context on line 2}
if l+first_count<=half_error_line then
  begin p:=0; n:=l+first_count;
  end
else  begin print("..."); p:=l+first_count-half_error_line+3;
  n:=half_error_line;
  end;
for q:=p to first_count-1 do print_char(trick_buf[q mod error_line]);
print_ln;
for q:=1 to n do print_char(" "); {print |n| spaces to begin line~2}
if m+n<=error_line then p:=first_count+m else p:=first_count+(error_line-n-3);
for q:=first_count to p-1 do print_char(trick_buf[q mod error_line]);
if m+n>error_line then print("...")
@y
@<Print two lines using the tricky pseudoprinted information@>=
if trick_count=1000000 then set_trick_count;
  {|set_trick_count| must be performed}
if tally<trick_count then m:=tally-first_count
else m:=trick_count-first_count; {context on line 2}
if l+first_count<=half_error_line then
  begin p:=0; n:=l+first_count;
  end
else  begin print("..."); p:=l+first_count-half_error_line+3;
  n:=half_error_line;
  end;
for q:=p to first_count-1 do print_char(trick_buf[q mod error_line]);
@<Split the context line display@>;
if m+n<=error_line then p:=first_count+m else p:=first_count+(error_line-n-3);
for q:=first_count to p-1 do print_char(trick_buf[q mod error_line]);
if m+n>error_line then print("...");
@<Indicate offending character@>
@z

{Section 318}
@x  <<<<<Added 7-DEC-1988 by BHK>>>>>
@<Pseudoprint the line@>=
begin_pseudoprint;
if buffer[limit]=end_line_char then j:=limit
else j:=limit+1; {determine the effective end of the line}
if j>0 then for i:=start to j-1 do
  begin if i=loc then set_trick_count;
  print(buffer[i]);
  end
@y
@<Pseudoprint the line@>=
begin_pseudoprint;
if buffer[limit]=end_line_char then j:=limit
else j:=limit+1; {determine the effective end of the line}
if j>0 then for i:=start to j-1 do
  begin if i=loc then set_trick_count;
  print(buffer[i]);
  end;
if name<=17 then @<Copy remainder of context to diagnostic file@>
@z

{Section 319}
@x  <<<<<Added 7-DEC-1988 by BHK>>>>>
@ @<Pseudoprint the token list@>=
begin_pseudoprint;
if token_type<macro then show_token_list(start,loc,100000)
else show_token_list(link(start),loc,100000) {avoid reference count}
@y
@ @<Pseudoprint the token list@>=
begin_pseudoprint;
if token_type<macro then show_token_list(start,loc,100000)
else show_token_list(link(start),loc,100000); {avoid reference count}
@<Copy remainder of context to diagnostic file@>;
@z

{Section 416} Correct typo in index entry for \\badness
<<<<<Added 14-NOV-1989 by BHK>>>>>
<<<<<Deleted 10-JAN-1990 by BHK>>>>>
Originally mistyped as:
@!@:badness_}{\.{{\\badness} primitive@>
and corrected to:
@!@:badness_}{\.{\\badness} primitive@>

{Section 513}
>>>>>modified 25-MAY-1990 by DAH>>>>>
@x [28] file names and default areas
following structure:  If the name contains `\.>' or `\.:', the file area
consists of all characters up to and including the final such character;
otherwise the file area is null.  If the remaining file name contains
`\..', the file extension consists of all such characters from the first
remaining `\..' to the end, otherwise the file extension is null.
@^system dependencies@>

We can scan such file names easily by using two global variables that keep track
of the occurrences of area and extension delimiters:
@y
following structure:  If the name contains `\.>', `\.]' or `\.:', the directory
consists of all characters up to and including the final such character;
otherwise the directory is null.  If the remaining file name contains
`\..', the file extension consists of all such characters from the first
remaining `\..' to the end, otherwise the file extension is null.
@^system dependencies@>

We can scan such file names easily by using two global variables that keep track
of the occurrences of area and extension delimiters:

We also take this opportunity to add some more global definitions for an
upcoming section.
@z

{Section 513}
@x
@!area_delimiter:pool_pointer; {the most recent `\.>' or `\.:', if any}
@y
@!area_delimiter:pool_pointer; {the most recent `\.>', `\.]' or `\.:', if any}
@!TEX_area, @!TEX_font_area:str_number;
@!inp_name, @!fonts_name : packed array[1..file_name_size] of char;
@!inp_len, @!fonts_len : file_size;
@!i: integer;
@z

{Section 514}
>>>>>Modified 25-MAY-1990 Don Hosek>>>>>
@x [28] system logical names
@d TEX_area=="TeXinputs:"
@.TeXinputs@>
@d TEX_font_area=="TeXfonts:"
@.TeXfonts@>
@y

For VMS, we handle setting the names for these two logicals by defining them in
the \.{CLD} file. We'll deal with getting that information later. The code that
follows here will be executed after we've read the command line and \.{CLD}
file and assumes that the first |inp_len| characters of |inp_name| are the
value to use for |TEX_area| and the first |fonts_len| characters of
|fonts_name| are the value to use for |TEX_font_area|. This code must be
inserted just after \TeX\ has initialized all of its strings but before its
first attempt at reading a file.

I apologize for adding |i| to the globals above, by the way. There's only so
much searching for a global in \.{TEX.WEB} one can take.
@^frustration@>

@<Read the values of \.{/TEXINPUTS} and \.{/TEXFONTS} and put them
  into the string pool@>=
VAX_cli_get_value('TEXINPUTS',inp_name,inp_len);
str_room(inp_len); for i:=1 to inp_len do append_char(xord[inp_name[i]]);
TEX_area:=make_string;
@#
VAX_cli_get_value('TEXFONTS',fonts_name,fonts_len);
str_room(fonts_len); for i:=1 to fonts_len do append_char(xord[fonts_name[i]]);
TEX_font_area:=make_string

@z

{Section 516}
<<<<<Modified 13-DEC-1989 by BHK [DEK in TeXhax V89 #110]>>>>>
<<<<<Modified 10_JAN-1990 by BHK [Bug 339 changes now in tex.web]>>>>>
<<<<<Modified 19-OCT-1994 by SPC : added `?' -> `%' conversion>>>>>
<<<<<Modified 24-OCT-1994 by SPC : repaired bug in last modification>>>>>
@x [28] directories delimited with square brackets
@ And here's the second. The string pool might change as the file name is
being scanned, since a new \.{\\csname} might be entered; therefore we keep
|area_delimiter| and |ext_delimiter| relative to the beginning of the current
string, instead of assigning an absolute address like |pool_ptr| to them.
@^system dependencies@>

@p function more_name(@!c:ASCII_code):boolean;
begin if c=" " then more_name:=false
else  begin str_room(1); append_char(c); {contribute |c| to the current string}
  if (c=">")or(c=":") then
    begin area_delimiter:=cur_length; ext_delimiter:=0;
    end
  else if (c=".")and(ext_delimiter=0) then ext_delimiter:=cur_length;
  more_name:=true;
  end;
end;
@y
@ And here's the second.  The string pool might change as the file name is
being scanned, since a new \.{\\csname} might be entered; therefore we keep
|area_delimiter| and |ext_delimiter| relative to the beginning of the current
string, instead of assigning an absolute address like |pool_ptr| to them.
@^system dependencies@>

On VMS, we support wildcards in filenames. But, since the single char
wildcard `\.\%' is interpreted as comment delimiter in the \TeX\ input
stream, the question mark `\.?' should be used for that purpose, instead.
Here, we have to convert those `\.?' characters back to `\.\%'.

@p function more_name(@!c:ASCII_code):boolean;
begin if c=" " then more_name:=false
else  begin str_room(1);  {check for enough string space to add one char}
  if (c="?") then
    c:="%";  {convert `\.?' ``single char wildcard'' into `\.\%'}
  append_char(c); {contribute |c| to the current string}
  if (c=">")or(c="]")or(c=":") then
    begin area_delimiter:=cur_length; ext_delimiter:=0;
    end
  else if (c=".")and(ext_delimiter=0) then
    ext_delimiter:=cur_length;
  more_name:=true;
  end;
end;
@z

{Section 517}
<<<<<Modified 13-NOV-1989 by BHK>>>>>
<<<<<Modified 13-DEC-1989 by BHK [DEK in TeXhax V89 #110]>>>>>
<<<<<Modified 10_JAN-1990 by BHK [Bug 339 changes now in tex.web]>>>>>
<<<<<Modified 24-OCT-1994 by SPC : area_delimiter is relative to str_start>>>>>
<<<<<Modified 06-APR-1995 by SPC : removed unused local variables>>>>>
@x [29] Logical name translation:
@ The third.
@^system dependencies@>

@p procedure end_name;
begin if str_ptr+3>max_strings then
  overflow("number of strings",max_strings-init_str_ptr);
@:TeX capacity exceeded number of strings}{\quad number of strings@>
if area_delimiter=0 then cur_area:=""
else  begin cur_area:=str_ptr;
  str_start[str_ptr+1]:=str_start[str_ptr]+area_delimiter; incr(str_ptr);
  end;
if ext_delimiter=0 then
  begin cur_ext:=""; cur_name:=make_string;
  end
else  begin cur_name:=str_ptr;
  str_start[str_ptr+1]:=str_start[str_ptr]+ext_delimiter-area_delimiter-1;
  incr(str_ptr); cur_ext:=make_string;
  end;
end;
@y
@ The third.  We have to check to see if a logical name has been
referred to, and if so, translate it.
@^system dependencies@>

@p procedure end_name;
label restart,exit;
var
@!t:packed array[1..file_name_size] of char;
@!i:pool_pointer;
@!len:signed_halfword;
begin
restart:
if (str_pool[str_start[str_ptr]+area_delimiter]=si(":")) and @|
   (pool_ptr=str_start[str_ptr]+area_delimiter+1) then
  begin
  cur_area:=make_string;
  len:=length(cur_area)-1; {don't include the colon}
  for i:=1 to len do
        t[i]:=xchr[so(str_pool[str_start[cur_area]+i-1])];
  if not translate(t,len) then begin
    cur_ext:=""; cur_name:=""; {silly case}
    return;
    end;
  flush_string; {needn't remember logical name in |cur_area|}
  begin_name;
  for i:=1 to len do
    if not more_name(xord[t[i]]) then goto restart;
  goto restart; {heavy!}
  end;
if str_ptr+3>max_strings then
  overflow("number of strings",max_strings-init_str_ptr);
@:TeX capacity exceeded number of strings}{\quad number of strings@>
if area_delimiter=0 then cur_area:=""
else  begin cur_area:=str_ptr;
  str_start[str_ptr+1]:=str_start[str_ptr]+area_delimiter; incr(str_ptr);
  end;
if ext_delimiter=0 then
  begin cur_ext:=""; cur_name:=make_string;
  end
else  begin cur_name:=str_ptr;
  str_start[str_ptr+1]:=str_start[str_ptr]+ext_delimiter-area_delimiter-1;
  incr(str_ptr); cur_ext:=make_string;
  end;
exit:
end;
@z

{Section 520}
@x [28] system logical names
@d format_default_length=20 {length of the |TEX_format_default| string}
@d format_area_length=11 {length of its area part}
@d format_ext_length=4 {length of its `\.{.fmt}' part}
@d format_extension=".fmt" {the extension, as a \.{WEB} constant}

@<Glob...@>=
@!TEX_format_default:packed array[1..format_default_length] of char;

@y

We want to be able to load the name of the area where formats live from an
argument on the command line (actually, this will work like the \.{/TEXINPUTS}
and \.{/TEXFONTS} qualifiers mentioned above, except that here, the qualifier
will be read by both \TeX\ and \.{INITEX}. Also, things are a little simpler
since we don't deal with the string pool ourselves. On the other hand some
\.{WEB} constants will be changed to variables since we cannot know at compile
time how long the argument to \.{/TEXFORMATS} will be.
@./TEXFORMATS@>
We also will take this opportunity to set the value for |pool_name| since we
need the value given by \.{/TEXFORMATS} to construct it.

@d format_name_length=9 {length of |'plain.fmt'|}
@d format_ext_length=4 {length of its `\.{.fmt}' part}
@d format_extension=".fmt" {the extension, as a \.{WEB} constant}
@d pool_name_length==@= length@>(pool_f_name)

@<Glob...@>=
@!TEX_f_name_default:packed array[1..format_name_length] of char;
   {abbreviated name for conflict considerations}
@!TEX_format_default:packed array[1..file_name_size] of char;
@!pool_name:packed array[1..file_name_size] of char;
@!format_area_length:file_size; {length of the area part}
@!format_default_length:integer; {length of the whole mess upon construction}
@z

{Section 521}
@x [28] system logical names
TEX_format_default:='TeXformats:plain.fmt';
@.TeXformats@>
@y
TEX_f_name_default:='plain.fmt';
VAX_cli_get_value('TEXFORMATS',TEX_format_default,format_area_length);
pool_name:=TEX_format_default;
for i:=1 to pool_name_length do
   pool_name[i+format_area_length]:=pool_f_name[i];
for i:=1 to format_name_length do
  TEX_format_default[i+format_area_length]:=TEX_f_name_default[i];
format_default_length:=format_area_length+format_name_length;
@z

{Section 522}
@x >>>>>Added on 25-MAY-1990 by DAH>>>>>
@ @<Check the ``constant'' values for consistency@>=
if format_default_length>file_name_size then bad:=31;
@y
@ There used to be a consistency check here, but since the value it checked
wouldn't be set until {\it after\/} the consistency checking, we've deleted it.
Besides, our code will automatically guarantee consistancy simply by the way
|TEX_format_default| is defined.
@z

{Section 525}
@x <<<<<Added on 25-AUG-1989 by BHK>>>>>
@ Operating systems often make it possible to determine the exact name (and
possible version number) of a file that has been opened. The following routine,
which simply makes a \TeX\ string from the value of |name_of_file|, should
ideally be changed to deduce the full name of file~|f|, which is the file
most recently opened, if it is possible to do this in a \PASCAL\ program.
@^system dependencies@>
@y
@ The VMS operating system is able to determine the exact name (and version
number) of a file that has been opened through use of the |user_action|
parameter of the |open| routine. The following routine makes a \TeX\ string from
the value of |last_name[1..last_length]|, which is the full specification of the
most recently opened file.
@^system dependencies@>
@z

{Section 525}
<<<<<Modified on 24-AUG-1989 by BHK>>>>>
@x [28] get file name from system
@p function make_name_string:str_number;
var k:1..file_name_size; {index into |name_of_file|}
begin if (pool_ptr+name_length>pool_size)or(str_ptr=max_strings)or
 (cur_length>0) then
  make_name_string:="?"
else  begin for k:=1 to name_length do append_char(xord[name_of_file[k]]);
  make_name_string:=make_string;
  end;
end;
@y
@p function make_name_string:str_number;
var k:1..file_name_size; {index into |name_of_file|}
begin if (pool_ptr+last_length>pool_size)or(str_ptr=max_strings)or
 (cur_length>0) then
  make_name_string:="?"
else  begin for k:=1 to last_length do append_char(xord[last_name[k]]);
  make_name_string:=make_string;
  end;
end;
@z

{Section 529}
@x <<<<<Added 25-AUG-1988 by BHK>>>>>
@ Here is a routine that manufactures the output file names, assuming that
|job_name<>0|. It ignores and changes the current settings of |cur_area|
and |cur_ext|.
@y
@ Here is a routine that manufactures the output file names, assuming that
|job_name<>0|. It ignores and changes the current settings of |cur_area|
and |cur_ext|.

Similarly, |pack_default_name| extracts a (possibly partial) file specification
from the appropriate command line qualifier, if used, and creates a string which
may be used to provide defaults for part of a file specification when opening
certain auxiliary files.  The routine |clear_default_name| is also provided to
ensure that no defaults are applied on successive calls of |open|.
@z

{Section 529}
<<<<<Modified 06-NOV-1989 by BHK : update for V3 beta test>>>>>
@x
@p procedure pack_job_name(@!s:str_number); {|s = ".log"|, |".dvi"|, or
  |format_extension|}
begin cur_area:=""; cur_ext:=s;
cur_name:=job_name; pack_cur_name;
end;
@y
@p procedure pack_job_name(@!s:str_number); {|s = ".lis"|, |".dvi"|, |".dia"|,
  |format_extension|}
begin cur_area:=""; cur_ext:=s;
cur_name:=job_name; pack_cur_name;
end;@#

function pack_default_name(qual : boolean;
               df_name : packed array [l1..u1:integer] of char;
               df_len  : file_size) : boolean;
  var k : integer;
begin
  for k:=1 to file_name_size do default_name[k] := name_of_file[k];
  deflt_length:=name_length;
  if qual then
  begin
    name_of_file := df_name;
    name_length := df_len;
    if name_length < file_name_size then
      for k:=name_length+1 to file_name_size do name_of_file[k]:=' ';
  end;
  pack_default_name := qual;  {Result is whether file wanted}
end;@#

procedure clear_default_name;
  var k : integer;
begin
  for k:=1 to file_name_size do default_name[k]:=' ';
end;
@z

{Section 530}
@x  <<<<<Added 4-MAY-1989 by BHK>>>>>
@ If some trouble arises when \TeX\ tries to open a file, the following
routine calls upon the user to supply another file name. Parameter~|s|
is used in the error message to identify the type of file; parameter~|e|
is the default extension if none is given. Upon exit from the routine,
variables |cur_name|, |cur_area|, |cur_ext|, and |name_of_file| are
ready for another attempt at file opening.
@y
@ If some trouble arises when \TeX\ tries to open a file, the following
routine calls upon the user to supply another file name. Parameter~|s|
is used in the error message to identify the type of file; parameter~|e|
is the default extension if none is given. Upon exit from the routine,
variables |cur_name|, |cur_area|, |cur_ext|, and |name_of_file| are
ready for another attempt at file opening.

Because this procedure invokes the |print_err| macro, but does not terminate
the ``error'' (by invoking |error|), we have to take special measures to
prevent everything from here onwards being written to the |temp_file| used for
diagnostics.  It does this by resetting the |temp_file|.
@z

{Section 530}
@x  <<<<<Added 4-MAY-1989 by BHK>>>>>
clear_terminal; prompt_input(": "); @<Scan file name in the buffer@>;
@y
clear_terminal; prompt_input(": "); @<Ensure |temp_file| not in use@>;
@<Scan file name in the buffer@>;
@z

{Section 532}
<<<<<Added 6-DEC-1988 by BHK>>>>>
<<<<<Modified 23-NOV-1994 by SPC: always call |clear_default_name| >>>>>
@x
@d ensure_dvi_open==if output_file_name=0 then
  begin if job_name=0 then open_log_file;
  pack_job_name(".dvi");
  while not b_open_out(dvi_file) do
    prompt_file_name("file name for output",".dvi");
  output_file_name:=b_make_name_string(dvi_file);
  end
@y
@d ensure_dvi_open==if output_file_name=0 then
  begin if job_name=0 then open_log_file;
    pack_job_name(".dvi");
    if pack_default_name(dvi_qual,dvif_name,dvif_len) then
    begin
      while not b_open_out(dvi_file) do
        prompt_file_name("file name for output",".dvi");
      output_file_name:=b_make_name_string(dvi_file);
    end else
      output_file_name:=".";
    clear_default_name;
  end
@z

{Section 534}
@x  <<<<<Added 6-DEC-1988 by BHK>>>>>
@ The |open_log_file| routine is used to open the transcript file and to help
it catch up to what has previously been printed on the terminal.

@p procedure open_log_file;
@y
@ The |open_log_file| routine is used to open the transcript file and to help
it catch up to what has previously been printed on the terminal.

@p @<Declare the |open_diag_file| procedure@>
@#
procedure open_log_file;
@z

{Section 534}
<<<<<Added 5-DEC-1988 by BHK>>>>>
<<<<<Modified 23-NOV-1994 by SPC: always call |clear_default_name| >>>>>
@x
if job_name=0 then job_name:="texput";
@.texput@>
pack_job_name(".log");
while not a_open_out(log_file) do @<Try to get a different log file name@>;
log_name:=a_make_name_string(log_file);
@y
if job_name=0 then job_name:="texput";
@.texput@>
pack_job_name(".lis");
if pack_default_name(log_qual,logf_name,logf_len) then
begin
  while not a_open_out(log_file) do @<Try to get a different log file name@>;
  log_name:=a_make_name_string(log_file);
end else
  log_name:=".";
clear_default_name;
open_diag_file;
@z

{Section 535}
@x
prompt_file_name("transcript file name",".log");
@y
prompt_file_name("transcript file name",".lis");
@z

{Section 537}
@x <<<<<Added 25-AUG-1988 by BHK>>>>>
@ Let's turn now to the procedure that is used to initiate file reading
when an `\.{\\input}' command is being processed.
@y
@ Let's turn now to the procedure that is used to initiate file reading
when an `\.{\\input}' command is being processed.

As originally used by \TeX82 under VMS, this procedure discarded the current
file name (as returned to it by the operating system) after it had been printed.
However, with this version of \TeX, with its capability of writing diagnostic
files for use by LSEdit's review mode, we need to be able to report the full
file specifications of any files that may be involved in |show_context|;
therefore, we do not call |flush_string| here.
@z

{Section 537}
<<<<<Added 03-JUN-1994 by SPC : new |x_open_in| with TEX_area lookup>>>>>
<<<<<Modified 19-OCT-1994 by SPC : added file name expansion for |job_name|>>>>>
@x

@p procedure start_input; {\TeX\ will \.{\\input} something}
label done;
begin scan_file_name; {set |cur_name| to desired file name}
@y

Here, |x_open_in|, a modified version of |a_open_in|, is inserted.
This function is located here (and not next to |a_open_in|) to eliminate
the neccessity of a forward declaration for the |pack_file_name| function.

The new function |x_open_in| is intended to replace the calls to
|a_open_in| in the procedures |start_input| and |open_or_close_in|.
It extends the file opening functionality of |a_open_in| by
a second try to open the file using the |TEX_area| path specification,
when no explicit path was specified. The code needed
for this purpose has been moved out of the |start_input| procedure.
This change unifies the file search behaviour of the \.{\\input} and
\.{\\openin} commands, a useful modification of \TeX\ recommended by the
new edition of the \LaTeX\ macro package.
The original function |a_open_in| is still used to read the \TeX\ pool file.

When |start_input| decides to initiate the transcript file (this can only
happen when |start_input| is called the first time!), the |cur_name| string
get updated with the contents of |last_basename|. This string is the base
name part of the file opened the previous |x_open_in| call. This modification
reveals the correct job name when the primary input file specification
contained wildcard characters in the base name part.

@p function x_open_in(var f:alpha_file):boolean;
begin
open(f,name_of_file,VAX_readonly,VAX_user_action:=user_reset,
        VAX_ignore_error);
if status(f)>0 then begin
  if cur_area="" then begin
    pack_file_name(cur_name,TEX_area,cur_ext);
    open(f,name_of_file,VAX_readonly,VAX_user_action:=user_reset,
         VAX_ignore_error);
    end;
  end;
if status(f)>0 then x_open_in:=false
else begin
 reset(f,VAX_ignore_error);
 x_open_in:=status(f)<=0;
 end;
end;
@#
procedure start_input; {\TeX\ will \.{\\input} something}
label done;
var k:integer;
begin scan_file_name; {set |cur_name| to desired file name}
@z

{Section 537}
<<<<<Added 03-JUN-1994 by SPC: Moved |TEX_area| search to |x_open_in|.>>>>>
@x
pack_cur_name;
loop@+  begin begin_file_reading; {set up |cur_file| and new level of input}
  if a_open_in(cur_file) then goto done;
  if cur_area="" then
    begin pack_file_name(cur_name,TEX_area,cur_ext);
    if a_open_in(cur_file) then goto done;
    end;
  end_file_reading; {remove the level that didn't work}
  prompt_file_name("input file name",".tex");
@y
pack_cur_name;
loop@+  begin begin_file_reading; {set up |cur_file| and new level of input}
  if x_open_in(cur_file) then goto done;
  end_file_reading; {remove the level that didn't work}
  prompt_file_name("input file name",".tex");
@z

{Section 537}
<<<<<Added 19-OCT-1994 by SPC: modify cur_name to be last_basename,
     when job_name is not yet set.>>>>>
@x
done: name:=a_make_name_string(cur_file);
if job_name=0 then
  begin job_name:=cur_name; open_log_file;
  end; {|open_log_file| doesn't |show_context|, so |limit|
    and |loc| needn't be set to meaningful values yet}
@y
done: name:=a_make_name_string(cur_file);
if job_name=0 then
  begin
    if last_basenam_len = length(cur_name) then
      begin
      for k:=1 to last_basenam_len do
         str_pool[str_start[cur_name]+k-1]:=si(xord[last_basename[k]]);
      end
    else
      if (pool_ptr+last_basenam_len<=pool_size)and(str_ptr<>max_strings)and
         (cur_length<=0) then
        begin
        for k:=1 to last_basenam_len do append_char(xord[last_basename[k]]);
        cur_name:=make_string;
        end;
    job_name:=cur_name; open_log_file;
  end; {|open_log_file| doesn't |show_context|, so |limit|
    and |loc| needn't be set to meaningful values yet}
@z

{Section 537}
@x <<<<<Added 9-DEC-1988 by BHK>>>>>
if name=str_ptr-1 then {we can conserve string pool space now}
  begin flush_string; name:=cur_name;
  end;
@y
@z

{Section 563}
@x [29] tfm blocking
if not b_open_in(tfm_file) then abort;
@y
if not b_open_in(tfm_file) then abort;
tfm_count:=0;
@z

{Section 564}
@x [29] tfm blocked
@d fget==get(tfm_file)
@d fbyte==tfm_file^
@y
@d fget==begin incr(tfm_count);
        if tfm_count=VAX_block_length then begin
                get(tfm_file,VAX_ignore_error); tfm_count:=0; end
        end
@d fbyte==tfm_file^[tfm_count]
@z

{Section 575}
@x [29] tfm blocked
if eof(tfm_file) then abort;
@y
if status(tfm_file)<>0 then abort;
@z

{Section 598}
@x [31] dvi blocked
@ Some systems may find it more efficient to make |dvi_buf| a |packed|
array, since output of four bytes at once may be facilitated.
@^system dependencies@>

@<Glob...@>=
@!dvi_buf:array[dvi_index] of eight_bits; {buffer for \.{DVI} output}
@y
@ Some systems may find it more efficient to make |dvi_buf| a |packed|
array, since output of four bytes at once may be facilitated.  On VMS,
we get even more complicated than that, for efficiency.

@d dvi_buf==d_buffer.b  {buffer for \.{DVI} output}

@<Glob...@>=
@!d_buffer: [VAX_volatile,VAX_aligned(9)] packed record
    case boolean of
        false: (@!b:packed array[dvi_index] of eight_bits);
        true:  (@!l:byte_block; @!r:byte_block; @!j:eight_bits);
    end;
@z

{Section 597}
@x [31] dvi blocked
@ The actual output of |dvi_buf[a..b]| to |dvi_file| is performed by calling
|write_dvi(a,b)|. For best results, this procedure should be optimized to
run as fast as possible on each particular system, since it is part of
\TeX's inner loop. It is safe to assume that |a| and |b+1| will both be
multiples of 4 when |write_dvi(a,b)| is called; therefore it is possible on
many machines to use efficient methods to pack four bytes per word and to
output an array of words with one system call.
@^system dependencies@>
@^inner loop@>
@^defecation@>

@p procedure write_dvi(@!a,@!b:dvi_index);
var k:dvi_index;
begin for k:=a to b do write(dvi_file,dvi_buf[k]);
end;
@y
@ The actual output of |dvi_buf[a..b]| to |dvi_file| is performed by calling
|write| on the other variant of the |dvi_buf| record.  Thus, we have to be
sure that things line up properly, by padding out with ``signature'' bytes.
@^system dependencies@>
@^inner loop@>
@^defecation@>

@<Check the ``co...@>=
if dvi_buf_size<>2*VAX_block_length then bad:=223;
@z

{Section 598}
% [31] dvi blocked
@x
  begin write_dvi(0,half_buf-1); dvi_limit:=half_buf;
@y
  begin if dvi_qual then write(dvi_file,d_buffer.l); dvi_limit:=half_buf;
@z

{Section 598}
@x
else  begin write_dvi(half_buf,dvi_buf_size-1); dvi_limit:=dvi_buf_size;
@y
else  begin if dvi_qual then write(dvi_file,d_buffer.r); 
      dvi_limit:=dvi_buf_size;
@z

{Section 599}
@x [31] dvi blocked
if dvi_limit=half_buf then write_dvi(half_buf,dvi_buf_size-1);
if dvi_ptr>0 then write_dvi(0,dvi_ptr-1)
@y
if (dvi_limit=half_buf) and dvi_qual then write(dvi_file,d_buffer.r);
for k:=dvi_ptr to dvi_buf_size do dvi_buf[k]:=223;
if (dvi_ptr>0) and dvi_qual then write(dvi_file,d_buffer.l);
if (dvi_ptr>half_buf) and dvi_qual then write(dvi_file,d_buffer.r);
@z

{Section 642}
<<<<<Added 6-DEC-1988 by BHK>>>>>
<<<<<Modified 26-SEP-1991 by BHK [DEK's now using slow_print]>>>>>
@x
  print_nl("Output written on "); slow_print(output_file_name);
@.Output written on x@>
  print(" ("); print_int(total_pages); print(" page");
  if total_pages<>1 then print_char("s");
  print(", "); print_int(dvi_offset+dvi_ptr); print(" bytes).");
  b_close(dvi_file);
@y
  if dvi_qual then
  begin
    b_close(dvi_file);
    print_nl("Output written on "); slow_print(output_file_name);
@.Output written on x@>
  end else
    print_nl("NO output file --- would have had");
  print(" ("); print_int(total_pages); print(" page");
  if total_pages<>1 then print_char("s");
  print(", "); print_int(dvi_offset+dvi_ptr); print(" bytes).");
@z

@x [49.1252] l.23230 - INI = VIR, so have to do runtime test.
    begin @!init new_patterns; goto done;@;@+tini@/
@y  23215
    begin @!Init new_patterns; goto done;@;@+Tini@/
@z

{Section 1275}
<<<<<Added 03-JUN-1994 by SPC: Use |x_open_in| instead of |a_open_in|.>>>>>
@x <<< |open_or_close_in| procedure >>>
if c<>0 then
  begin scan_optional_equals; scan_file_name;
  if cur_ext="" then cur_ext:=".tex";
  pack_cur_name;
  if a_open_in(read_file[n]) then read_open[n]:=just_open;
  end;
end;
@y
if c<>0 then
  begin scan_optional_equals; scan_file_name;
  if cur_ext="" then cur_ext:=".tex";
  pack_cur_name;
  if x_open_in(read_file[n]) then read_open[n]:=just_open;
  end;
end;
@z

{Section 1293}
@x <<<<<Added 13-FEB-1989 by BHK>>>>>
procedure show_whatever;
@y
procedure show_whatever;
@z
@x
begin case cur_chr of
@y
begin
  @<Clear out the |temp_file|@>;
  case cur_chr of
@z

{Section 1305}
@x [50] block fmt files
@d dump_wd(#)==begin fmt_file^:=#; put(fmt_file);@+end
@d dump_int(#)==begin fmt_file^.int:=#; put(fmt_file);@+end
@d dump_hh(#)==begin fmt_file^.hh:=#; put(fmt_file);@+end
@d dump_qqqq(#)==begin fmt_file^.qqqq:=#; put(fmt_file);@+end
@y
@d fmt_put==begin incr(fmt_count);
        if fmt_count=VAX_block_length then begin
                put(fmt_file,VAX_ignore_error); fmt_count:=0; end
        end
@d fmt_word==fmt_file^[fmt_count]

@d dump_wd(#)==begin fmt_word:=#; fmt_put;@+end
@d dump_int(#)==begin fmt_word.int:=#; fmt_put;@+end
@d dump_hh(#)==begin fmt_word.hh:=#; fmt_put;@+end
@d dump_qqqq(#)==begin fmt_word.qqqq:=#; fmt_put;@+end
@z

{Section 1306}
@x
@d undump_wd(#)==begin get(fmt_file); #:=fmt_file^;@+end
@d undump_int(#)==begin get(fmt_file); #:=fmt_file^.int;@+end
@d undump_hh(#)==begin get(fmt_file); #:=fmt_file^.hh;@+end
@d undump_qqqq(#)==begin get(fmt_file); #:=fmt_file^.qqqq;@+end
@y
@d fmt_get==begin incr(fmt_count);
        if fmt_count=VAX_block_length then begin
                get(fmt_file,VAX_ignore_error); fmt_count:=0; end
        end
@d undump_wd(#)==begin fmt_get; #:=fmt_word;@+end
@d undump_int(#)==begin fmt_get; #:=fmt_word.int;@+end
@d undump_hh(#)==begin fmt_get; #:=fmt_word.hh;@+end
@d undump_qqqq(#)==begin fmt_get; #:=fmt_word.qqqq;@+end
@z

{Section 1308}
@x
x:=fmt_file^.int;
@y
x:=fmt_word.int;
@z

{Section 1329}
@x [50] fmt file blocked
w_close(fmt_file)
@y
while fmt_count>0 do dump_int(0); {flush out the buffer}
w_close(fmt_file)
@z

{Section 1331}
<<<<<Modified 06-APR-1995 by SPC: removed |ready_already| entirely >>>>>
@x
@!ready_already:integer; {a sacrifice of purity for economy}
@y
@!init
@!init_flag: boolean;
tini@/
@z

{Section 1332}
>>>>>>>>Modified for new IniTeX handling 25-May-1990 by DAH>>>>>
>>>>>>>>Deleted previous change to definition of ready_already
>>>>>>>>      24-MAY-1990 by DAH>>>>>
@x <<<<<Added 25-AUG-1989 by BHK>>>>>
@ Now this is really it: \TeX\ starts and ends here.

The initial test involving |ready_already| should be deleted if the
\PASCAL\ runtime system is smart enough to detect such a ``mistake.''
@^system dependencies@>
@y
@ Now this is really it: \TeX\ starts and ends here.

The initial test involving |ready_already| should be deleted if the \PASCAL\
runtime system is smart enough to detect such a ``mistake'' and in fact it has
been deleted. We also take this opportunity to find out if we're supposed to be
\TeX\ or \.{INITEX}. (As an interesting note, Knuth simply uses \.{INITEX} for
everything now. The performance difference is too minimal on his machine to
make it worth maintaining two copies of \TeX.)

Since all files are opened with a |disposition:=delete| clause, they will be
deleted automatically if the program does not complete properly.  However, if a
fatal error occurs, the |jumpout| procedure also causes termination of the
program without closing the files, therefore we ensure that at least the
|log_file| gets closed so that the fatal error can be examined!
@z

{Section 1332}
<<<<<Added 24-MAY-1990 by DAH; modified one day later.>>>>
<<<<<Modified 19-OCT-1994 by SPC : added CLI table initialization>>>>>
<<<<<Modified 29-NOV-1997 PEB; added init...tini>>>>>
@x
if ready_already=314159 then goto start_of_TEX;
@y
if not odd(init_cli(TeX_CLD_table,'TEX','TEXFORMATS')) then
begin
  wterm_ln('Ouch---access to DCL command line interface has failed!');
  goto final_end;
end;
@!init init_flag:=odd(VAX_cli_present('INIT'));@+tini
@z

{Section 1332}
<<<<<Added 13-SEP-1990 by BHK; need to get /EIGHT_BIT before strings made>>>>
<<<<<Modified 29-NOV-1997 PEB; Init...Tini>>>>>
@x
@!init if not get_strings_started then goto final_end;
init_prim; {call |primitive| for each primitive}
init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr; fix_date_and_time;
tini@/
@y
eight_qual:=odd(VAX_cli_present('EIGHT_BIT'));
@./EIGHT_BIT@>
@!Init if not get_strings_started then goto final_end;
init_prim; {call |primitive| for each primitive}
init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr; fix_date_and_time;
Tini@/
@z

{Section 1332}
<<<<<Added 1-DEC-1988 by BHK>>>>>
<<<<<Modified 06-APR-1995 by SPC: removed |ready_already| entirely >>>>>
@x
ready_already:=314159;
start_of_TEX: @<Initialize the output routines@>;
@y
start_of_TEX: @<Extract command-line and qualifiers@>;
  @<Initialize the output routines@>;
@z

{Section 1332}
<<<<<Added 10-JUN-1988 by BHK>>>>>
<<<<<Modified 06-APR-1995 by SPC: removed |ready_already| entirely,
                                  use |slow_print| for |log_name| >>>>>
@x
end_of_TEX: close_files_and_terminate;
final_end: ready_already:=0;
end.
@y
end_of_TEX: close_files_and_terminate;
final_end:
if log_opened then
  begin wlog_cr;
  a_close(log_file); selector:=selector-2;
  if selector=term_only then
    begin
      if log_qual then
      begin print_nl("Transcript written on ");
@.Transcript written...@>
      slow_print(log_name); print_char(".");
      end else
      print_nl("NO transcript file.");
    end;
  end;
@<Exit to operating system with final status@>;
end.
@z

{Section 1333}
<<<<<Added 5-DEC-1988, modified 25-AUG-1989 by BHK>>>>>
<<<<<Modified 26-SEP-1991 by BHK [DEK's now using slow_print]>>>>>
<<<<<Changed 06-APR-1995 by SPC>>>>>
@x
if log_opened then
  begin wlog_cr; a_close(log_file); selector:=selector-2;
  if selector=term_only then
    begin print_nl("Transcript written on ");
@.Transcript written...@>
    slow_print(log_name); print_char(".");
    end;
  end;
@y
if diag_qual then
  begin wdiag_cr; wdiag_ln('end module');
  a_close(diag_file);
  print_nl("Diagnostics written on ");
@.Diagnostics written...@>
  slow_print(diag_name); print_char(".");
  end;
@z

@x [51.1335] l.24335 - Only do dump if ini.
  begin @!init for c:=top_mark_code to split_bot_mark_code do
@y
  begin @!Init for c:=top_mark_code to split_bot_mark_code do
@z

@x [51.1335] l.24337 - Only do dump if ini.
  store_fmt_file; return;@+tini@/
@y
  store_fmt_file; return;@+Tini@/
@z

{Section 1337}
>>>>>Added 28-MAY-1990 by DAH>>>>>
@x
@<Initialize the print |selector|...@>;
@y
@<Initialize the print |selector|...@>;
@<Read the values of \.{/TEXINPUTS} and \.{/TEXFONTS} and put them
  into the string pool@>;
@z

<<<<<Added 06-APR-1995 by SPC : removed unused local variables >>>>>
@x
@t\4@>@<Declare procedures needed in |do_extension|@>@;
procedure do_extension;
var i,@!j,@!k:integer; {all-purpose integers}
@!p,@!q,@!r:pointer; {all-purpose pointers}
@y
@t\4@>@<Declare procedures needed in |do_extension|@>@;
procedure do_extension;
var k:integer; {all-purpose integers}
@!p:pointer; {all-purpose pointers}
@z

{Section 1379...}
>>>>>Modified around 27-MAY-1990 by DAH>>>>>>>>>>>>
<<<<<Modified 06-NOV-1989 by BHK : update for V3 beta test>>>>>
<<<<<Modified on 1-DEC-1988 by BHK>>>>>
<<<<<Modified on 6-DEC-1988 by BHK>>>>>
<<<<<Modified on 25-AUG-1988 by BHK>>>>>
<<<<<Modified throughout period 24-AUG/28-SEP -1989 by BHK>>>>>
<<<<<Modified 06-NOV-1989 by BHK>>>>>
<<<<<Modified 13-NOV-1989 by BHK>>>>>
<<<<<Modified 10-JAN-1990 by BHK>>>>>
<<<<<Modified 13-SEP-1990 by BHK>>>>>
<<<<<Modified 19-OCT-1994 by SPC>>>>>
<<<<<Modified 23-NOV-1994 by SPC: always call |clear_default_name| >>>>>
<<<<<Modified 06-APR-1995 by SPC: alignment of |editor_ident| record >>>>>
<<<<<Modified 03-AUG-1995 by SPC: editor command file handling >>>>>
@x [54] left overs
This section should be replaced, if necessary, by any special
modifications of the program
that are necessary to make \TeX\ work at a particular installation.
It is usually best to design your change file so that all changes to
previous sections preserve the section numbering; then everybody's version
will be consistent with the published program. More extensive changes,
which introduce new sections, can be inserted here; then only the index
itself will get a new section number.
@y
Here are the remaining changes to the program
that are necessary to make \TeX\ work on VMS.  Note that especial care
has been taken not to introduce any new sections; therefore, everything up
to this point has had the same section numbers as the canonical versions in
``\TeX: The Program''.

Firstly, putting the cart before the horse, this is how we can return the final
status of \TeX\ to the operating system, in such a way that DCL command
procedures and the like can determine whether the run of \TeX\ was successfull
or not.  We use the \.{\$EXIT} system service; the value of its parameter is
@.{\$}EXIT@>
given by an appropriate symbolic constant taken from the \.{starlet} library.
We also take this opportunity to call the |symbol_jobname| routine (defined
below).

@d VAX_exit==@=$exit@>
@d VAX_ss_normal==@= sts$k_success @>
@d VAX_ss_ignore==@= sts$m_inhib_msg @>
@d VAX_ss_warning==@= sts$k_warning+sts$m_inhib_msg @>
@d VAX_ss_error==@= sts$k_error+sts$m_inhib_msg @>
@d VAX_ss_fatal==@= sts$k_severe+sts$m_inhib_msg @>

@<Exit to operating system with final status@>=
symbol_jobname;
case history of         { Issue an appropriate VAX exit status }
spotless: VAX_exit(VAX_ss_normal);      { Everything OK! }
warning_issued: VAX_exit(VAX_ss_warning);
error_message_issued: VAX_exit(VAX_ss_error);
fatal_error_stop: VAX_exit(VAX_ss_fatal)
endcases

@ |symbol_jobname| is a routine which takes the |job_name| string and the
numeric portion of the extension of the output file and writes that information
to the DCL symbol given by \.{/JOBNAME\_SYMBOL}.
@./JOBNAME_SYMBOL@>
The code here is based on code
donated by Jim Walker of South Carolina University.

@d VAX_set_symbol == @= lib$set_symbol @>

@<Last-minute ...@>=
procedure@?VAX_set_symbol(VAX_immed symbol: descr_ptr;
   VAX_immed value_string: descr_ptr;
   tbl_ind: integer := VAX_immed 0); external; @t\2@>
@#
procedure symbol_jobname;
var tmp_descr: descr_ptr;
begin tmp_descr:=nil;
   if job_qual then begin
     str_to_descr(job_name,tmp_descr);
     VAX_set_symbol (VAX_stdescr l_jobname, tmp_descr, 2);
   end;
end;

@ Support is provided for the \.{REVIEW} mode of DEC's Language-sensitive
editor,
@^Language-sensitive editor@>
@^LSE@>
by generating a \.{.dia} file. Any output sent via this routine is also repeated
to that file if the global |copy_err| is |print_it|: if the characters are being
``repeated'' to produce a \.{label} for a \.{region/text} directive, then
characters will only be copied if no more than |label_max| have been output;
this is controlled by |label_size|.  Negative values for this variable always
permit printing.

Since \TeX\ produces its error messages by many separate calls to various
printing routines, we accumulate the full text in the \PASCAL\ internal file
|temp_file| when |copy_err| is set to |save_it|.  This file can later be |reset|
and ``played back'' by standard \PASCAL\ routines.

@d label_max=14

@<Save printed character for diagnostic messages@>=
case copy_err of
print_it:   begin if label_size<> 0 then diag_char(s);
              if label_size>0 then decr(label_size)
            end;
ignore_it:  do_nothing;
save_it:    temp_char(s)
endcases

@ We introduce here variables which control the action of error reporting
routines.  When error message display is commenced, the variable |copy_err| is
set to |save_it|: this causes parts of the error message to be saved in
the internal file |temp_file|, which is rewound at this point.  Certain parts of
the error message are not so saved (|copy_err=ignore_it|).  This variable is
also used to cause messages to be written (|copy_err=print_it|) to the
diagnostics file |diag_file|.  Since VAX-\PASCAL\ supports proper enumeration
types, we don't bother with defining numeric constants for this.

When information is being written to the |diag_file|, we restrict the ``label''
portion of a diagnostic message to |label_max| characters, to preserve on-screen
alignment in LSEdit's \.{REVIEW} buffer.  Characters are only output through to
the |diag_file| if |label_size| is non-zero, and this variable is decremented
after each character has been output if it is positive.  Thus negative values of
|label_size| do not impose any restriction on the amount of text that may be
output to the |diag_file|.

@<Glob...@>=
@!copy_err:(ignore_it,print_it,save_it);
@!label_size:-1..label_max; {Restricts ``printing'' in the \.{.dia} file}

@ After the terminating period has been written, |copy_err| is reset to
prevent further output to |temp_file|, which is also reset, ready to be
``replayed'' into the diagnostics file itself.

This code is also used during initialization, and also before prompting for a
new file name when \TeX\ has been unable to find a users' file.

@<Ensure |temp_file| not in use@>=
copy_err:=ignore_it; reset(temp_file) {Full \TeX\ message in |temp_file|}

@ Every error message that \TeX\ creates is ``wrapped'' into a \.{diagnostic}
environment for use by LSE's \.{REVIEW} mode.

This is the text generated for the start of such an environment.

@<Commence an LSE diagnostic report@>=
wdiag_ln('!');
wdiag_ln('  start diagnostic')

@ And this finishes off the \.{diagnostic} environment: we copy into it the
informational part of \TeX's own error message.

@<Terminate an LSE diagnostic report@>=
wdiag('      message "%TEX-E-TEXERROR, ');
while not eof(temp_file) do
  begin wdiag(temp_file^); get(temp_file) end;
wdiag_ln('"');
wdiag_ln('  end diagnostic')

@ If the error report arises within the expansion of a macro, \TeX\ will report
the expansions of all macros and arguments involved: each such line of
information is used in the diagnostics file as a \.{label} (in the terminology
of LSE's \.{REVIEW} mode).  This is how we start it off, and ensure that no more
than |label_max| characters are printed, thus preserving alignment of the text
within the \.{\$REVIEW} buffer.

@<Output the location to the diagnostics file@>=
wdiag('    region/text/label="');
copy_err:=print_it; label_size:=label_max

@ The rest of the context (display of macro expansions, or whatever) forms the
remainder of the diagnostic region label.

@<Prepare to copy tokens to the diagnostic region@>=
copy_err:=ignore_it; wdiag('" "')

@ On the other hand, if \TeX's error report specifies a location within a
source file, the diagnostic region generated in the diagnostics file reports
that location thus:

@<Report location within source file to diagnostics@>=
wdiag('    region/file/primary ');
diag_print(name); wdiag_ln(' -'); {Continuation line follows}
wdiag_ln('          /line=',line:1,'/column_range=(1,65535)')

@ Whenever |show_context| involves printing out a token list, we arrange to
capture the printed tokens for our diagnostic file.

@<Include token report within diagnostics file@>=
wdiag('    region/text/label="');
copy_err:=print_it; label_size:=label_max

@ As we write out the second line of the original source, split at the point of
error detection, we don't want to include within the diagnostic file the newline
nor the leading spaces.  This looks like horrible duplication of code, but
remember that |copy_err=print_it| \&{only} if a diagnostic file is being
generated.

@<Split the context line display@>=
if copy_err=print_it then
begin
  copy_err:=ignore_it;
  print_ln;
  for q:=1 to n do print_char(" "); {print |n| spaces to begin line~2}
  copy_err:=print_it
end
else
begin
  print_ln;
  for q:=1 to n do print_char(" "); {print |n| spaces to begin line~2}
end

@ After we've completed the display of the error context, we are able to
complete the diagnostic region within the diagnostics file.

@<Indicate offending character@>=
if copy_err=print_it then
begin
  wdiag('"/line=1/column_range=(');
  n:=n-l;
  wdiag_ln(n+1:1,',65535)');
  copy_err:=ignore_it;
end
else
  wdiag_ln('    region/nested/column=',loc-start+1:1)

@ When we are writing the remainder of the context to the terminal and/or
transcript file, we need to ensure that it is also \&{all} copied to the
diagnostics file.

The diagnostic is completed by ``playing back'' the contents of the |temp_file|,
which contains \TeX's error message.

@<Copy remainder of context to diagnostic file@>=
begin copy_err:=print_it; label_size:=-1 end

@ When the \.{\string\show} primitive is used, it will later involve the display
of a token; the latter would cause output to be written to the |temp_file| used
for accumulating error messages for the diagnostics file, so we ensure here that
the file will not be overfilled.

@<Clear out the |temp_file|@>=
copy_err:=ignore_it; rewrite(temp_file); {Internal file will later be |reset|}

@ The |open_diag_file| routine is used to open a file into which error
diagnostics are written to support DEC's Language-sensitive Editor (LSEdit).
@^Language-sensitive editor@>
@^LSE@>
These may be used by the latter to locate the editor at the position within
the source file at which an error has been detected.

@<Declare the |open_diag_file| procedure@>=
procedure open_diag_file;
begin
  pack_job_name(".dia");
  if pack_default_name(diag_qual,diagf_name,diagf_len) then
  begin
    while not a_open_out(diag_file) do
      prompt_file_name("diagnostics file name",".dia");
    diag_name:=a_make_name_string(diag_file);
    wdiag_ln('start module');
  end else
    diag_name:=".";
  clear_default_name;
end;

@ Here are a number of variables used during the initial extraction of the
command line and its qualifiers.  Firstly, we require separate flags for each of
the possible qualifiers.

We also need to declare those variables associated with support for the
diagnostics file, utilized by LSEdit.
@^Language-sensitive editor@>
@^LSE@>
When \TeX\ is producing error messages, they are created in ``dribs and drabs'';
we utilize a \PASCAL\ `internal' file |temp_file| to accumulate the whole
message for transfer to the diagnostics file.

This mechanism is also used to create a command line by means of which an editor
can be invoked by the user answering `\.e' in response to \TeX's error prompt.
Since such invocation of an editor will disrupt access to the values associated
with any qualifiers on the \.{TEX} command, we have to provide storage space for
any values provided with those qualifiers, so that they may be read during the
initialization phase, in preparation for use later (in some cases, much later)
in the program.  For each such piece of text, we need somewhere to save it, and
somewhere else to record its length, for use with |pack_default_name|.

@<Glob...@>=
@!format_qual, @!dvi_qual, @!cmd_line_present, @!continue_qual, @!eight_qual,
@!job_qual,@!batch_qual, @!log_qual, @!diag_qual, @!edit_qual : boolean;
@#
@!diag_file : alpha_file;
@!diag_name : str_number;
@!temp_file : alpha_file;
@#
@!logf_name, @!diagf_name, @!edit_name, @!l_jobname,
@!dvif_name : packed array[1..file_name_size] of char;
@!logf_len, @!diagf_len, @!edit_len, @!l_len_name, @!dvif_len : file_size;

@ Since we provide a command-line qualifier which will ``preload'' a format
file, it would be best to extract all the qualifiers before the |banner| gets
printed, so that the correct preloaded format can be displayed (it will never
{\it really\/} be preloaded, but a VAX doesn't take long to read a \.{.FMT}
file!)  In fact, it is {\it essential\/} that all command-line qualifiers be
read at this stage; the reader might imagine that extraction of qualifiers
and their values could be deferred until the point at which the qualifier is
used, but any intervening activation of another image (for example, an editor)
results in the information being wiped out.

The |cmd_line_present| flag will later avoid clearing the |buffer| if a
command-line has already been ``read'' into it.  We can control \TeX's operation
in |batch_mode| through the \.{/BATCH} qualifier.

At this point, we also initialize |copy_err|, which controls the insertion into
the diagnostics file of text being (pseudo)printed in traditional \TeX\ error
message.

@<Extract command-line and qualifiers@>=
diag_name := 0;
get_command_line;
if batch_qual then interaction:=batch_mode;
copy_err:=ignore_it@;

@ For interacting with a user-supplied command line, we need to call the VAX
standard library routines \.{CLI\$PRESENT}, \.{CLI\$GET\_VALUE},
\.{CLI\$DCL\_PARSE} and \.{LIB\$GET\_FOREIGN}.
@.CLI{\$}PRESENT@>
@.CLI{\$}GET_VALUE@>
@.CLI{\$}DCL_PARSE@>
@.LIB{\$}GET_FOREIGN@>
This is a definition of their external interfaces: note the application of the
`external' attribute, and use of the |extern| directive.

@d VAX_external==@= external@>
@d VAX_asynchronous==@= asynchronous@>
@d VAX_cli_dcl_parse==@= cli$dcl_parse@>
@d VAX_lib_get_foreign==@= lib$get_foreign@>
@d VAX_lib_sig_to_ret==@= lib$sig_to_ret@>
@d VAX_establish==@= establish@>

@<VMS procedures@>=
[VAX_external] function VAX_cli_present(@/
  VAX_stdescr @!entity: [VAX_volatile,VAX_readonly]
                packed array [l1..u1:integer] of char
        := VAX_immed 0) : integer; @/
  extern;@;@t\2@>@#

[VAX_external] function VAX_cli_get_value(@/
  VAX_stdescr @!entity: [VAX_volatile,VAX_readonly]
                packed array [l1..u1:integer] of char
        := VAX_immed 0;
  VAX_stdescr @!returns: [VAX_volatile]
                packed array [l2..u2:integer] of char
        := VAX_immed 0;
  var @!retlen: [VAX_volatile] sixteen_bits := VAX_immed 0):integer; @/
  extern;@;@t\2@>@#

[VAX_external] function VAX_cli_dcl_parse(@/
  VAX_stdescr @!cmdline: [VAX_volatile,VAX_readonly]
                packed array [l1..u1:integer] of char
        := VAX_immed 0;
  VAX_immed @!cld_table: [VAX_volatile,VAX_readonly]
                VAX_unsigned
        := VAX_immed 0):integer; @/
  extern;@;@t\2@>@#

[VAX_external] function VAX_lib_get_foreign(@/
  VAX_stdescr @!cmdlin: [VAX_volatile] packed array [l1..u1:integer] of char
        := VAX_immed 0;
  VAX_stdescr @!prompt: [VAX_volatile] packed array [l2..u2:integer] of char
        := VAX_immed 0;
  var @!len: [VAX_volatile] sixteen_bits := VAX_immed 0;
  var @!flag: [VAX_volatile] integer := VAX_immed 0)
    :integer; @/
  extern;@;@t\2@>@#

[VAX_external, VAX_asynchronous] function VAX_lib_sig_to_ret(@/
  VAX_ref @!signal_args: [VAX_volatile,VAX_unsafe]
                         array [l1..u1:integer] of [VAX_byte] eight_bits;
  VAX_ref @!mechan_args: [VAX_volatile,VAX_unsafe]
                         array [l2..u2:integer] of [VAX_byte] eight_bits)
    :integer; @/
  extern;

@ The following global symbol is used to refer to the command definition
table linked into the \TeX\ program
@d TeX_CLD_table == @=TEX_CLI@>
@<Glob...@>=
@!TeX_CLD_table : [VAX_external, VAX_readonly] VAX_unsigned;

@ The |init_cli| function is invoked right at the beginning of \TeX,
only preceded by the terminal output initialization. Its purpose
is to make sure that the DCL command interface is available.

This function checks, if the program was invoked by the DCL command
specified through |verb_name| and that a command
qualifier specified by |qual_name| is present (or defaulted) in the
command description. For the second test, a small subroutine |check_cli|
is needed, because of the "caller--callee" dependence required by
the user error handler facility.
The |verb_name| string supplied to |init_cli| by the caller must not
exceed a length of 4, otherwise the comparison with the "last DCL command"
does never succeed, because the DCL parser truncates commands to a length
of 4!
The test item |qual_name|  should be a specific, non-negatable command
qualifier for the verb |verb_name|, which is set by default in the command
description.

If either of these two tests fail, it can be assumed that the program
was invoked as a foreign command (or started by the RUN command).
If this case, the command line tail is fetched with the
\.{LIB\$GET\_FOREIGN} runtime functions and parsed internally,
using the \.{CLI\$DCL\_PARSE} utility routine and the command table
linked into the program executable, whose name is supplied by the
|table| formal parameter.
@.LIB{\$}GET_FOREIGN@>
@.CLI{\$}DCL_PARSE@>

@<Last-minute procedures@>=
function init_cli(
        var @!table:[VAX_readonly] VAX_unsigned;
        @!verb_name:[VAX_readonly] packed array[l1..u1:integer] of char;
        @!qual_name:[VAX_readonly] packed array[l2..u2:integer] of char
       ): integer;
label exit;
var command_line: packed array[1..256] of char;
@!len: sixteen_bits;
@!sts: integer;

function check_cli(
        @!unique_def_qual:[VAX_readonly] packed array[l1..u1:integer] of char
                 ): integer;
begin
  VAX_establish(VAX_lib_sig_to_ret);
  check_cli := VAX_cli_present(unique_def_qual);
end;

begin
  sts := VAX_cli_get_value('$VERB',command_line,len);
  if (odd(sts) and (len > 0)) then
    if (VAX_substr(command_line,1,len) = verb_name) then
       if (odd(check_cli(qual_name))) then
       begin
          init_cli := 1;
          return;
       end;

  VAX_lib_get_foreign(command_line,,len);
  {prepend |verb_name| plus a blank to |command_line|}
  command_line := verb_name + ' ' + VAX_substr(command_line,1,len);
  init_cli := VAX_cli_dcl_parse(command_line, VAX_address_of(table));
exit:end;

@ Logically, the following procedure belongs with |init_terminal|; however,
we can't declare it there because it calls functions which don't get
declared until later, so we'll stuff it in just before the main program starts.

If an editor is invoked later, its use of the command-line interface parsing
routines will ``disable communications'', so we'd better extract any values
associated with qualifiers now.  The various flags are set or cleared according
as to whether the associated qualifier is or is not present.

@<Last-minute procedures@>=
procedure get_command_line;
var qual_argument: packed array[1..256] of char;
@!len: sixteen_bits;
@!i: integer;
@!j: 0..buf_size;
begin
  cmd_line_present := odd(VAX_cli_present('COMMAND_LINE'));
  edit_qual := odd(VAX_cli_present('EDITOR'));
  if edit_qual then VAX_cli_get_value('EDITOR',edit_name,edit_len);
  job_qual:=odd(VAX_cli_present('JOBNAME_SYMBOL'));
  if job_qual then VAX_cli_get_value('JOBNAME_SYMBOL',l_jobname,l_len_name);
  continue_qual := odd(VAX_cli_present('CONTINUE'));
  batch_qual := odd(VAX_cli_present('BATCH'));
  dvi_qual := odd(VAX_cli_present('OUTPUT'));
  if dvi_qual then VAX_cli_get_value('OUTPUT',dvif_name,dvif_len);
  log_qual := odd(VAX_cli_present('LOG_FILE'));
  if log_qual then VAX_cli_get_value('LOG_FILE',logf_name,logf_len);
  diag_qual := odd(VAX_cli_present('DIAGNOSTICS'));
  if diag_qual then VAX_cli_get_value('DIAGNOSTICS',diagf_name,diagf_len);
  format_qual := odd(VAX_cli_present('FORMAT'));
  if format_qual then
  begin
    VAX_cli_get_value('FORMAT',qual_argument,len);
    loc := 0; buffer[0] := xord['&']; j := 1;
    for i := 1 to len do
    begin
      buffer[j] := xord[qual_argument[i]]; incr(j)
    end;
    buffer[j] := xord[' '];       { |open_fmt_file| requires space after name }
    if format_ident <> 0 then initialize;
    if not open_fmt_file then goto final_end;
    if not load_fmt_file then
    begin
      w_close(fmt_file); goto final_end;
    end;
    w_close(fmt_file);
  end;
end;

@ Here are the things we need for |byte_file| and |word_file| files:

@<Glob...@>=
@!tfm_count: 0..VAX_block_length;
@!fmt_count: 0..VAX_block_length;

@ Here's the interrupt stuff.

At this point, we define some attributes for specifying particular sizes and
alignments of numerical quantities in VAX-\PASCAL.

@d VAX_word==@= word @>
@d VAX_longword==@= long @>
@d VAX_byte==@= byte @>
@d VAX_unsigned==@= unsigned @>

@<Types...@>=
@!signed_halfword=[VAX_word] -32768..32767;
@!sixteen_bits=[VAX_word] 0..65535;
@!file_size=[VAX_word] 0..file_name_size;
@#
@!VAX_F_float = packed record    {Bit pattern layout of F-Floating Reals}
                @!Frac1 : 0..127;     {the 7 MSBits of the mantissa}
                @!Expo  : 0..255;     {8 bit exponent}
                @!Sign  : boolean;    {1 sign bit}
                @!Frac  : 0..65535;   {the 16 lower bits of the mantissa}
                end;

@ @<Glob...@>=
@!res: [VAX_volatile] integer;
@!tt_chan: [VAX_volatile] signed_halfword;

@ @<VMS procedures@>=
[VAX_asynchronous] procedure @!ctrlc_rout;
begin
interrupt:=1;
enable_control_C;
end;

@ Here is the stuff for magic file operations.

@d VAX_FAB_type==@= FAB$type @>
@d VAX_RAB_type==@= RAB$type @>
@d VAX_NAM_type==@= NAM$type @>

@<Types...@>=
@!unsafe_file = [VAX_unsafe] file of char;
@!FAB_ptr = ^VAX_FAB_type;
@!RAB_ptr = ^VAX_RAB_type;
@!NAM_ptr = ^VAX_NAM_type;
@!chrptr = ^char;

@ We supply the following two routines to be used (in a call of the VAX-\PASCAL\
|open| procedure) as a |user_action| function.  When called from within the
|open| routine, the addresses of the |FAB| and |RAB| allocated to the file are
passed to such a function, along with the file variable; the latter is tagged as
`unsafe' to prevent undesirable compiler optimizations.

The |user_reset| function, used to open files for reading, performs
wild card expansion on the file specification and opens the first matching
file.

Both |user_action| functions copy the fully qualified name of the file
that was actually opened into the global variable |last_name|. Additionally,
the basename part of the filename is available in the string variable
|last_basename|. The latter string is converted to lowercase; to comply
with normal usage on other (case-sensitive) operating systems.

The two external functions |VAX_PAS_FAB| and |VAX_PAS_RAB| permit access by the
program to these structures after the file has been opened.

@d VAX_rms_parse==@=$parse@>
@d VAX_rms_search==@=$search@>
@d VAX_rms_create==@=$create@>
@d VAX_rms_connect==@=$connect@>
@d VAX_rms_open==@=$open@>
@#
@d VAX_FAB_V_NAM== @=FAB$V_NAM@>
@d VAX_FAB_L_NAM== @=FAB$L_NAM@>
@d VAX_NAM_B_RSL== @=NAM$B_RSL@>
@d VAX_NAM_L_RSA== @=NAM$L_RSA@>
@d VAX_NAM_B_NAME== @=NAM$B_NAME@>
@d VAX_NAM_L_NAME== @=NAM$L_NAME@>

@<VMS procedures@>=
function user_reset
        (var FAB:VAX_FAB_type;
         var RAB:VAX_RAB_type;
         var F:unsafe_file):integer;
label done;
var sts:integer; @!NAM:NAM_ptr; @!p:chrptr; @!i:integer; @!ichr:integer;
begin
last_length:=0;
sts:=VAX_rms_parse(FAB);
if not odd(sts) then goto done;
sts:=VAX_rms_search(FAB);
if odd(sts) then
  FAB.VAX_FAB_V_NAM:=true; {Use |NAM| block in |VAX_rms_open| call!}
sts:=VAX_rms_open(FAB);
if not odd(sts) then goto done;
sts:=VAX_rms_connect(RAB);
if not odd(sts) then goto done;
NAM:=FAB.VAX_FAB_L_NAM::NAM_ptr;
if NAM=nil then goto done;
last_length:=NAM^.VAX_NAM_B_RSL;
for i:=1 to last_length do begin
        p:=(NAM^.VAX_NAM_L_RSA::integer+i-1)::chrptr;
        last_name[i]:=p^;
        end;
last_basenam_len:=NAM^.VAX_NAM_B_NAME;
for i:=1 to last_basenam_len do begin
        p:=(NAM^.VAX_NAM_L_NAME::integer+i-1)::chrptr;
        ichr:=ord(p^);
        if (ichr > 64) and (ichr < 91) then ichr := ichr+32;
        last_basename[i]:=chr(ichr);
        end;
done: user_reset:=sts;
end;
@#
function user_rewrite
        (var FAB:VAX_FAB_type;
         var RAB:VAX_RAB_type;
         var F:unsafe_file):integer;
label done;
var sts:integer; @!NAM:NAM_ptr; @!p:chrptr; @!i:integer; @!ichr:integer;
begin
sts:=VAX_rms_create(FAB);
if not odd(sts) then goto done;
sts:=VAX_rms_connect(RAB);
if not odd(sts) then goto done;
NAM:=FAB.VAX_FAB_L_NAM::NAM_ptr;
if NAM=nil then goto done;
last_length:=NAM^.VAX_NAM_B_RSL;
for i:=1 to last_length do begin
        p:=(NAM^.VAX_NAM_L_RSA::integer+i-1)::chrptr;
        last_name[i]:=p^;
        end;
last_basenam_len:=NAM^.VAX_NAM_B_NAME;
for i:=1 to last_basenam_len do begin
        p:=(NAM^.VAX_NAM_L_NAME::integer+i-1)::chrptr;
        ichr:=ord(p^);
        if (ichr > 64) and (ichr < 91) then ichr := ichr+32;
        last_basename[i]:=chr(ichr);
        end;
done: user_rewrite:=sts;
end;
@#
function VAX_PAS_FAB(var foobar:unsafe_file):FAB_ptr; extern;@;@t\2@>@/
function VAX_PAS_RAB(var foobar:unsafe_file):RAB_ptr; extern;

@ @<Glob...@>=
@!in_FAB,out_FAB: FAB_ptr;
@!in_RAB,out_RAB: RAB_ptr;
@!last_length: integer;
@!last_name:packed array[1..file_name_size] of char;
@!last_basenam_len: integer;
@!last_basename:packed array[1..file_name_size] of char;

@ The following procedure is used to translate any logical name that may appear
as its parameter into its equivalence string and makes use of the \.{\$TRNLNM}
@.{\$}TRNLNM@>
system service in place of the obsolete \.{\$TRNLOG}. If the content of the
@.{\$}TRNLOG@>
buffer is a logical name, it is replaced by its equivalence string and the
routine returns |true|.  If no translation can be found, the result is |false|,
and the original string is left unchanged.

The VAX-\PASCAL\ procedure |substr| is used to extract a substring into the
|varying| array which is passed to the system service, whilst another
VAX-specific function |iaddress| is used to obtain the address of various data
items to fill in the |item_list|.

@d VAX_trnlnm==@= $trnlnm@>
@d VAX_lnm_case_blind==@= lnm$m_case_blind @>
@d VAX_lnm_string==@= lnm$_string @>
@#
@d VAX_substr==@= substr@>
@d VAX_address_of==@= iaddress@>

@<VMS procedures@>=
function translate ( var t : packed array [l1..u1 : integer] of char;
                     var len : signed_halfword): boolean;
  var
    @!s: varying[file_name_size] of char;
    @!trnlnm_return: integer; {what did the \.{\$TRNLNM} return?}
    @!return_length: [VAX_volatile] integer;
    @!attributes: unsigned;
    @!item_list: [VAX_volatile] array [0..1] of VMS_item_list;
begin
  s:=VAX_substr(t,1,len);
  attributes := VAX_lnm_case_blind;
  return_length := 0;
  with item_list[0] do
  begin
    buffer_length := file_name_size;
    item_code := VAX_lnm_string;
    buffer_addr := VAX_address_of(t);
    ret_len_addr := VAX_address_of(return_length);
  end;
  item_list[1].next_item := 0;
  trnlnm_return := VAX_trnlnm(attributes,'LNM$DCL_LOGICAL',s,,item_list);
  len := return_length;
  translate := trnlnm_return=VAX_ss_normal;
end;

@ Here is a new type introduced to support \.{\$TRNLNM}.  Many VMS system
@.{\$}TRNLNM@>
services make use of an |item_list| to pass information in and out.  An
|item_list| consists of a number of |item_list| elements, with each element
containing the following fields:

\centerline{\vtop{\offinterlineskip\hrule
  \halign{\vrule#\hskip2pt&\strut#\hfil&#\hfil&#\hfil&\hskip2pt\vrule#\cr
    height2pt&\omit&\omit&\omit&\cr
    &\hfil Name & \hfil Type & \hfil Usage&\cr
    height2pt&\omit&\omit&\omit&\cr
    \noalign{\hrule}
    height2pt&\omit&\omit&\omit&\cr
    &|buffer_length| & 16-bit word & Size of buffer&\cr
    &|item_code| & unsigned 16-bit word & Code for desired operation&\cr
    &|buffer_address| & Pointer to char & Address of buffer&\cr
    &|ret_len_addr| & Pointer to integer & To receive length of
      translation&\cr
    height2pt&\omit&\omit&\omit&\cr}
  \hrule
}}

This structure is overlaid with a single 32-bit integer whose use is solely to
hold the value zero indicating the end of the list.

@<Types in the...@>==
  @!VMS_item_list =
    packed record
      case boolean of
      true: (
        @!buffer_length : sixteen_bits;@/
        @!item_code     : sixteen_bits;@/
        @!buffer_addr   : [VAX_longword] integer;@/
        @!ret_len_addr  : [VAX_longword] integer);
      false: (
        @!next_item : [VAX_longword] integer)
    end;

@ If the user, in response to \TeX's error message, elects to edit the source
file, then we have to find some method of invoking an editor.  The simplest
solution, under VMS, is simply to spawn a sub-process, but this is expensive
in terms of image activation and might leave the sub-process short of page file
quota, since the latter is shared by all processes in the current `job'.

Therefore, where possible, we invoke a ``callable'' editor, which merely
requires that we find the relevant editor's entry point in an installed
shareable image.  However, the library routine which can perform this trick
returns the entry point as an address, and yet we want the \PASCAL\ code to
think that it's invoking the editor through a procedure call, passing
appropriate parameter(s).

The callable versions of LSEdit
@^Language-sensitive editor@>
@^LSE@>
and TPU each require a single parameter which is
@^TPU@>
@.EDIT/TPU@>
a string similar to the DCL command that could be used to invoke the
non-callable versions.  In the case of EDT
@^EDT@>
@.EDIT/EDT@>
@^Callable editors@>
and TECO,
@^TECO@>
@.EDIT/TECO@>
the first parameter gives
the name of the file to be edited, the second (if used) names the output file,
whilst the third can specify the name of a command file.  Both editors can also
take further parameters, and their meanings differ, but luckily we don't need
any of these other parameters!

Unfortunately, \PASCAL\ provides no mechanism by which a routine, which has
amongst its formal parameters one which is in turn another routine, may be
called with anything but the name of an \\{actual} routine (with congruent
parameters) substitued for that formal parameter.  Therefore, it is not
permissible to pass the address of the routine instead and yet that is all that
we have available!

We therefore provide a procedure which calls, in turn, the actual editor
``procedure'', and resorting to subterfuge, invoke a rather useful VAX Library
Routine:

@d VAX_lib_callg==@= lib$callg@>

@<VMS procedures@>=
[VAX_external] function VAX_lib_callg (@/
     VAX_immed arg_list : [VAX_longword] integer;
     VAX_immed user_proc: [VAX_longword] integer) : integer;
   extern;@t\2@>@#

function call_editor ( @!proc: [VAX_longword] integer;
                       @!param_1, @!param_3 :
                               [VAX_volatile] descr_ptr ) : integer;
  var
    @!call_G_descriptor : packed array [1..4] of [VAX_longword] integer;
begin
  call_G_descriptor[1] := 1; {Number of arguments}
  call_G_descriptor[2] := param_1::integer; {DCL-like command line or
                                             name of file to be edited}
  if param_3 <> nil then
  begin
    call_G_descriptor[1] := 3; {EDT and TECO require more arguments}
    call_G_descriptor[3] := 0; {Default the output file name}
    call_G_descriptor[4] := param_3::integer; {Editor command file}
  end;
  call_editor:=VAX_lib_callg(VAX_address_of(call_G_descriptor),proc)
end;

@ Here is the interface to two routines from the run-time library to handle
dynamic strings.  Also, we declare here the interface to the \.{LIB\$SIGNAL}
@.LIB{\$}SIGNAL@>
library function, because we don't have much else to fall back on if an error
crops up whilst allocating strings!

@d str_allocate ==@= str$get1_dx@>
@d str_release  ==@= str$free1_dx@>
@d lib_signal   ==@= lib$signal@>
@d VAX_char_string==@= dsc$k_dtype_t @>
@d VAX_class_S==@= dsc$k_class_s @>
@d VAX_class_D==@= dsc$k_class_d @>

@<VMS procedures@>=
[VAX_external, VAX_asynchronous] function str_allocate(@/
        VAX_ref alloc    : [VAX_readonly] sixteen_bits;
        VAX_immed descrp : descr_ptr ) : integer;
extern;@t\2@>
@#
[VAX_external, VAX_asynchronous] function str_release(@/
        VAX_immed descrp : descr_ptr ) : integer;
extern;@t\2@>
@#
[VAX_external, VAX_asynchronous] procedure lib_signal(@/
        VAX_immed cond_code: integer;
        VAX_immed num_of_args: integer := VAX_immed 0;
        VAX_immed fao_argument: [@=list,unsafe@>] integer );
extern;

@ Some editors require either command or file specifications to be passed to
them as parameters, which in turn requires that they be passed in the form of
string descriptors.  Many of the strings that we have to deal with are held
within \TeX's string pool.

This routine converts a \.{WEB}-type string (from the pool) into an appropriate
VAX-\PASCAL\ string descriptor.  Any existing string described by |dynam_str| is
returned to the operating system and a new string allocated to reflect the
actual length of the string in |pool_string|.

@<VMS procedures@>=
procedure str_to_descr( @!pool_string : str_number;
                    var @!dynam_str : [VAX_volatile] descr_ptr);
  var @!ch_ptr, @!str_stat : integer;
      @!str_size : sixteen_bits;
      @!ch_ctr : chrptr;
begin
  if dynam_str = nil then
  begin
    new( dynam_str );
    with dynam_str^ do
    begin len := 0;
      desc_type := VAX_char_string;
      desc_class := VAX_class_D;
      string := 0
    end;
  end
  else
    if dynam_str^.len <> 0 then
    begin
      str_stat := str_release( dynam_str );
      if not odd(str_stat) then lib_signal(str_stat)
    end;
  ch_ptr := str_start[pool_string];
  str_size := str_start[pool_string+1]-str_start[pool_string];
  str_stat := str_allocate(str_size,dynam_str);
  if not odd(str_stat) then lib_signal(str_stat);
  ch_ctr := dynam_str^.string :: chrptr;
  while str_size>0 do
  begin
    ch_ctr^ := xchr[so(str_pool[ch_ptr])];
    ch_ctr := (ch_ctr::integer + 1)::chrptr;
    incr(ch_ptr);
    decr(str_size)
  end;
end;

@ Here is where we declare a structure to hold a VMS Descriptor.  We could just
have used one of the definitions in the \.{STARLET} library that we've
inherited, but declaring it here is an aid to understanding.

\centerline{\vtop{\offinterlineskip\hrule
  \halign{\vrule#\hskip2pt&\strut#\hfil&#\hfil&#\hfil&\hskip2pt\vrule#\cr
    height2pt&\omit&\omit&\omit&\cr
    &\hfil Name & \hfil Type & \hfil Usage&\cr
    height2pt&\omit&\omit&\omit&\cr
    \noalign{\hrule}
    height2pt&\omit&\omit&\omit&\cr
    &|len| & 16-bit word & Elements in the array&\cr
    &|desc_type| & unsigned 8-bit byte & Type of items in array&\cr
    &|desc_class| & unsigned 8-bit byte & \\{e.g.} Fixed, Varying, Dynamic&\cr
    &|string| & Pointer to char & Address of first item in array&\cr
    height2pt&\omit&\omit&\omit&\cr}
  \hrule
}}

It also makes life much easier, when passing dynamic strings as parameters,
especially to system services and library routines which expect to be passed the
address of such a descriptor, to have a type which is a pointer to such a
descriptor, and then pass the pointer's value by immediate parameter-passing
mechanism.

@<Types...@>=
@!descr_type =  packed record {A VAX-descriptor object}
                  @!len       : sixteen_bits;
                  @!desc_type : eight_bits;
                  @!desc_class: eight_bits;
                  @!string    : [VAX_longword] integer;
                end;
@!descr_ptr = ^descr_type;

@ Here is a procedure to dispose of dynamically-allocated strings when they are
no longer required.

@<VMS proc...@>=
procedure release ( @!string : descr_ptr );
  var str_stat : integer;
begin
  if string <> nil then
  begin
    str_stat := str_release( string );
    if not odd(str_stat) then lib_signal( str_stat );
    dispose(string);
    string := nil;
  end;
end;

@ This version of \TeX\ supports various editors; that required by the user
must be specified by the qualifier \.{/EDITOR} which is set by default to
\.{TEX\_EDIT} (which should be defined as a VMS logical name, in analogy with
@.TEX_EDIT@>
@.MAIL{\$}EDIT@>
\.{MAIL\$EDIT}---in fact some system managers may want to set the default in
the CLD file to {\it be\/} \.{MAIL\$EDIT}).
If this qualifier specifies one of the strings
`\.{Callable\_LSE}', `\.{Callable\_TPU}', `\.{Callable\_EDT}
@.Callable_xxx@>
or `\.{Callable\_TECO}' (or a logical name translating to one of those
strings), the appropriate
editor is invoked from its callable shared image.  Any other value
for \.{/EDITOR} is treated as a DCL command, and a sub-process is spawned in
which the command is executed; the name of the file to be edited, together with
the location of the error, are passed as parameters to this DCL command, which
will most usefully, therefore, be defined to invoke a command procedure.
Because the original version of this change file simply used \.{TEX\_EDIT}
directly and this is the default value, the remainder of this exposition will
simply refer to the value of \.{/EDITOR} as \.{TEX\_EDIT}.

Here is a data structure which holds details of the supported callable editors:

@<Types...@>=
  @!editor_ident = record
                     @!cmd_offset : integer;
                     @!image, @!entry, @!quitting,
                     @!exiting, @!cmd_text: str_number;
                     @!start_qual, @!EDT_like : boolean;
                     @!logical : packed array[1..file_name_size] of char;
                   end;

@ We need a suitably sized array of such structures:

@d max_editor=4
@#
@d LSE_editor=1
@d TPU_editor=2
@d EDT_editor=3
@d TECO_editor=4

@<Glob...@>=
  @!editor : packed array [1..max_editor] of editor_ident;

@ And we needs must initialize them:

@<Set init...@>=
with editor[LSE_editor] do
begin
  logical := 'CALLABLE_LSE';
  image := "LSESHR";
  entry := "LSE$LSE";
  quitting := "TPU$_QUITTING";
  exiting := "TPU$_EXITING";
  cmd_text := "LSEdit";
  cmd_offset := 0;
  start_qual:=true;
  EDT_like := false;
end;
with editor[TPU_editor] do
begin
  logical := 'CALLABLE_TPU';
  image := "TPUSHR";
  entry := "TPU$TPU";
  quitting := "TPU$_QUITTING";
  exiting := "TPU$_EXITING";
  cmd_text := "EDIT/TPU";
  cmd_offset := 5; {Actual command expected by \.{TPU\$TPU} omits \.{EDIT/}}
  start_qual:=true;
  EDT_like := false;
end;
with editor[EDT_editor] do
begin
  logical := 'CALLABLE_EDT';
  image := "EDTSHR";
  entry := "EDT$EDIT";
  quitting := 0;
  exiting := 0;
  cmd_text := "EDIT/EDT";
  cmd_offset := 0;
  start_qual:=false;
  EDT_like := true;
end;
with editor[TECO_editor] do
begin
  logical := 'CALLABLE_TECO';
  image := "TECOSHR";
  entry := "TECO$EDIT";
  quitting := 0;
  exiting  := 0;
  cmd_text := "EDIT/TECO";
  cmd_offset := 0;
  start_qual := false;
  EDT_like := true;
end;

@ When we invoke an editor, there are three (possibly more?) potential outcomes:
(1) The editor cannot be invoked --- perhaps we should find some other method;
(2) The user makes no change to the file (quits); (3) The use produces a new
version of the file.  This type allows us to discriminate between these
outcomes:

@<Types...@>=
@!edit_result = (failed,quit,edited);

@ If the user elects to edit the relevant input file in response to an error
message, we prefer to use an editor provided as a ``callable image'', since this
saves the overhead of spawning a sub-process.  DEC provide callable versions of
EDT,
@^EDT@>
@.EDIT/EDT@>
@^Callable editors@>
TPU,
@^TPU@>
@.EDIT/TPU@>
LSEdit (the language-sensitive editor, highly recommended for \LaTeX),
@^Language-sensitive editor@>
@^LSE@>
and even for that editor beloved of many, TECO.
@^TECO@>
@.EDIT/TECO@>

To activate such a callable image, we need to load it into the process's \.{P0}
space, and determine its entry point before transferring control to it with
appropriate parameters.

If it proves impossible to load a suitable callable image, we can adopt the
expedient of spawning a new (DCL) sub-process, and pass to it the command to be
executed.  When such a spawned sub-process is given a single command to execute,
the exit status of that command is passed back to the parent process when the
sub-process exits.  In most useful applications of such a sub-process, the
``command'' to be executed will be a DCL command procedure; the code below will
accept an exit status of $1$ as indicating that an edit has taken place, the
value $0$ (which is of warning severity level) as showing that the edit was
aborted (the user quit), and any other value will be interpreted as indicative
of a failure of the sub-process to perform editing.

The official definition of \.{LIB\$SPAWN} has about a dozen parameters, but
@.LIB{\$}SPAWN@>
since all of them are optional, and we only need to pass a command (which is the
first parameter) and get back the completion status (which is the seventh),
we'll pretend that it only takes seven parameters.

@d VAX_find_image ==@= lib$find_image_symbol@>
@d VAX_lib_spawn ==@= lib$spawn@>

@<VMS procedures@>=
[VAX_external] function VAX_find_image (@/
     VAX_immed @!filenm : descr_ptr;
     VAX_immed @!symbol : descr_ptr;
     VAX_ref   @!symbol_value : [VAX_volatile,VAX_longword] integer;
     VAX_immed @!image_name : descr_ptr := VAX_immed 0) : integer; @/
   extern;@t\2@>

@#
[VAX_external] function VAX_lib_spawn (@/
      VAX_immed @!cmd : descr_ptr;
      VAX_immed @!sys_input  : descr_ptr := VAX_immed 0;
      VAX_immed @!sys_output : descr_ptr := VAX_immed 0;
      VAX_ref   @!flags  : [VAX_longword] integer := VAX_immed 0;
      VAX_immed @!prcnm  : descr_ptr := VAX_immed 0;
      VAX_ref   @!pid    : [VAX_longword] integer := VAX_immed 0;
      VAX_ref   @!status : [VAX_longword] integer := VAX_immed 0 ): integer; @/
   extern;@t\2@>

@# function Edit ( @!filenm, @!cmd_file : str_number;
                   @!editor   : editor_ident ): edit_result;
  var @!edit_command_line : descr_ptr;
      @!char_ct : sixteen_bits;
      @!editor_entry : integer;
      @!editor_status, @!str_stat : integer;
      @!ch_ptr : chrptr;
      @!quit_status, @!exit_status : integer;
      @!image_symbol, @!entry_point, @!bad_symbol, @!good_symbol : descr_ptr;
      @!edit_file, @!edit_cmd : descr_ptr;
begin
  @<Determine length of the editor command@>;
  edit_command_line := nil;
  @<Copy editor command into dynamic string@>;
  edit_file:=nil; edit_cmd:=nil;
  if editor.EDT_like then {Such editors take \\{filenames} as parameters}
  begin
    str_to_descr(filenm,edit_file);
    str_to_descr(cmd_file,edit_cmd);
  end;
  Edit := failed; {Assume the worst!}
  editor_status := 4; {Neither edited nor quitted}
  quit_status := 0; {Users' command procedures can return this for quitting}
  exit_status := VAX_ss_normal;
  @<Create names of symbols to be sought@>;
  if editor.image <> 0 then {Possibly callable}
  begin
    if VAX_find_image(image_symbol,entry_point,editor_entry)=VAX_ss_normal
    then
      @<Load shareable image and call the editor@>
    else
      editor.image := 0 {Indicate inability to invoke shareable image}
  end;
  if editor.image = 0 then {Use non-shareable-image editing}
    str_stat:=VAX_lib_spawn(cmd:=edit_command_line,status:=editor_status);
  @<Dispose of dynamic strings used by |Edit|@>;
  @<Interpret exit status of editor@>
end;

@ The data structure |editor| contains pool strings giving the name of the
required shareable image and the names of symbols which are to be sought for in
it.  This is where we translate those strings into dynamic ones to be passed to
\.{LIB\$FIND\_IMAGE\_SYMBOL}
@.LIB{\$}FIND_IMAGE_SYMBOL@>

@<Create names of symbols to be sought@>==
  image_symbol := nil; entry_point := nil;
  bad_symbol := nil;   good_symbol := nil;
  str_to_descr(editor.image,image_symbol);
  str_to_descr(editor.entry,entry_point);
  str_to_descr(editor.quitting,bad_symbol);
  str_to_descr(editor.exiting,good_symbol)

@ If we're to invoke a callable editor, we have now obtained its entry point,
which will have caused its image to be loaded into the process's \.{P0} space.
Now we find within the image the values associated with the symbols which
indicate whether the editor was used to create a new file or whether the use
quit without creating a new file (only possible for LSEdit and TPU; with EDT and
TECO, we assume that any successful exit resulted in the creation of a new
file).

@<Load shareable image and call the editor@>=
begin
  @<Preset anticipated exit statuses of the called editor@>;
  if editor.EDT_like then
    editor_status:=call_editor(editor_entry,edit_file,edit_cmd)
  else
    @<Invoke callable LSEdit or TPU@>;
end

@ Just to keep things tidy, we dispose of all dynamic strings used by |Edit|
before exit; this ensures that repeated invocation of an editor will not result
in the ``eating up'' of virtual memory.

@<Dispose of dynamic strings used by |Edit|@>=
  release(image_symbol); release(entry_point); release(bad_symbol);
  release(good_symbol);
  release(edit_command_line); release(edit_file); release(edit_cmd);

@ After the editor, whether running in a spawned sub-process or as a callable
version in a shared image, has returned control to \TeX, we attempt to interpret
its exit status.  Having removed any flag instructing the CLI to ignore the
error status (because the editor will have reported such an error already), we
attempt to match the exit status against the values which we have preset as
indicating normal exit or quit from the editor.  Any other value will leave the
value |failed| to be returned by |Edit|: this should cause \TeX\ to inform the
user that the edit will have to be performed ``off-line''.

@<Interpret exit status of editor@>=
if editor_status>=VAX_ss_ignore then editor_status:=editor_status-VAX_ss_ignore;
if editor_status = exit_status then Edit := edited
else
if editor_status = quit_status then Edit := quit

@ As well as containing the entry point at which the callable editor should be
entered, its image file may also contain global symbols which give the exit
status which will be returned by the editor if the user exits successfully,
having written a new file, or quits without writing a new file.  We extract the
values of these symbols so that this status can be interpreted on exit from this
procedure |Edit|.

@<Preset anticipated exit statuses of the called editor@>=
if editor.quitting<>0 then
  if not odd(VAX_find_image(image_symbol,bad_symbol,quit_status)) then
    quit_status := VAX_ss_normal;
if editor.exiting<>0 then
  if not odd(VAX_find_image(image_symbol,good_symbol,exit_status)) then
    exit_status := VAX_ss_normal

@ If we're invoking the callable version of TPU, we have to remove the
`\.{EDIT/}' from the `\.{EDIT/TPU...}' command that we've constructed in
|edit_command_line|.  This code removes the first |editor.cmd_offset| characters
of the command by overwriting with spaces, which achieves the desired effect.

We then invoke the editor through |call_editor|.

@<Invoke callable LSEdit or TPU@>=
begin
  ch_ptr := edit_command_line^.string :: chrptr;
  for char_ct := 1 to editor.cmd_offset do
  begin
    ch_ptr^ := ' ';    {Expunge the first |cmd_offset| characters}
    ch_ptr := (ch_ptr::integer + 1)::chrptr
  end;
  editor_status:=call_editor(editor_entry,edit_command_line,nil);
end

@ So far, we've managed to construct in the |temp_file| a command to be passed
to the callable editor (through appropriate diversion to that \PASCAL\ internal
file during the analysis of the logical \.{TEX\_EDIT}).  So that we can allocate
@.TEX_EDIT@>
an appropriately sized dynamic string and its descriptor to be passed to the
callable image, we need initially to determine how long that command really is:

@<Determine length of the editor command@>=
reset(temp_file); char_ct:=1;
while not eof(temp_file) do
begin
  get(temp_file); incr(char_ct)
end

@ Now we can allocate the dynamic string to hold the editor command, and copy
the latter into it.  Perhaps it might be thought that this could be simplified,
because we could ``replay'' the command from the |temp_file| into a pool string
by setting |selector| to |new_string| and then using |str_to_descr|: however,
I'm not sure that this would be safe if in so doing we exceeded the allocated
string pool, so we're going to do a bit more work!

@<Copy editor command into dynamic string@>=
new( edit_command_line );
with edit_command_line^ do
begin len := 0;
  desc_type := VAX_char_string;
  desc_class := VAX_class_D;
  string := 0
end;
str_stat := str_allocate( char_ct, edit_command_line );
if not odd(str_stat) then lib_signal(str_stat);
ch_ptr := edit_command_line^.string::chrptr;
reset(temp_file);
while not eof(temp_file) do
begin
  ch_ptr^ := temp_file^;
  get(temp_file);
  ch_ptr := (ch_ptr::integer + 1)::chrptr
end

@ Certain VAX callable editors (\.{LSE} and \.{TPU}) accept a qualifier which
may be used to specify the row and column number at which the editor's cursor is
to be positioned.  This routine adds suitable characters to the editor command
line currently under construction in |temp_file|.

@<Basic printing...@>=
procedure edit_locate(@!line, @!col : integer);
begin
  print("/START_POSITION=("); print_int(line); print_char(",");
  print_int(col); print(") ")
end;

@ The function |edit_file| is called from the error reporting routine with the
context of an input file and the
line number as parameters.  It forms a command for the desired editor
(making using of |temp_file| and various of the error printing routines).

The function returns |true| if it was able to invoke an editor.  If |false| is
returned, the user-interface routine should tell the user what and where to
edit, and exit from \TeX.

First of all, we need to make a forward declaration in order that the code which
interprets the user's response can be compiled to call this procedure.

@<Basic printing...@>=
function edit_file( @!stack_item : in_state_record; line : integer ) : boolean;
forward;

@ But the function itself needs to {\it follow\/} all those declared in \.{WEB}
modules, so we put it just before the main program itself.

To determine what name to use in invoking the editor, this function
attempts to translate the value of \.{/EDITOR}; if the translation is
recognized, then we'll use that as the value, otherwise, we'll use the value
given by \.{/EDITOR}.
If the editing of the file has
(or could have) created a new version of the source file, then steps are taken
to ensure that further edits all access the newly created file(s) rather than
the original.

@<Last-minute procedures@>=

function edit_file;  {|( @!stack_item : in_state_record; line : integer ) :
boolean|}
  var @!equivalence : packed array[1..file_name_size] of char;
    @!equv_len : signed_halfword;
    @!old_setting : integer;
    @!edit_status : edit_result;
    @!edit_ctr : integer;
    @!edit_found : integer;
    @<Variables for |edit_file|@>@;
begin
  old_setting:=selector; selector:=log_only;
  edit_file := false;
  edit_status:=failed; {Assume the worst!}
  equivalence:=edit_name; equv_len:=edit_len;
  if edit_qual then
     if equivalence[equv_len]=':' then begin
         equivalence[equv_len]:=' '; decr(equv_len);
         edit_qual:=translate(equivalence,equv_len);
         end;
  if edit_qual then
      @<Attempt to invoke desired editor@>;
  if edit_status<>failed then
  begin
    edit_file := true;
    if edit_status=edited then
      @<Remove version number from file specification@>
  end;
  selector:=old_setting;
end;

@ If the logical \.{TEX\_EDIT} has a suitable translation, we attempt to
@.TEX_EDIT@>
identify the ``preferred'' editors (preferred in the sense that they can be
invoked from a shareable image, without the overhead of spawning a new process).

@<Attempt to invoke desired editor@>=
begin
  print_nl("Issuing the following command:");
@.Issuing the following command:@>
  @<Upcase the equivalence string@>;
  @<Attempt to recognize name of editor@>;
  if edit_found<>0 then
    @<Invoke callable editor@>
  else
    @<Invoke editor in a sub-process@>;
  copy_err:=ignore_it; selector:=old_setting;
end

@ The equivalence string for \.{TEX\_EDIT} needs to be converted to upper-case,
@.TEX_EDIT@>
to ensure that it may be matched to the names of the preferred editors.

@<Upcase the equivalence string@>=
for edit_ctr:=1 to equv_len do
  if equivalence[edit_ctr] in ['a'..'z'] then
    equivalence[edit_ctr] := xchr[xord[equivalence[edit_ctr]]+"A"-"a"]

@ Now that we have the equivalence string in upper-case, we attempt to match it
with the names of the preferred editors in the data structure |editor|.

For testing equality between two strings, we use VAX-\PASCAL's |index| function.

@d VAX_index==@= index@>

@<Attempt to recognize name of editor@>=
edit_ctr:=1;  edit_found:=0;
while (edit_ctr<=max_editor) and (edit_found=0) do
begin
  if VAX_index(editor[edit_ctr].logical,equivalence) = 1 then
    edit_found:=edit_ctr;
  incr(edit_ctr)
end;

@ Well, we now know that the user wishes to use one of the supported
\\{callable} editors.  So the next move is to construct suitable command strings
and invoke the editor from the appropriate shareable image.

@<Invoke callable editor@>=
with editor[edit_found] do
begin
  rewrite(temp_file); copy_err:=save_it;
  print_nl(cmd_text);
  if start_qual then with stack_item do
    edit_locate(line,loc_field-start_field+1);
  if edit_found=EDT_editor then
    @<Create \.{EDT} command file@>;
  if edit_found=TECO_editor then
    @<Create \.{TECO} command file@>;
  print(stack_item.name_field);
  copy_err:=ignore_it; selector:=old_setting;
  if EDT_like then
  begin
    edit_status := Edit(stack_item.name_field,cmd_file,editor[edit_found]);
    @<Delete an editor command file@>
  end
  else
    edit_status := Edit(0,0,editor[edit_found]);
end

@ The common-or-garden \.{EDT} editor doesn't have a qualifier to specify the
starting position, so we create a small command file, and specify its name on
the \.{/COMMAND} qualifier for \.{EDT}

The command file contains line-mode commands to position the cursor
appropriately.  Strictly speaking, it is illegal to issue a \.{CHANGE} command
(which is the only one that accepts no-keypad commands to position the cursor)
except from a terminal, and \.{EDT} will display a message about this when it
executes the command from the command file; however, it \\{does} honour the
command correctly, so the end does justify the means!

It seemed too complicated to try to delve into \TeX's
``whatsits'', and yet we'll want to use |print|, etc, to transfer text into the
file.  Previous versions of the VMS implementation tried to create the file
using the first free |file| in the array |write_file|.  But this approach
failed, if \TeX\ has got all sixteen available files in use (although this
is a rare case).  To prevent this interference with TeX's own use of its
output streams, the present solution uses a dedicated file |edcmd_file| for
creating the editor command file.  It is access by setting |selector| to
the additional mode |edcmd_write|.

@<Create \.{EDT} command file@>=
begin
  name_of_file:='TEX_EDTINI'; default_name:='.EDT';
  if a_open_out(edcmd_file) then
  begin
    cmd_file:=make_name_string;
    equivalence:='EDTINI'; equv_len:=6; {If it's defined}
    if not translate(equivalence,equv_len) then equv_len:=6;
    copy_err:=ignore_it; selector:=edcmd_write;
    print("SHOW COMMAND");
    print_ln; print("CHANGE "); print_int(line); print_char(";");
    with stack_item do print_int(loc_field-start_field);
    print("(+C)");
    print_ln; print("SET MODE CHANGE");
    print_ln; print("SET COMMAND ");
    for kkk:=1 to equv_len do print_char(xord[equivalence[kkk]]);
    a_close(edcmd_file);
    copy_err:=save_it; selector:=log_only;
    print("/COMMAND="); print(cmd_file); print_char(" ");
  end
end

@ Here are the other variables used in the above module:

@<Variables for |edit_file|@>=
@!kkk : integer;
@!cmd_file : str_number;

@ Neither does the \.{TECO} editor accept such a qualifier, so again we create a
suitable command file.

@<Create \.{TECO} command file@>=
begin
  name_of_file:='TEX_TECOINI'; default_name:='.TEC';
  if a_open_out(edcmd_file) then
  begin
    cmd_file:=make_name_string;
    equivalence:='TEC$INIT'; equv_len:=8; {If it's defined}
    copy_err:=ignore_it; selector:=edcmd_write;
    if translate(equivalence,equv_len) then
    begin
      if equivalence[1]='$' then
      begin
        print("EI");
        for kkk:=2 to equv_len do print_char(xord[equivalence[kkk]]);
      end else
        for kkk:=1 to equv_len do print_char(xord[equivalence[kkk]]);
      print_char(@"1B);
      print_ln;
    end;
    print("@@^U1/"); print_int(line); print("U0");
    with stack_item do print_int(loc_field-start_field);
    print("U20U1<(Q1+1)U1(Q0-Q1-1):;L(.-Z)""LF>'^E""L(Q1+1)U1'P>Q2CT/");
    print_char(@"1B); print_char(@"1B);
    print_ln;
    a_close(edcmd_file);
    copy_err:=save_it; selector:=log_only;
    print("/COMMAND="); print(cmd_file); print_char(" ");
    @<Create logical \.{TEC\$INIT}@>
  end
end

@ Unfortunately, the present version (V40.36) of \.{TECO} does not appear to
make use of the third parameter (which is supposed to be the name of an
initialization file).  Therefore, we create (or redefine) the logical name
\.{TEC\$INIT}, which the callable version of TECO will then use.  Afterwards, of
@.TEC{\$}INIT@>
course, we have to put things back as they were, since otherwise a further
invocation of the editor would introduce a circularity.

The requirement for \.{TEC\$INIT} is that its first character shall be a dollar
sign (`\.\$') to indicate that the rest of the logical name gives the name of a
file to be used for initialization.

@d VAX_create_logical==@= $crelnm@>

@<Create logical \.{TEC\$INIT}@>=
begin
  TECO_cmd := '$';
  kkk:=str_start[cmd_file];
  while kkk<str_start[cmd_file+1] do
  begin
    TECO_cmd:=TECO_cmd+xchr[so(str_pool[kkk])];
    incr(kkk)
  end;
  with item_list[0] do
  begin
    buffer_length := VAX_length(TECO_cmd);
    item_code := VAX_lnm_string;
    buffer_addr := VAX_address_of(VAX_body(TECO_cmd));
    ret_len_addr := 0;
  end;
  item_list[1].next_item := 0;
  VAX_create_logical(,'LNM$PROCESS_TABLE','TEC$INIT',,item_list);
end

@ Here are some additional variables used in the above module.

@<Variables for |edit_file|@>=
@!TECO_cmd  : [VAX_volatile] varying [file_name_size] of char;
@!item_list : [VAX_volatile] array [0..1] of VMS_item_list;


@ After \.{EDT} or \.{TECO} has completed its editing, we are at liberty to
delete the command file that was used to direct the cursor to the appropriate
place.  We've got the full file specification saved up from when the file was
created, so we can go ahead and use the VAX-\PASCAL\ |delete_file| command to
remove the file.

@d VAX_delete_logical==@= $dellnm@>
@d VAX_delete_file   ==@= delete_file@>

@<Delete an editor command file@>=
begin
  if edit_found=TECO_editor then
  begin
    if equv_len>0 then
    begin
      with item_list[0] do
      begin
        buffer_length := equv_len;
        item_code := VAX_lnm_string;
        buffer_addr := VAX_address_of(equivalence);
        ret_len_addr := 0;
      end;
      item_list[1].next_item := 0;
      VAX_create_logical(,'LNM$PROCESS_TABLE','TEC$INIT',,item_list);
    end
    else
      VAX_delete_logical('LNM$PROCESS_TABLE','TEC$INIT');
  end;
  VAX_delete_file(last_name)
end

@ Once a source file has been edited, any further calls of an editor should
access the latest version of the source file, rather than that first opened by
\TeX.  Therefore, as a crude approximation to this desired outcome, we truncate
the file specification held in the pool by substituting spaces for the `\.;'
and any characters that follow it in there.  (This is a good approximation,
since generally any revised file will have been written out to the next higher
version number, and the method adopted is easier than trying to shuffle all of
the pool down to fill the vacant space.)

@<Remove version...@>=
begin
  had_semicolon := false;
  for next_ch := str_start[stack_item.name_field] to
         str_start[stack_item.name_field+1]-1 do
  begin
    if str_pool[next_ch] = si(";") then had_semicolon := true;
    if had_semicolon then str_pool[next_ch] := si(" ")
  end;
end

@ Here's the necessary global variables for the previous module:

@<Variables for |edit_file|@>=
    @!next_ch : pool_pointer;
    @!had_semicolon : boolean;

@ If we were unable to recognize the equivalence string for the \.{TEX\_EDIT}
@.TEX_EDIT@>
logical name, it's assumed to be a DCL command (most probably preceded by an
`\.@@' to invoke a command procedure).  The command will be run in a
sub-process, and provided with three parameters: the name of the file to be
edited, and the row and column numbers (starting from 1) of the file at which
the error was detected.

The following code constructs the requisite DCL command ready to be passed to
the spawned sub-process by procedure |Edit|.  As for the callable editors above,
this command is constructed in the \PASCAL\ internal file |temp_file|, using
various print commands.

@<Invoke editor in a sub-process@>=
begin
  rewrite(temp_file); copy_err:=save_it; print_ln;
  for kkk:=1 to equv_len do print(xord[equivalence[kkk]]);
  print(" "); print(stack_item.name_field); print(" ");
  print_int(line); print(" ");
  with stack_item do print_int(loc_field-start_field+1);
  edit_status := Edit(0,0,empty_editor);
end

@ Here's a dummy |editor| structure to be passed to |Edit| for non-callable
editors:

@<Glob...@>=
  @!empty_editor : editor_ident;

@ and its initialization:

@<Set initial...@>=
with empty_editor do
begin
  logical    := '';
  image      := 0;
  entry      := 0;
  quitting   := 0;
  exiting    := 0;
  cmd_text   := 0;
  cmd_offset := 0;
  start_qual := false;
  EDT_like   := false;
end;
@z