(c) Copyright 1983, 1984 by Charles Karney and Phil Andrews This is the changes file produced by Phil Andrews and Charles Karney for TeX, based on the one for Tops-20 by DRF When compiling for INITEX, it is necessary to reduce the following constants: mem_max=58000 to mem_max=45000 string_vacancies=15000 to string_vacancies=13000 pool_size=40000 to pool_size=37000 Here is a description of the changes Print banner line Initialization procedure Compile time constants Make all characters readable File opening and closing File input Open TTY Implement wake_up_terminal Initialize terminal Enter editing hooks 36-bit words; quarter and half words start at zero Date and time Establish system areas and parse file names Type out CPU time & run editor Besides implementing the various system dependent parts of TeX, this implements the following features (1) All control characters (except null) in files are read (2) Command line is scanned and remembered (3) CPU time is printed at the end of the run (4) The E option for the editor (5) Finds system PPNs from logical names Record of changes (started a bit late) 83/02/19 Reads all control characters Scans command line 83/03/23 Remembers command line 83/03/26 Updated for 0.96 (hi, ho, pool_size) Print out CPU time 83/03/28 Removed sensitive debug, stats, compile time constants, bad_fonts_code; increased save_size (to match TEX.WEB) 83/03/29 Changed some directories to aid in porting versions 83/04/04 Changed PPPL to MFE in Banner Introduced first hooks for calling editor Changed input_ln so that form_feed acts as end-of-line (needed for consistency with FINE) 83/04/08 Cleaned up editing stuff 83/04/13 Moved fonts to [120,44,TFM] 83/04/14 Made INITEX set the system PPNs from logical names (still need to do it for formats) 83/04/19 Updated for TeX 0.97 wake_up_terminal implemented 83/04/20 Use normal TTY for output (fixes 'Improper mode for file TTY:TTY' bug with detached jobs) 83/05/01 Use TMP:TEX instead of nnnTEX.TMP, since DELETE no longer works with the new COMPIL. 83/07/07 Updated for TeX 0.99 and TANGLE 1.7 83/07/29 Updated for TeX 0.999. 83/08/02 Updated for TeX 0.9999. 83/08/03 Allowed fonts names of greater than 6 chars by take first 3 and last 3 characters. 83/08/18 Finish jump-to-editor. 83/08/22 Support devices in file names. Changed TEX.POO and PLAIN.FMT to be on TEXFMT:. (TEX_area and TEX_font_area can be switched to logical device names when they are eslablished at the system level). 83/09/09 Change all devices to TeX: 83/09/31 Remove DISPOSE---not worth the overhead. 83/11/29 Updated for TeX 1.0. Remove change for line length (since FINE now handles 79 char lines on a 80 wide terminal). Change MFE to TOPS-10 in banner. 84/01/05 Increased various limits for LaTeX. Values taken from Tops-20 change file. 84/01/14 Changed TEX to LATEX in set_up_editing. Added copyright notice. 84/04/12 Change set_up_editing to read program name at run-time. Print banner line @x @d banner=='This is TeX, Version 1.0' {printed when \TeX\ starts} @y @d banner=='This is TeX82, TOPS-10 Version 1.0' {printed when \TeX\ starts} @z Initialization procedure @x @d init== {change this to `$\\{init}\equiv\.{@@\{}$' in the production version} @d tini== {change this to `$\\{tini}\equiv\.{@@\}}$' in the production version} @y CHANGE FOR INITEX @d init==@{ {change this to `$\\{init}\eqv\.{@@\{}$' in the production version} @d tini==@} {change this to `$\\{tini}\eqv\.{@@\}}$' in the production version} @z Compile-time constants @x [1] Compile-time constants: @!mem_max=30000; {greatest index in \TeX's internal |mem| array, must be strictly less than |max_halfword|} @!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} @!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 CHANGE FOR INITEX @!mem_max=58000; {greatest index in \TeX's internal |mem| array, must be strictly less than |max_halfword|} @!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=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=6; {maximum number of input files and error insertions that can be going on simultaneously} @!font_max=100; {maximum internal font number, must not exceed |max_quarterword|} @!font_mem_size=25000; {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=4400; {maximum number of strings} @!string_vacancies=15000; {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=40000; {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} @!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='TeX:TEX.POO '; {string of length |file_name_size|, where string pool appears} @z @x [1] TANGLE-time constants: @d mem_base=0 {smallest index in the |mem| array, must not be less than |min_halfword|} @d hi_mem_base=13000 {smallest index in the single-word area of |mem|, must be substantially larger than |mem_base| and smaller 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-hi_mem_base)/6|, but 2100 is already quite generous} @d hash_prime=1777 {a prime number equal to about 85\% of |hash_size|} @d hyph_size=307 {another prime; the number of \.{\\hyphenation} exceptions} @y @d mem_base=0 {smallest index in the |mem| array, must not be less than |min_halfword|} @d hi_mem_base=25000 {smallest index in the single-word area of |mem|, must be substantially larger than |mem_base| and smaller than |mem_max|} @d font_base=0 {smallest internal font number, must not be less than |min_quarterword|} @d hash_size=2500 {maximum number of control sequences; it should be at most about |(mem_max-hi_mem_base)/6|, but 2500 is already quite generous} @d hash_prime=2129 {a prime number equal to about 85\% of |hash_size|} @d hyph_size=307 {another prime; the number of \.{\\hyphenation} exceptions} @z Make all characters readable. @x for i:=1 to @'37 do xchr[i]:=' '; @y for i:=1 to @'37 do xchr[i]:=chr(i); @z File opening and closing @x @d reset_OK(#)==erstat(#)=0 @y @d reset_OK(#)==(erstat(#)=0) or (erstat(#)=@'20000) {empty file} @z @x @p function a_open_in(var f:alpha_file):boolean; {open a text file for input} begin reset(f,name_of_file,'/O'); a_open_in:=reset_OK(f); @y @p function erstat(var f:file):integer; extern; {in the runtime library} function a_open_in(var f:alpha_file):boolean; {open a text file for input} begin reset(f,name_of_file,'/O/E'); a_open_in:=reset_OK(f); {/E turns off end-of-line censor} @z @x function b_open_in(var f:byte_file):boolean; {open a binary file for input} begin reset(f,name_of_file,'/O'); b_open_in:=reset_OK(f); @y function b_open_in(var f:byte_file):boolean; {open a binary file for input} begin reset(f,name_of_file,'/O/B:8'); b_open_in:=reset_OK(f); @z @x function b_open_out(var f:byte_file):boolean; {open a binary file for output} begin rewrite(f,name_of_file,'/O'); b_open_out:=rewrite_OK(f); @y function b_open_out(var f:byte_file):boolean; {open a binary file for output} begin rewrite(f,name_of_file,'/O/B:8'); b_open_out:=rewrite_OK(f); @z File input @x @ Input from text files is read one line at a time, using a routine called |input_ln|. This function is defined in terms of global variables called |buffer|, |first|, and |last| that will be described in detail later; for now, it suffices for us to know that |buffer| is an array of |ASCII_code| values, and that |first| and |last| are indices into this array representing the beginning and ending of a line of text. @= @!buffer:array[0..buf_size] of ASCII_code; {lines of characters being read} @!first:0..buf_size; {the first unused position in |buffer|} @!last:0..buf_size; {end of the line just input to |buffer|} @!max_buf_stack:0..buf_size; {largest index used in |buffer|} @y @ Input from text files is read one line at a time, using a routine called |input_ln|. This function is defined in terms of global variables called |buffer|, |first|, and |last| that will be described in detail later; for now, it suffices for us to know that |buffer| is an array of |ASCII_code| values, and that |first| and |last| are indices into this array representing the beginning and ending of a line of text. We will read the lines first into an auxiliary buffer, in order to save the running time of procedure-call overhead. @^Knuth, Donald Ervin@> @d line_feed=@'12 {ASCII code for line feed} @d form_feed=@'14 {ditto for form feed} @d ASCII_space=@'40 {ditto for space} @d aux_buf_size=80 @= @!buffer:array[0..buf_size] of ASCII_code; {lines of characters being read} @!first:0..buf_size; {the first unused position in |buffer|} @!last:0..buf_size; {end of the line just input to |buffer|} @!max_buf_stack:0..buf_size; {largest index used in |buffer|} @!aux_buf:array[0..aux_buf_size-1] of text_char; {where the characters go first} @!cpu_time:nonnegative_integer; {cumulative runtime in ms} @z @x @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:30} 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 overflow("buffer size",buf_size); @:TeX capacity exceeded buffer size}{\quad buffer size@> 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 @p function input_ln(var f:alpha_file;@!bypass_eoln:boolean):boolean; {inputs the next line or returns |false|} label 1,2,3; var n: integer; @!full,@!fake_eol,@!form_eol:boolean; @!k,@!m: 0..buf_size; {indices into |buffer|} begin if bypass_eoln then {input the first character of the line into |f^|} begin if not eof(f) then get(f); if not eof(f) then if f^=chr(line_feed) then get(f); {skip past a |line_feed|} end; last:=first; if eof(f) then input_ln:=false else begin 1: full:=false; fake_eol:=false; form_eol:=false; read(f,aux_buf:n); 2: if (n>aux_buf_size) then begin n:=n-1; full:=true end; {aux_buf overflowed} if last+n>max_buf_stack then if last+n>=buf_size then begin max_buf_stack:=buf_size; overflow("buffer size",buf_size); @:TeX capacity exceeded buffer size}{\quad buffer size@> end else max_buf_stack:=last+n; if n>0 then begin m:=last; last:=m+n; for k:=m to last-1 do buffer[k]:=xord[aux_buf[k-m]]; if full then {there's more on this line} goto 1; end; if form_eol then goto 3; {simulate new line with formfeed} if fake_eol then goto 1; if (f^<>chr(carriage_return)) and (f^<>chr(ASCII_space)) then {only allow carriage_return or space (for TTY) as end-of-line} begin aux_buf[0]:=f^; n:=1; if f^=chr(form_feed) then form_eol:=true else begin get(f); fake_eol:=true end; goto 2 end; while last>first do if buffer[last-1]<>" " then goto 3 else decr(last); 3: input_ln:=true; end; end; @z Open TTY @x @ The user's terminal acts essentially like other files of text, except that it is used both for input and for output. When the terminal is considered an input file, the file variable is called |term_in|, and when it is considered an output file the file variable is |term_out|. @^system dependencies@> @= @!term_in:alpha_file; {the terminal as an input file} @!term_out:alpha_file; {the terminal as an output file} @y @ The user's terminal acts essentially like other files of text, except that it is used both for input and for output. When the terminal is considered an input file, the file variable is called |term_in|, and when it is considered an output file the file variable is |term_out|. Here, we use the built-in |TTY| file for output, but open a file explicitly for input. On input this allows ^Z to be used as an end-of-file character. If a file is explicity opened for terminal output, then the output happens even when the \TeX82 is |CCONT|ed. If the job is then detached, you get an `Improper mode' error. @^system dependencies@> @d term_out==TTY {the terminal as an output file} @= @!term_in:alpha_file; {the terminal as an input file} @z @x @d t_open_out==rewrite(term_out,'TTY:','/O') {open the terminal for text output} @y @d t_open_out==do_nothing {open the terminal for text output} @z Implement wake_up_terminal and call_fine @x @d wake_up_terminal == do_nothing {cancel the user's cancellation of output} @y @d wake_up_terminal == undo_ctrl_O {cancel the user's cancellation of output} @p procedure undo_ctrl_O; var val:integer; arg:array[0..2] of integer; success:boolean; begin calli(@'115,,-1,val,success);{TRMNO gets UDX code for 0=this terminal} if (val<>0) then begin {0 is returned for detached jobs} arg[0]:=@'2045; {.TOOSU=1045+.TOSET=1000 sets the ^O state} arg[1]:=val; {UDX code} arg[2]:=0; {0 means output switched on} {TRMOP does the action, 3 is the number of arguments} calli(@'116,3,arg,val,success) end end; procedure call_fine; {procedure to call the editor FINE on exit} mtype name_type = (dev, fil_name, ext, dummy, ppn_no, core); pack6 = packed array[1..6] of char; var val: integer; skipp : boolean; run_name : array[name_type] of integer; function conv_six_bit(line_in : pack6):integer; mtype six_bit = 0..63; word = record case boolean of true : ( line : packed array[1..6] of six_bit;); false : ( int : integer;); end; var temp : word; i :integer; begin for i := 1 to 6 do temp.line[i] := ord(line_in[i]) - @'40; conv_six_bit := temp.int; end; begin run_name[dev] := conv_six_bit('SYS '); run_name[fil_name] := conv_six_bit('FINE '); run_name[ext] := conv_six_bit('EXE '); run_name[dummy] := 0; run_name[ppn_no] := 0; run_name[core] := 0; calli(@'35, 1, run_name, val, skipp); end; @z Initialize terminal @x @ 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 @p function rescan:boolean; extern; @t\2@>@; {puts the command line into the terminal buffer, or returns |false| if there was no command line} @# function init_terminal:boolean; {gets the terminal files started} mtype @!lin=record blank:integer; txt:packed array[0..aux_buf_size-1] of char end; @!args=packed record nam:halfword;blank:halfword; len:halfword;ptr:^lin end; var @!xblock:args;@!lina:^lin; @!val,i,j:integer; @!success:boolean; @!delim_char:char; @# procedure init_tmp_cor; begin new(lina); xblock.nam:=@'644570; {SIXBIT /TEX/} xblock.blank:=0; {zero out rest of word} xblock.ptr:=lina; {pointer to word before text} end; @# function read_tmp_file:boolean; begin val:=aux_buf_size div 5; xblock.len:=@'1000000-val; calli(@'44,1,xblock,val,success); {TMPCOR call, 1 => read} if val=0 then success:=false; if success then begin j:=5*val; {number of characters in file} loc:=first; last:=first+j; for i:=0 to j-1 do buffer[loc+i]:=ord(xblock.ptr^.txt[i]); end; read_tmp_file:=success; end; @# procedure write_tmp_file; begin j:=last-loc; if j>aux_buf_size then j:=aux_buf_size; for i:=0 to j-1 do xblock.ptr^.txt[i]:=chr(buffer[loc+i]); val:=(j+4) div 5; for i:=j to 5*val-1 do xblock.ptr^.txt[i]:=' ';{pad to end of word} xblock.len:=@'1000000-val; {-length in words} if val<>0 then {if empty, delete file} calli(@'44,3,xblock,val,success) {TMPCOR call, 3 => write} else calli(@'44,2,xblock,val,success); {TMPCOR call, 2 => delete} end; @# function read_jcl:boolean; begin read_jcl:=false; debug if false then@;@+gubed@;@/ if rescan then begin loc:=first; last:=first; read_ln(term_in); {get first character into |term_in^|} while (not eoln(term_in)) and (term_in^=' ') do get(term_in); {skip leading spaces} if ((term_in^='R') or (term_in^='r')) then delim_char:=';' else delim_char:=' '; while (not eoln(term_in)) and (term_in^<>delim_char) do get(term_in); {skip TEX or R TEX} if not eoln(term_in) then get(term_in); {skip delim_char} while not eoln(term_in) do begin buffer[last]:=xord[term_in^]; incr(last); get(term_in) end; end; while (loc 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= procedure jump_out; begin goto end_of_TEX; end; @y The procedure |set_up_editing|, writes out a TMP: file for FINE to read. This causes FINE to read in the file to be edited and the position the cursor at the right line. It will also cause FINE to run TEX when exited. @= procedure jump_out; begin goto end_of_TEX; end; @# {Declare this here for use by error procedures} procedure undo_ctrl_O; forward; @# procedure set_up_editing; const this_buf_size = 150; mtype halfword = 0..@'777777; lin=record blank:integer; txt:packed array[0..this_buf_size] of char end; args=packed record nam:halfword;blank:halfword; len:halfword;ptr:^lin end; var xblock:args;lina:^lin; procedure init_commands; begin new(lina); xblock.nam:= @'454464; {SIXBIT /EDT/} xblock.blank:=0; {zero out rest of word} xblock.ptr:=lina; {pointer to word before text} end; procedure write_commands; const ctrl_u = @'25; ctrl_n = @'16; esc = @'33; mtype six_bit = 0..63; word = record case boolean of true : ( line : packed array[1..6] of six_bit;); false : ( int : integer;); end; var success : boolean; i, s, val : integer; dummy : file of char; j:pool_pointer; temp : word; begin strwrite(dummy, xblock.ptr^.txt); {fake file} write(dummy, 's'); {precursor to file name} s:=input_stack[base_ptr].name_field; for j:=str_start[s] to str_start[s+1]-1 do write(dummy,xchr[str_pool[j]]); {file name} write(dummy, chr(@'15), chr(@'12)); calli(@'41,-1,3,temp.int,success); {GETTAB call to read program name} for i:=1 to 6 do if (temp.line[i]>0) then write(dummy, chr(@'40+temp.line[i])); write(dummy, '!',chr(@'15), chr(@'12)); {run tex on exit from fine} write(dummy, chr(esc), '<', chr(ctrl_u), (line-1):1, chr(ctrl_n), chr(0)); { go down line-1 lines } getindex(dummy, i); xblock.len:=@'1000000-((i+4) div 5); {-length in words} calli(@'44,3,xblock,val,success) {TMPCOR call, 3 => write} end; begin init_commands; write_commands; run_editor:=true; end; @z @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 what file should be edited and the relevant line number. @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. What we do here is write a TMP: file which will be read by FINE when we call with an offset of 1 on leaving \TeX. FINE will then read in the source file and position the cursor at the correct line. Note that on leaving FINE \TeX\ will be run automatically. @z @x begin print_nl("You want to edit file "); @.You want to edit file x@> print(input_stack[base_ptr].name_field); print(" at line "); print_int(line); interaction:=scroll_mode; jump_out; @y begin print_nl("Calling FINE to edit file "); @.You want to edit file x@> print(input_stack[base_ptr].name_field); print(" at line "); print_int(line); interaction:=scroll_mode; set_up_editing; jump_out; @z @x if base_ptr>0 then print("E to edit your file,"); @y if base_ptr>0 then print("E to edit your file using FINE,"); @z 36-bit words; quarter and half words start at zero @x It is usually most efficient to have |min_quarterword=min_halfword=0|, so one should try to achieve this unless it causes a severe problem. The values defined here are recommended for most 32-bit computers. @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 It is usually most efficient to have |min_quarterword=min_halfword=0|, so one should try to achieve this unless it causes a severe problem. The values defined here are recommended for most 36-bit computers. @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==262143 {largest allowable value in a |halfword|} @z @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(#)==# {to put an |eight_bits| item into a quarterword} @d qo(#)==# {to take an |eight_bits| item from a quarterword} @d hi(#)==# {to put a sixteen-bit item into a halfword} @d ho(#)==# {to take a sixteen-bit item from a halfword} @z Date and time @x 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} end; @y @d ptime==t@&i@&m@&e @p procedure do_date(var min_since_midnite:integer); { DATE result is a PACKED ARRAY [1..9] OF CHAR. The date is returned in the form 'DD-Mmm-YY'. Here we use CALLI 14,ac. This means we don't have to decode the Mmm. TIME current time in msec (type integer) } var success:boolean; date_code : integer; begin calli(@'14,0,0,date_code,success); { returns 31*(12*(year-1964)+(month-1))+(day-1) in date_code } day := (date_code mod 31)+1; month := ((date_code div 31) mod 12)+1; year := (date_code div (31*12))+1964; min_since_midnite := ptime div (1000*60); end; procedure fix_date_and_time; begin do_date(time); end; @z Establish system areas and parse file names @x @ The file names we shall deal with have the 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 `\..' 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: @= @!area_delimiter:pool_pointer; {the most recent `\.>' or `\.:', if any} @!ext_delimiter:pool_pointer; {the relevant `\..', if any} @y @ The file names we shall deal with have the following structure: If the name contains `\.:' or `\.[', the file area consists of all characters before and including the `\.:' concatenated with everything beyond (and including) the `.['. If the remaining file name contains `\..', the file extension consists of all such characters from this character to the @\.[' or end, otherwise the file extension is null. We can assume that there is at most one of each of \.:', `\.[' and `\..'. This is all compliciated by the presence of device and PPN specifications at opposite ends of the file names. The trick is to combine them together for internal TeX consumption, and split them apart in |print_file_name| and |pack_file_name|. We can scan such file names easily by using three global variables that keep track of the occurrences of area and extension delimiters: @d file_buf_size=40 @= @!dev_delimiter:-1..file_buf_size; {the most recent `\.:', if any} @!area_delimiter:-1..file_buf_size; {the most recent `\.[', if any} @!ext_delimiter:-1..file_buf_size; {the relevant `\..', if any} @!file_buf:packed array[0..file_buf_size-1] of 0..127; {where the file names first} @!file_index:0..file_buf_size; {index into file_buf} @^system dependencies@> @z @x @d TEX_area=="TeXinputs:" @.TeXinputs@> @d TEX_font_area=="TeXfonts:" @.TeXfonts@> @y @d TEX_area=="TeX:" @.TeXinputs@> @d TEX_font_area=="TeX:[,,TFM]" @.TeXfonts@> @z @x @p procedure begin_name; begin area_delimiter:=0; ext_delimiter:=0; end; @ And here's the second. @^system dependencies@> @p function more_name(@!c:ASCII_code):boolean; begin if c=" " then more_name:=false else begin if (c=">")or(c=":") then begin area_delimiter:=pool_ptr; ext_delimiter:=0; end else if (c=".")and(ext_delimiter=0) then ext_delimiter:=pool_ptr; str_room(1); append_char(c); {contribute |c| to the current string} more_name:=true; end; end; @ 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; incr(str_ptr); str_start[str_ptr]:=area_delimiter+1; end; if ext_delimiter=0 then begin cur_ext:=""; cur_name:=make_string; end else begin cur_name:=str_ptr; incr(str_ptr); str_start[str_ptr]:=ext_delimiter; cur_ext:=make_string; end; end; @y @p procedure begin_name; begin dev_delimiter:=-1; area_delimiter:=-1; ext_delimiter:=-1; file_index:=0 end; @ And here's the second. @^system dependencies@> @p function more_name(@!c:ASCII_code):boolean; begin if (c<=" ") or (c>"~") or (file_index=file_buf_size) then more_name:=false else begin if (c=":") and (ext_delimiter<0) and (area_delimiter<0) then dev_delimiter:=file_index else if (c=".") and (area_delimiter<0) then ext_delimiter:=file_index else if (c="[") then area_delimiter:=file_index; file_buf[file_index]:=c; incr(file_index); more_name:=true; end; end; @ The third. @^system dependencies@> @p procedure end_name; var j:0..file_buf_size-1; 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) and (dev_delimiter<0) then cur_area:="" else begin if dev_delimiter>=0 then begin str_room(dev_delimiter+1); for j:=0 to dev_delimiter do append_char(file_buf[j]) end; if area_delimiter>=0 then begin str_room(file_index-area_delimiter); for j:=area_delimiter to file_index-1 do append_char(file_buf[j]) end; cur_area:=make_string end; if area_delimiter<0 then area_delimiter:=file_index; if (ext_delimiter<0) then begin cur_ext:=""; ext_delimiter:=area_delimiter end else begin str_room(area_delimiter-ext_delimiter); for j:=ext_delimiter to area_delimiter-1 do append_char(file_buf[j]); cur_ext:=make_string end; str_room(ext_delimiter-dev_delimiter-1); for j:=dev_delimiter+1 to ext_delimiter-1 do append_char(file_buf[j]); cur_name:=make_string end; @z @x procedure print_file_name(@!n,@!a,@!e:integer); begin print(a); print(n); print(e); end; @y procedure print_file_name(@!n,@!a,@!e:str_number); var delim,j:pool_pointer; begin delim:=str_start[a+1]-1; while (delim>=str_start[a]) and (str_pool[delim]<>":") do decr(delim); for j:=str_start[a] to delim do print_char(str_pool[j]); print(n); print(e); for j:=delim+1 to str_start[a+1]-1 do print_char(str_pool[j]); end; @z @x @p procedure pack_file_name(@!n,@!a,@!e:str_number); var k:integer; {number of positions filled in |name_of_file|} @!c: ASCII_code; {character being packed} @!j:pool_pointer; {index into |str_pool|} begin k:=0; for j:=str_start[a] to str_start[a+1]-1 do append_to_name(str_pool[j]); for j:=str_start[n] to str_start[n+1]-1 do append_to_name(str_pool[j]); for j:=str_start[e] to str_start[e+1]-1 do append_to_name(str_pool[j]); if k<=file_name_size then name_length:=k@+else name_length:=file_name_size; for k:=name_length+1 to file_name_size do name_of_file[k]:=' '; end; @y @p procedure pack_file_name(@!n,@!a,@!e:str_number); var k:integer; {number of positions filled in |name_of_file|} @!c: ASCII_code; {character being packed} @!delim,j:pool_pointer; {index into |str_pool|} begin delim:=str_start[a+1]-1; while (delim>=str_start[a]) and (str_pool[delim]<>":") do decr(delim); k:=0; for j:=str_start[a] to delim do append_to_name(str_pool[j]); {Handle fonts whose names are longer than 6 characters by taking the first 3 and last 3 characters only.} if (e=".tfm") and (length(n)>6) then begin for j:=str_start[n] to str_start[n]+2 do append_to_name(str_pool[j]); for j:=str_start[n+1]-3 to str_start[n+1]-1 do append_to_name(str_pool[j]); end else for j:=str_start[n] to str_start[n+1]-1 do append_to_name(str_pool[j]); for j:=str_start[e] to str_start[e+1]-1 do append_to_name(str_pool[j]); for j:=delim+1 to str_start[a+1]-1 do append_to_name(str_pool[j]); if k<=file_name_size then name_length:=k@+else name_length:=file_name_size; for k:=name_length+1 to file_name_size do name_of_file[k]:=' '; end; @z @x @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} @y @d format_default_length=13 {length of the |TEX_format_default| string} @d format_area_length=4 {length of its area part} @d format_ext_length=4 {length of its `\.{.fmt}' part} @z @x TEX_format_default:='TeXformats:PLAIN.fmt'; @y TEX_format_default:='TeX:plain.fmt'; @z Type out CPU time & run editor @x wake_up_terminal; @; if job_name>0 then begin wlog_cr; a_close(log_file); selector:=selector-2; if selector=term_only then begin print_nl("Transcript written on "); print(log_name); print_char("."); end; end; end; @y wake_up_terminal; @; cpu_time:=runtime-cpu_time; print_nl("CPU time = "); print_int(cpu_time div 1000); print_char("."); print_two(cpu_time div 10); print(" s."); if job_name>0 then begin wlog_cr; a_close(log_file); selector:=selector-2; if selector=term_only then begin print_nl("Transcript written on "); print(log_name); print_char("."); end; end; if run_editor then call_fine; end; @z