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 Make TeX issue VAX/VMS exit status for use with LSEdit. 22-JUN-1988 BHK Extend save_size to 2000 (from 600). 21-NOV-1988 CNK Set |last_text_char| = 255 (from 127). See TeXhax vol. 88, no. 100 29-NOV-1988 thru 6-DEC-1988 BHK Provide .CLD command-line interface for TeX, etc. 6-DEC-1988 BHK Provide .DIA (diagnostics) file output for use with LSEdit 9-DEC-1988 BHK Call Language-sensitive Editor when user interacts with `E' 17-FEB-1989 CNK Surround the above change with 'LSEdit' 'tidESL' delimiters like 'init' and 'tini'. 20-APR-1989 BHK Build with larger hash table space, to permit more command names. 25-APR-1989 BHK Build with larger save and font tables, for Mainz compatibility 4-MAY-1989 BHK Tidy up use of temp_file after prompting for different file name. 24-AUG-1989 BHK (V2.991-1) Revised for TeX V2.991 Use $TRNLNM instead of $TRNLOG 25-AUG-1989 BHK General tidying up to improve WEAVEability. 29-AUG-1989 BHK Remove buggy definition of \\ from limbo material. Use LIB$FIND_IMAGE_SYMBOL instead of linking with LSESHR. 30-AUG-1989 BHK (V2.991-2) Invoke other (callable) editors, or command procedure 12-SEP-1989 BHK (V2.991-3) Cleanup the nauseous interface to |Edit| 14-SEP-1989 BHK (V2.991-4) Use VAX/VMS dynamic strings and string descriptors for |Edit| 19-SEP-1989 BHK (V2.991-5) Support for TECO 25-SEP-1989 BHK (V2.991-6) Use $CRELNM to pass command file to TECO$EDIT 26-SEP-1989 BHK (V2.991-7) Remove the byte-alignment for |str_pool|; was actually WORSE! 26-SEP-1989 BHK (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 (V2.991-9) Capture exit status of subprocess spawned by LIB$SPAWN 28-SEP-1989 BHK (V2.991-10) 19-OCT-1989 CNK (V2.991-11) Fix bug which causedthe "Entering \batchmode..." message to be deferred until the image exit. 6-NOV-1989 BHK (V2.992 [beta test of V3]) Updated for this major revision of TeX 13-NOV-1989 BHK Minor cosmetic revisions. Replace some occurrences of so(...) on left of equation/assignment by si(...) on the right. 14-NOV-1989 BHK 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 Added DEK's changes (bugfix 339 - see TeXhax V89 #110) (V2.993-0) 10-JAN-1990 BHK Updated for definitive v2.993 of tex.web (dated 8-Jan at 6am PST) 5-APR-1990 BHK Updated for canonical v3 of TeX.web [This would be PD VMS 3.0] 21-APR-1990 thru 27-APR-1990 Don Hosek 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 Incorporated changes for TeX.web V3.1 26-SEP-1990 BHK 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 [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 [PD V3.4] Updated for TeX.web V3.14 26-SEP-1991 BHK [PD V3.4] Updated for alpha-test 3.14a version 03-FEB-1992 BHK [PD V3.4] Updated for alpha-test 3.14b version 17-MAR-1992 BHK [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 - 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 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 - 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 - |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 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 - 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 - 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 - 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 - More reorganization to comply with parallel developement of e-TeX change file. 03-AUG-1995 SPC Christian Spieler - 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 30-AUG-1990 by DAH>>>>> <<<<>>>> >>>>>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 \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 30-AUG-1990 by DAH>>>>> <<<<>>>> >>>>>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 <<<<>>>> {Section 4} @x procedure initialize; {this procedure gets things started properly} @y @t\4@>@@/ procedure initialize; {this procedure gets things started properly} @z <<<<>>>> {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 <<<<>>>> {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 <<<<>>>> <<<<>>>> <<<<>>>> <<<<>>>> {Section 8} @x Parts of the program that are needed in (1) but not in (2) are delimited by 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 Parts of the program that are needed in (1) but not in (2) are delimited by the codewords `$|init|\ldots|tini|$'. @d init==if init_flag then begin @d tini==end; @z <<<<>>>> <<<<>>>> {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@> @= @{@&$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 @= @/@=[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 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@> @= @!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@> @= @!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 (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} <<<<>>>> @x [2] System-dependent character set changes: @^character set dependencies@> @^system dependencies@> @= 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 @= 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} <<<<>>>> @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 <<<<>>>> implement \TeX\ can open a file whose external name is specified by |name_of_file|. @^system dependencies@> @= @!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@> @= @!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} <<<<>>>> @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 <<<<>>>> 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 <<<<>>>> {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. @= @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 @= @!aux_buf:varying [size_input_line] of char; {where the characters go first} @z {Section 31} <<<<>>>> @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 @; 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; 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 <<<<>>>> {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 <<<<>>>> {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) @# @= 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 <<<<>>>> <<<<>>>> <<<<>>>> <<<<>>>> {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 @^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>>>>Added 25-May-1990 DAH--declare all init functions>>>>> @x @p @!init function get_strings_started:boolean; {initializes the string pool, but returns |false| if something goes wrong} @y @p function get_strings_started:boolean; {initializes the string pool, but returns |false| if something goes wrong} @z {Section 47} >>>>>Added 25-May-1990 DAH--declare all init functions>>>>> @x or give an error message and return |false|@>; exit:end; tini @y or give an error message and return |false|@>; exit:end; @z {Section 49} >>>>>Added 26-May-1990 DAH--print ASCII characters in [x'a0',x'fe']. <<<< @z {Section 49} >>>>>Added 26-May-1990 DAH--print ASCII characters in [x'a0',x'fe']. <<<<"~") @y (k<" ")or((k>"~")and (k<160))or(k=255)or((k>=160)and not eight_qual) @z {Section 50} >>>>>Added 25-May-1990 DAH--declare all init functions>>>>> @x @!init @!pool_file:alpha_file; {the string-pool file output by \.{TANGLE}} tini @y @!pool_file:alpha_file; {the string-pool file output by \.{TANGLE}} @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} <<<<>>>> @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} <<<<>>>> @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} <<<<>>>> @x @d max_selector=21 {highest selector setting} @= @!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} @= @!log_file : alpha_file; {transcript of \TeX\ session} @!edcmd_file : alpha_file; {command file used when invoking some editors} @z {Section 56} <<<<>>>> @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) @= 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} <<<<>>>> @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} <<<<>>>> <<<<>>>> @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|. @= procedure print_char(@!s:ASCII_code); {prints a single character} label exit; begin if @ then if selector= procedure print_char(@!s:ASCII_code); {prints a single character} label exit; begin if @ then if selector; 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} @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 <<<<>>>> @d print_err(#)==begin if interaction=error_stop_mode then wake_up_terminal; print_nl("! "); print(#); end @= @!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 @= @!interaction:batch_mode..error_stop_mode; {current level of interaction} @z {Section 82} @x <<<<>>>> @ 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 <<<<>>>> begin if history; @y begin if history; show_context; if interaction=error_stop_mode then @; @z {Section 84} >>>>>Modified 24-MAY-1990 by DAH>>>>> <<<<>>>> @x <<<<>>>> @ 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} <<<<>>>> >>>>>Modified 30-JUN-1990 by DAH>>>>> <<<<>>>> @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} <<<<>>>> @x @= 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 @= 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 @= @!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,,,); @= @!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|} @= @!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|} @= @!glue_ratio=r@&e@&a@&l; {one-word representation of a glue expansion factor} @z {Section 110} <<<<: 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} <<<<>>>> @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 131} >>>>>Added 25-May-1990 DAH--declare all init functions>>>>> @x @p @!init procedure sort_avail; {sorts the available variable-size nodes by location} @y @p procedure sort_avail; {sorts the available variable-size nodes by location} @z {Section 131} >>>>>Added 25-May-1990 DAH--declare all init functions>>>>> @x tini @y @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 @ 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 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 264} >>>>>Added 25-May-1990 DAH--declare all init functions>>>>> @x @p @!init procedure primitive(@!s:str_number;@!c:quarterword;@!o:halfword); @y @p procedure primitive(@!s:str_number;@!c:quarterword;@!o:halfword); @z {Section 264} >>>>>Added 25-May-1990 DAH--declare all init functions>>>>> @x eq_level(cur_val):=level_one; eq_type(cur_val):=c; equiv(cur_val):=o; end; tini @y eq_level(cur_val):=level_one; eq_type(cur_val):=c; equiv(cur_val):=o; end; @z {Section 311} @x <<<<>>>> @@/ begin base_ptr:=input_ptr; input_stack[base_ptr]:=cur_input; {store current state} @y @@/ begin base_ptr:=input_ptr; input_stack[base_ptr]:=cur_input; {store current state} @; @z {Section 311} @x <<<<>>>> done: cur_input:=input_stack[input_ptr]; {restore original state} end; @y done: cur_input:=input_stack[input_ptr]; {restore original state} @; end; @z {Section 313} @x <<<<>>>> @= if name<=17 then if terminal_input then if base_ptr=0 then print_nl("<*>") else print_nl(" ") else begin print_nl(" print_char(">"); end @y @= if name<=17 then begin @; if terminal_input then if base_ptr=0 then print_nl("<*>") else print_nl(" ") else begin print_nl(" print_char(">"); end; @; end @z {Section 313} @x <<<<>>>> else begin print_nl("l."); print_int(line); end; @y else begin print_nl("l."); print_int(line); @; end; @z {Section 314} @x <<<<>>>> @ @= case token_type of parameter: print_nl(" "); @y @ @= @; case token_type of parameter: print_nl(" "); @z {Section 314} @x <<<<>>>> mark_text: print_nl(" "); write_text: print_nl(" "); othercases print_nl("?") {this should never happen} endcases @y mark_text: print_nl(" "); write_text: print_nl(" "); othercases print_nl("?") {this should never happen} endcases; @ @z {Section 317} @x <<<<>>>> @= if trick_count=1000000 then set_trick_count; {|set_trick_count| must be performed} if tallyerror_line then print("...") @y @= if trick_count=1000000 then set_trick_count; {|set_trick_count| must be performed} if tally; 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("..."); @ @z {Section 318} @x <<<<>>>> @= 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 @= 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 @ @z {Section 319} @x <<<<>>>> @ @= begin_pseudoprint; if token_type= begin_pseudoprint; if token_type; @z {Section 416} Correct typo in index entry for \\badness <<<<>>>> <<<<>>>> 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@> @= 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} <<<<>>>> <<<<>>>> <<<< `%' conversion>>>>> <<<<>>>> @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} <<<<>>>> <<<<>>>> <<<<>>>> <<<<>>>> <<<<>>>> @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} @= @!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) @= @!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>>>>> @ @= 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 <<<<>>>> @ 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} <<<<>>>> @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 <<<<>>>> @ 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} <<<<>>>> @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 <<<<>>>> @ 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 <<<<>>>> clear_terminal; prompt_input(": "); @; @y clear_terminal; prompt_input(": "); @; @; @z {Section 532} <<<<>>>> <<<<>>>> @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 <<<<>>>> @ 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 @ @# procedure open_log_file; @z {Section 534} <<<<>>>> <<<<>>>> @x if job_name=0 then job_name:="texput"; @.texput@> pack_job_name(".log"); while not a_open_out(log_file) do @; 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 @; 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 <<<<>>>> @ 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} <<<<>>>> <<<<>>>> @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} <<<<>>>> @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} <<<<>>>> @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 <<<<>>>> 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@> @= @!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} @= @!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@> @= 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} <<<<>>>> <<<<>>>> @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 {Section 942} >>>>>Added 25-May-1990 DAH--declare all init functions>>>>> @x @!init @@; tini @y @@; @z {Section 943} >>>>>Added 25-May-1990 DAH--declare all init functions>>>>> @x @!init@! trie_op_hash:array[-trie_op_size..trie_op_size] of 0..trie_op_size; @y @! trie_op_hash:array[-trie_op_size..trie_op_size] of 0..trie_op_size; @z {Section 943} >>>>>Added 25-May-1990 DAH--declare all init functions>>>>> @x tini @y @z {Section 947} >>>>>Added 25-May-1990 DAH--declare all init functions>>>>> @x @!init @!trie_c:packed array[trie_pointer] of packed_ASCII_code; @y @!trie_c:packed array[trie_pointer] of packed_ASCII_code; @z {Section 947} >>>>>Added 25-May-1990 DAH--declare all init functions>>>>> @x @t\hskip10pt@>@!trie_hash:packed array[trie_pointer] of trie_pointer; {used to identify equivalent subtries} tini @y @t\hskip10pt@>@!trie_hash:packed array[trie_pointer] of trie_pointer; {used to identify equivalent subtries} @z {Section 950} >>>>>Added 25-May-1990 DAH--declare all init functions>>>>> @x @!init@!trie_taken:packed array[1..trie_size] of boolean; {does a family start here?} @y @!trie_taken:packed array[1..trie_size] of boolean; {does a family start here?} @z {Section 950} >>>>>Added 25-May-1990 DAH--declare all init functions>>>>> @x @t\hskip10pt@>@!trie_not_ready:boolean; {is the trie still in linked form?} tini @y @t\hskip10pt@>@!trie_not_ready:boolean; {is the trie still in linked form?} @z {Section 1275} <<<<>>>> @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 <<<<>>>> procedure show_whatever; label common_ending; var p:pointer; {tail of a token list to show} begin case cur_chr of @y procedure show_whatever; label common_ending; var p:pointer; {tail of a token list to show} begin @; case cur_chr of @z {Section 1302} >>>>>Added 25-May-1990 DAH--declare all init functions>>>>> @x @!init procedure store_fmt_file; @y procedure store_fmt_file; @z {Section 1302} >>>>>Added 25-May-1990 DAH--declare all init functions>>>>> @x @; @; end; tini @y @; @; end; @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} <<<<>>>> @x @!ready_already:integer; {a sacrifice of purity for economy} @y @!init_flag: boolean; @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 <<<<>>>> @ 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} <<<<>>> <<<<>>>> @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_flag:=odd(VAX_cli_present('INIT')); @z {Section 1332} @x <<<<>>> @!init if not get_strings_started then goto final_end; @y eight_qual:=odd(VAX_cli_present('EIGHT_BIT')); @./EIGHT_BIT@> @!init if not get_strings_started then goto final_end; @z {Section 1332} <<<<>>>> <<<<>>>> @x ready_already:=314159; start_of_TEX: @; @y start_of_TEX: @; @; @z {Section 1332} <<<<>>>> <<<<>>>> @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; @; end. @z {Section 1333} <<<<>>>> <<<<>>>> <<<<>>>> @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 {Section 1336} >>>>>Added 05-MAY-1990 by DAH--declare all init functions>>>>> @x @!init procedure init_prim; {initialize all the primitives} @y procedure init_prim; {initialize all the primitives} @z {Section 1336} >>>>>Added 05-MAY-1990 by DAH--declare all init functions>>>>> @x no_new_control_sequence:=true; end; tini @y no_new_control_sequence:=true; {Read my lips---there will be no new control sequences.} end; @z {Section 1337} >>>>>Added 28-MAY-1990 by DAH>>>>> @x @; @y @; @; @z <<<<>>>> @x @t\4@>@@; procedure do_extension; var i,@!j,@!k:integer; {all-purpose integers} @!p,@!q,@!r:pointer; {all-purpose pointers} @y @t\4@>@@; procedure do_extension; var k:integer; {all-purpose integers} @!p:pointer; {all-purpose pointers} @z {Section 1379...} >>>>>Modified around 27-MAY-1990 by DAH>>>>>>>>>>>> <<<<>>>> <<<<>>>> <<<<>>>> <<<<>>>> <<<<>>>> <<<<>>>> <<<<>>>> <<<<>>>> <<<<>>>> <<<<>>>> <<<<>>>> <<<<>>>> <<<<>>>> @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 @> @= 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 @> @= 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 @= 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|. @= @!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. @= 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. @= 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. @= 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. @= 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. @= 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: @= 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. @= 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. @= 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. @= 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. @= 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. @= 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. @= 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|. @= @!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. @= 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@> @= [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@> @= @!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@> @= 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. @= 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: @= @!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 @> @= @!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; @ @= @!res: [VAX_volatile] integer; @!tt_chan: [VAX_volatile] signed_halfword; @ @= [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 @> @= @!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@> @= 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; @ @= @!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@> @= 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. @== @!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@> @= [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 @> @= [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|. @= 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. @= @!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. @= 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: @= @!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 @= @!editor : packed array [1..max_editor] of editor_ident; @ And we needs must initialize them: @= 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: @= @!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@> @= [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 @; edit_command_line := nil; @; 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; @; if editor.image <> 0 then {Possibly callable} begin if VAX_find_image(image_symbol,entry_point,editor_entry)=VAX_ss_normal then @ 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); @; @ 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@> @== 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). @= begin @; if editor.EDT_like then editor_status:=call_editor(editor_entry,edit_file,edit_cmd) else @; 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. @= 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''. @= 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|. @= 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|. @= 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: @= 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! @= 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|. @= 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. @= 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. @= 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; @@; 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 @; if edit_status<>failed then begin edit_file := true; if edit_status=edited then @ 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). @= begin print_nl("Issuing the following command:"); @.Issuing the following command:@> @; @; if edit_found<>0 then @ else @; 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. @= 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@> @= 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. @= 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 @; if edit_found=TECO_editor then @; 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]); @ 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|. @= 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: @= @!kkk : integer; @!cmd_file : str_number; @ Neither does the \.{TECO} editor accept such a qualifier, so again we create a suitable 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(" "); @ 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@> @= begin TECO_cmd := '$'; kkk:=str_start[cmd_file]; while kkk= @!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@> @= 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.) @= 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: @= @!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. @= 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: @= @!empty_editor : editor_ident; @ and its initialization: @= 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