diff options
Diffstat (limited to 'Build/source/texk/web2c/mf.ch')
-rw-r--r-- | Build/source/texk/web2c/mf.ch | 2251 |
1 files changed, 2251 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/mf.ch b/Build/source/texk/web2c/mf.ch new file mode 100644 index 00000000000..e9ea738a48e --- /dev/null +++ b/Build/source/texk/web2c/mf.ch @@ -0,0 +1,2251 @@ +% mf.ch for C compilation with web2c, derived from various other change +% files: INITEX.CH for Berkeley Unix TeX 1.1 (by Howard Trickey and +% Pavel Curtis), by Paul Richards. web2c modifications by Tim Morgan, et al. +% +% (more recent changes in ChangeLog) +% Revision 2.0 90/3/27 20:20:00 ken To version 2.0. +% Revision 1.9 90/1/20 09:05:32 karl To version 1.9. +% Revision 1.8 89/11/30 09:08:16 karl To version 1.8. +% Revision 1.7 88/12/27 15:02:24 mackay Cosmetic upgrade for version 1.7 +% Revision 1.6 88/12/11 15:59:15 morgan Brought up to MF version 1.6 +% Revision 1.5 88/03/02 13:25:44 morgan More C changes +% Revision 1.4 87/12/09 12:50:00 hesse Changes for C version +% Revision 1.3 87/03/07 21:15:21 mackay +% Minor changes found on archive version on SCORE +% Revision 1.2 86/09/29 21:46:43 mackay +% Made no-debug the default, and changed version number +% to correspond with improved mf.web file +% (Got rid of debug code to avoid bug in range check +% code of VAX4.3 BSD and SUN3 version 3.1 Os pc interpreter) +% Revision 1.0 86/01/31 15:46:08 richards +% Incorporates: New binary I/O library, separate optimized +% arithmetic for takefraction/makefraction, new graphics interface. + +@x [0] WEAVE: print changes only. +\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in} +@y +\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in} +\let\maybe=\iffalse +\def\title{\MF\ changes for C} +\def\glob{13}\def\gglob{20, 25} % these are defined in module 1 +@z + +@x [1.7] Convert `debug..gubed' and `stat..tats' into #ifdefs. +@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging} +@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging} +@y +@d debug==ifdef('TEXMF_DEBUG') +@d gubed==endif('TEXMF_DEBUG') +@z + +@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==ifdef('STAT') +@d tats==endif('STAT') +@z + +@x [1.8] Same, for `init..tini'. +@d init== {change this to `$\\{init}\equiv\.{@@\{}$' in the production version} +@d tini== {change this to `$\\{tini}\equiv\.{@@\}}$' in the production version} +@y +@d init==ifdef('INIMF') +@d tini==endif('INIMF') +@z + +% [1.11] Compile-time constants. Although we only change a few of +% these, listing them all makes the patch file for a big Metafont simpler. +% 16K for BSD I/O; file_name_size is set from the system constant. +@x +@<Constants...@>= +@!mem_max=30000; {greatest index in \MF's internal |mem| array; + must be strictly less than |max_halfword|; + must be equal to |mem_top| in \.{INIMF}, otherwise |>=mem_top|} +@!max_internal=100; {maximum number of internal quantities} +@!buf_size=500; {maximum number of characters simultaneously present in + current lines of open files; 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} +@!screen_width=768; {number of pixels in each row of screen display} +@!screen_depth=1024; {number of pixels in each column of screen display} +@!stack_size=30; {maximum number of simultaneous input sources} +@!max_strings=2000; {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 identifier names and strings, + after \MF'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 identifiers; + must exceed |string_vacancies| by the total + length of \MF's own strings, which is currently about 22000} +@!move_size=5000; {space for storing moves in a single octant} +@!max_wiggle=300; {number of autorounded points per cycle} +@!gf_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='MFbases:MF.POOL '; + {string of length |file_name_size|; tells where the string pool appears} +@.MFbases@> +@!path_size=300; {maximum number of knots between breakpoints of a path} +@!bistack_size=785; {size of stack for bisection algorithms; + should probably be left at this value} +@!header_size=100; {maximum number of \.{TFM} header words, times~4} +@!lig_table_size=5000; {maximum number of ligature/kern steps, must be + at least 255 and at most 32510} +@!max_kerns=500; {maximum number of distinct kern amounts} +@!max_font_dimen=50; {maximum number of \&{fontdimen} parameters} +@y +@d file_name_size == maxint +@d ssup_error_line = 255 + +@<Constants...@>= +@!max_internal=300; {maximum number of internal quantities} +@!screen_width=1664; {number of pixels in each row of screen display} +@!screen_depth=1200; {number of pixels in each column of screen display} +@!stack_size=300; {maximum number of simultaneous input sources} +@!max_strings=7500; {maximum number of strings; must not exceed |max_halfword|} +@!string_vacancies=74000; {the minimum number of characters that should be + available for the user's identifier names and strings, + after \MF's own error messages are stored} +@!pool_size=100000; {maximum number of characters in strings, including all + error messages and help texts, and the names of all identifiers; + must exceed |string_vacancies| by the total + length of \MF's own strings, which is currently about 22000} +@!move_size=20000; {space for storing moves in a single octant} +@!max_wiggle=1000; {number of autorounded points per cycle} +@!pool_name=TEXMF_POOL_NAME; + {string that tells where the string pool appears} +@!engine_name=TEXMF_ENGINE_NAME; {the name of this engine} +@!path_size=1000; {maximum number of knots between breakpoints of a path} +@!bistack_size=1500; {size of stack for bisection algorithms; + should probably be left at this value} +@!header_size=100; {maximum number of \.{TFM} header words, times~4} +@!lig_table_size=15000; {maximum number of ligature/kern steps, must be + at least 255 and at most 32510} +@!max_kerns=2500; {maximum number of distinct kern amounts} +@!max_font_dimen=60; {maximum number of \&{fontdimen} parameters} +@# +@!inf_main_memory = 3000; +@!sup_main_memory = 8000000; + +@!inf_buf_size = 500; +@!sup_buf_size = 300000; +@z + +@x [1.12] Constants defined as WEB macros. +@d mem_min=0 {smallest index in the |mem| array, must not be less + than |min_halfword|} +@d mem_top==30000 {largest index in the |mem| array dumped by \.{INIMF}; + must be substantially larger than |mem_min| + and not greater than |mem_max|} +@d hash_size=2100 {maximum number of symbolic tokens, + must be less than |max_halfword-3*param_size|} +@d hash_prime=1777 {a prime number equal to about 85\pct! of |hash_size|} +@d max_in_open=6 {maximum number of input files and error insertions that + can be going on simultaneously} +@d param_size=150 {maximum number of simultaneous macro parameters} +@y +@d mem_min=0 {smallest index in the |mem| array, must not be less + than |min_halfword|} +@d hash_size=9500 {maximum number of symbolic tokens, + must be less than |max_halfword-3*param_size|} +@d hash_prime=7919 {a prime number equal to about 85\pct! of |hash_size|} +@d max_in_open=15 {maximum number of input files and error insertions that + can be going on simultaneously} +@d param_size=150 {maximum number of simultaneous macro parameters} +@z + +@x [1.13] Global parameters that can be changed in texmf.cnf. +@<Glob...@>= +@!bad:integer; {is some ``constant'' wrong?} +@y +@<Glob...@>= +@!bad:integer; {is some ``constant'' wrong?} +@# +@!init +@!ini_version:boolean; {are we \.{INIMF}? Set in \.{lib/texmfmp.c}} +@!dump_option:boolean; {was the dump name option used?} +@!dump_line:boolean; {was a \.{\%\AM base} line seen?} +tini@/ +@# +@!bound_default:integer; {temporary for setup} +@!bound_name:^char; {temporary for setup} +@# +@!main_memory:integer; {total memory words allocated in initex} +@!mem_top:integer; {largest index in the |mem| array dumped by \.{INIMF}; + must be substantially larger than |mem_bot|, + equal to |mem_max| in \.{INIMF}, else not greater than |mem_max|} +@!mem_max:integer; {greatest index in \MF's internal |mem| array; + must be strictly less than |max_halfword|; + must be equal to |mem_top| in \.{INIMF}, otherwise |>=mem_top|} +@!buf_size:integer; {maximum number of characters simultaneously present in + current lines of open files; must not exceed |max_halfword|} +@!error_line:integer; {width of context lines on terminal error messages} +@!half_error_line:integer; {width of first lines of contexts in terminal + error messages; should be between 30 and |error_line-15|} +@!max_print_line:integer; {width of longest text lines output; + should be at least 60} +@!gf_buf_size:integer; {size of the output buffer, must be a multiple of 8} +@!parse_first_line_p:c_int_type; {parse the first line for options} +@!file_line_error_style_p:c_int_type; {output file:line:error style errors.} +@!eight_bit_p:c_int_type; {make all characters printable by default} +@!halt_on_error_p:c_int_type; {stop at first error} +@!quoted_filename:boolean; {current filename is quoted} +@z + +@x [1.16] Use C macros for `incr' and `decr'. +@d incr(#) == #:=#+1 {increase a variable by unity} +@d decr(#) == #:=#-1 {decrease a variable by unity} +@y +@z + +% [2.19] The text_char type is used as an array index into xord. The +% default type `char' produces signed integers, which are bad array +% indices in C. +@x +@d text_char == char {the data type of characters in text files} +@y +@d text_char == ASCII_code {the data type of characters in text files} +@z + +@x [2.22] Allow any character as input. +@^character set dependencies@> +@^system dependencies@> + +@<Set init...@>= +for i:=0 to @'37 do xchr[i]:=' '; +for i:=@'177 to @'377 do xchr[i]:=' '; +@y +@^character set dependencies@> +@^system dependencies@> + +@d tab = @'11 { ASCII horizontal tab } +@d form_feed = @'14 { ASCII form feed } + +@<Set init...@>= +{Initialize |xchr| to the identity mapping.} +for i:=0 to @'37 do xchr[i]:=i; +for i:=@'177 to @'377 do xchr[i]:=i; +@z + +@x [2.24] +for i:=0 to @'176 do xord[xchr[i]]:=i; +@y +for i:=0 to @'176 do xord[xchr[i]]:=i; +{Set |xprn| for printable ASCII, unless |eight_bit_p| is set.} +for i:=0 to 255 do xprn[i]:=(eight_bit_p or ((i>=" ")and(i<="~"))); + +{The idea for this dynamic translation comes from the patch by + Libor Skarvada \.{<libor@@informatics.muni.cz>} + and Petr Sojka \.{<sojka@@informatics.muni.cz>}. I didn't use any of the + actual code, though, preferring a more general approach.} + +{This updates the |xchr|, |xord|, and |xprn| arrays from the provided + |translate_filename|. See the function definition in \.{texmfmp.c} for + more comments.} +if translate_filename then read_tcx_file; +@z + +% [3.25] Declare name_of_file as a C string. See comments in tex.ch for +% why we change the element type to text_char. +@x +@!name_of_file:packed array[1..file_name_size] of char;@;@/ + {on some systems this may be a \&{record} variable} +@y +@!name_of_file:^text_char; +@z + +@x [3.26] Do file opening in C. +@ The \ph\ compiler with which the present version of \MF\ 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 `\ignorespaces|packed +array[@t\<\\{any}>@>] of text_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 +\MF\ to undertake appropriate corrective action. +@:PASCAL H}{\ph@> +@^system dependencies@> + +\MF'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; + {open a text file for input} +begin reset(f,name_of_file,'/O'); a_open_in:=reset_OK(f); +end; +@# +function a_open_out(var @!f:alpha_file):boolean; + {open a text file for output} +begin rewrite(f,name_of_file,'/O'); a_open_out:=rewrite_OK(f); +end; +@# +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); +end; +@# +function w_open_in(var @!f:word_file):boolean; + {open a word file for input} +begin reset(f,name_of_file,'/O'); w_open_in:=reset_OK(f); +end; +@# +function w_open_out(var @!f:word_file):boolean; + {open a word file for output} +begin rewrite(f,name_of_file,'/O'); w_open_out:=rewrite_OK(f); +end; +@y +@ All of the file opening functions are defined in C. +@z + +@x [3.27] Do file closing in C. +@ 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. + +@p procedure a_close(var @!f:alpha_file); {close a text file} +begin close(f); +end; +@# +procedure b_close(var @!f:byte_file); {close a binary file} +begin close(f); +end; +@# +procedure w_close(var @!f:word_file); {close a word file} +begin close(f); +end; +@y +@ And all the file closing routines as well. +@z + +@x [3.29] Array size of input buffer is determined at runtime. +@!buffer:array[0..buf_size] of ASCII_code; {lines of characters being read} +@y +@!buffer:^ASCII_code; {lines of characters being read} +@z + +@x [3.30] Do `input_ln' in C. +Standard \PASCAL\ says that a file should have |eoln| immediately +before |eof|, but \MF\ needs only a weaker restriction: If |eof| +occurs in the middle of a line, the system function |eoln| should return +a |true| result (even though |f^| will be undefined). + +@p function input_ln(var @!f:alpha_file;@!bypass_eoln:boolean):boolean; + {inputs the next line or returns |false|} +var @!last_nonblank:0..buf_size; {|last| with trailing blanks removed} +begin if bypass_eoln then if not eof(f) then get(f); + {input the first character of the line into |f^|} +last:=first; {cf.\ Matthew 19\thinspace:\thinspace30} +if eof(f) then input_ln:=false +else begin last_nonblank:=first; + while not eoln(f) do + begin if last>=max_buf_stack then + begin max_buf_stack:=last+1; + if max_buf_stack=buf_size then + @<Report overflow of the input buffer, and abort@>; + end; + buffer[last]:=xord[f^]; get(f); incr(last); + if buffer[last-1]<>" " then last_nonblank:=last; + end; + last:=last_nonblank; input_ln:=true; + end; +end; +@y +We define |input_ln| in C, for efficiency. Nevertheless we quote the module +`Report overflow of the input buffer, and abort' here in order to make +\.{WEAVE} happy. + +@p @{ @<Report overflow of the input buffer, and abort@> @} +@z + +@x [3.31] `term_in' and `term_out' are standard input and output. +@<Glob...@>= +@!term_in:alpha_file; {the terminal as an input file} +@!term_out:alpha_file; {the terminal as an output file} +@y +@d term_in==stdin {the terminal as an input file} +@d term_out==stdout {the terminal as an output file} +@z + +@x [3.32] We don't need to open the terminal files. +@ 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. |t_open_out| does nothing. +|t_open_in|, on the other hand, does the work of ``rescanning,'' or getting +any command line arguments the user has provided. It's defined in C. + +@d t_open_out == {output already open for text output} +@z + +@x [3.33] Flushing output. +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} +@y +these operations can be specified with {\mc UNIX}. |update_terminal| +does an |fflush|. |clear_terminal| is redefined +to do nothing, since the user should control the terminal. +@^system dependencies@> + +@d update_terminal == fflush(term_out) +@d clear_terminal == do_nothing +@z + +@x [3.36] Reading the command line. +@ The following program does the required initialization +without retrieving a possible command line. +It should be clear how to modify this routine to deal with command lines, +if the system permits them. +@^system dependencies@> + +@p function init_terminal:boolean; {gets the terminal input started} +label exit; +begin t_open_in; +loop@+begin wake_up_terminal; write(term_out,'**'); update_terminal; +@.**@> + if not input_ln(term_in,true) then {this shouldn't happen} + begin write_ln(term_out); + write(term_out,'! End of file on the terminal... why?'); +@.End of file on the terminal@> + init_terminal:=false; return; + end; + loc:=first; + while (loc<last)and(buffer[loc]=" ") do incr(loc); + if loc<last then + begin init_terminal:=true; + return; {return unless the line was all blank} + end; + write_ln(term_out,'Please type the name of your input file.'); + end; +exit:end; +@y +@ The following program does the required initialization. +Iff anything has been specified on the command line, then |t_open_in| +will return with |last > first|. +@^system dependencies@> + +@p +function init_terminal:boolean; {gets the terminal input started} +label exit; +begin + t_open_in; + if last > first then begin + loc := first; + while (loc < last) and (buffer[loc]=' ') do + incr(loc); + if loc < last then begin + init_terminal := true; + goto exit; + end; + end; + loop@+begin + wake_up_terminal; write(term_out, '**'); update_terminal; +@.**@> + if not input_ln(term_in,true) then begin {this shouldn't happen} + write_ln(term_out); + write_ln(term_out, '! End of file on the terminal... why?'); +@.End of file on the terminal@> + init_terminal:=false; + return; + end; + + loc:=first; + while (loc<last)and(buffer[loc]=" ") do + incr(loc); + + if loc<last then begin + init_terminal:=true; + return; {return unless the line was all blank} + end; + write_ln(term_out, 'Please type the name of your input file.'); + end; +exit: +end; +@z + +@x [4.49] l.1239 -- change documentation (probably needed in more places) +would like string @'32 to be the single character @'32 instead of the +@y +would like string @'32 to be printed as the single character @'32 instead +of the +@z + +% [4.51] Open the pool file using a path, and can't do string +% assignments directly. (`strcpy' and `strlen' work here because +% `pool_name' is a constant string, and thus ends in a null and doesn't +% start with a space.) +@x +name_of_file:=pool_name; {we needn't set |name_length|} +if a_open_in(pool_file) then +@y +name_length := strlen (pool_name); +name_of_file := xmalloc_array (ASCII_code, 1 + name_length); +strcpy (stringcast(name_of_file+1), pool_name); {copy the string} +if a_open_in (pool_file, kpse_mfpool_format) then +@z + +@x [4.51,52,53] Make `MF.POOL' lowercase, and change how it's read. +else bad_pool('! I can''t read MF.POOL.') +@y +else bad_pool('! I can''t read ', pool_name, '; bad path?') +@z +@x +begin if eof(pool_file) then bad_pool('! MF.POOL has no check sum.'); +@.MF.POOL has no check sum@> +read(pool_file,m,n); {read two digits of string length} +@y +begin if eof(pool_file) then bad_pool('! ', pool_name, ' has no check sum.'); +@.MF.POOL has no check sum@> +read(pool_file,m); read(pool_file,n); {read two digits of string length} +@z +@x + bad_pool('! MF.POOL line doesn''t begin with two digits.'); +@y + bad_pool('! ', pool_name, ' line doesn''t begin with two digits.'); +@z +@x + bad_pool('! MF.POOL check sum doesn''t have nine digits.'); +@y + bad_pool('! ', pool_name, ' check sum doesn''t have nine digits.'); +@z +@x +done: if a<>@$ then bad_pool('! MF.POOL doesn''t match; TANGLE me again.'); +@y +done: if a<>@$ then + bad_pool('! ', pool_name, ' doesn''t match; tangle me again (or fix the path).'); +@z + +@x [5.54] error_line is a variable, so can't be a subrange array bound +@!trick_buf:array[0..error_line] of ASCII_code; {circular buffer for +@y +@!trick_buf:array[0..ssup_error_line] of ASCII_code; {circular buffer for +@z + +@x [5.58] Do not expand printable characters. +if (s<256)and(selector>pseudo) then print_char(s) +@y +if (s<256)and((selector>pseudo)or xprn[s]) then print_char(s) +@z + +@x [5.59] Do not expand printable characters. +if (s<256)and(selector>pseudo) then print_char(s) +@y +if (s<256)and((selector>pseudo) or xprn[s])then print_char(s) +@z + +@x [5.61] Print rest of banner, eliminate misleading `(no base preloaded)'. +wterm(banner); +if base_ident=0 then wterm_ln(' (no base preloaded)') +else begin slow_print(base_ident); print_ln; + end; +@y +wterm (banner); +wterm (version_string); +if base_ident>0 then slow_print(base_ident); print_ln; +if translate_filename then begin + wterm('('); + fputs(translate_filename, stdout); + wterm_ln(')'); +end; +@z + +@x [6.68] l.1603 - Add unspecified_mode. +@d error_stop_mode=3 {stops at every opportunity to interact} +@y +@d error_stop_mode=3 {stops at every opportunity to interact} +@d unspecified_mode=4 {extra value for command-line switch} +@z + +@x [6.68] l.1605 - file:line:error style messages. + print_nl("! "); print(#); +@y + if (file_line_error_style_p and not terminal_input) then + begin + print_nl (""); + print (full_source_filename_stack[in_open]); + print (":"); print_int (line); print (": "); + print (#); + end + else begin print_nl("! "); print(#) end; +@z + +@x [6.68] l.1610 - Add interaction_option. +@!interaction:batch_mode..error_stop_mode; {current level of interaction} +@y +@!interaction:batch_mode..error_stop_mode; {current level of interaction} +@!interaction_option:batch_mode..unspecified_mode; {set from command line} +@z + +@x [6.69] l.1612 - Allow override by command line switch. +@ @<Set init...@>=interaction:=error_stop_mode; +@y +@ @<Set init...@>=if interaction_option=unspecified_mode then + interaction:=error_stop_mode +else + interaction:=interaction_option; +@z + +@x [6.76] Eliminate non-local goto. +@<Error hand...@>= +procedure jump_out; +begin goto end_of_MF; +end; +@y +@d do_final_end==begin + update_terminal; + ready_already:=0; + if (history <> spotless) and (history <> warning_issued) then + uexit(1) + else + uexit(0); + end +@<Error hand...@>= +procedure jump_out; +begin +close_files_and_terminate; +do_final_end; +end; +@z + +@x [6.77] l.1736 -- halt on error? +print_char("."); show_context; +@y +print_char("."); show_context; +if (halt_on_error_p) then begin + history:=fatal_error_stop; jump_out; +end; +@z + +@x [6.79] Handle the switch-to-editor option. +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@> + +There is a secret `\.D' option available when the debugging routines haven't +been commented~out. +@^debugging@> +@y +line ready to be edited. +We do this by calling the external procedure |call_edit| with a pointer to +the filename, its length, and the line number. +However, here we just set up the variables that will be used as arguments, +since we don't want to do the switch-to-editor until after \MF\ has closed +its files. +@^system dependencies@> + +There is a secret `\.D' option available when the debugging routines have +not been commented out. +@^debugging@> +@d edit_file==input_stack[file_ptr] +@z +@x +"E": if file_ptr>0 then + begin print_nl("You want to edit file "); +@.You want to edit file x@> + slow_print(input_stack[file_ptr].name_field); + print(" at line "); print_int(line);@/ + interaction:=scroll_mode; jump_out; +@y +"E": if file_ptr>0 then + begin + edit_name_start:=str_start[edit_file.name_field]; + edit_name_length:=str_start[edit_file.name_field+1] - + str_start[edit_file.name_field]; + edit_line:=line; + jump_out; +@z + +@x [7.96] Do half in cpascal.h. And add halfp as in MetaPost for speed. +@d half(#)==(#) div 2 +@y +@z + +@x [102] Use halfp. +round_decimals:=half(a+1); +@y +round_decimals:=halfp(a+1); +@z + +@x [7.107-7.115] Optionally replace make_fraction etc. with external routines +@p function make_fraction(@!p,@!q:integer):fraction; +@y +In the C version, there are external routines that use double precision +floating point to simulate functions such as |make_fraction|. This is +carefully done to be virtually machine-independent and it gives up to 12 +times speed-up on machines with hardware floating point. Since some +machines do not have fast double-precision floating point, we provide a +C preprocessor switch that allows selecting the standard versions given +below. (There's no configure option to select FIXPT, however, since I +don't expect anyone will actually notice.) + +@p ifdef('FIXPT')@/ +function make_fraction(@!p,@!q:integer):fraction; +@z +@x + if negative then make_fraction:=-(f+n)@+else make_fraction:=f+n; + end; +end; +@y + if negative then make_fraction:=-(f+n)@+else make_fraction:=f+n; + end; +end;@/ +endif('FIXPT') +@z +@x +@p function take_fraction(@!q:integer;@!f:fraction):integer; +@y +@p ifdef('FIXPT')@/ +function take_fraction(@!q:integer;@!f:fraction):integer; +@z +@x +else take_fraction:=n+p; +end; +@y +else take_fraction:=n+p; +end;@/ +endif('FIXPT') +@z + +@x [111] +@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>= +p:=fraction_half; {that's $2^{27}$; the invariants hold now with $k=28$} +if q<fraction_four then + repeat if odd(f) then p:=half(p+q)@+else p:=half(p); + f:=half(f); + until f=1 +else repeat if odd(f) then p:=p+half(q-p)@+else p:=half(p); + f:=half(f); + until f=1 +@y +@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>= +p:=fraction_half; {that's $2^{27}$; the invariants hold now with $k=28$} +if q<fraction_four then + repeat if odd(f) then p:=halfp(p+q)@+else p:=halfp(p); + f:=halfp(f); + until f=1 +else repeat if odd(f) then p:=p+halfp(q-p)@+else p:=halfp(p); + f:=halfp(f); + until f=1 +@z + +@x +@p function take_scaled(@!q:integer;@!f:scaled):integer; +@y +@p ifdef('FIXPT')@/ +function take_scaled(@!q:integer;@!f:scaled):integer; +@z +@x +else take_scaled:=n+p; +end; +@y +else take_scaled:=n+p; +end;@/ +endif('FIXPT') +@z + +@x [113] +@ @<Compute $p=\lfloor qf/2^{16}+{1\over2}\rfloor-q$@>= +p:=half_unit; {that's $2^{15}$; the invariants hold now with $k=16$} +@^inner loop@> +if q<fraction_four then + repeat if odd(f) then p:=half(p+q)@+else p:=half(p); + f:=half(f); + until f=1 +else repeat if odd(f) then p:=p+half(q-p)@+else p:=half(p); + f:=half(f); + until f=1 +@y +@ @<Compute $p=\lfloor qf/2^{16}+{1\over2}\rfloor-q$@>= +p:=half_unit; {that's $2^{15}$; the invariants hold now with $k=16$} +@^inner loop@> +if q<fraction_four then + repeat if odd(f) then p:=halfp(p+q)@+else p:=halfp(p); + f:=halfp(f); + until f=1 +else repeat if odd(f) then p:=p+halfp(q-p)@+else p:=halfp(p); + f:=halfp(f); + until f=1 +@z + +@x +operands are positive. \ (This procedure is not used especially often, +so it is not part of \MF's inner loop.) + +@p function make_scaled(@!p,@!q:integer):scaled; +@y +operands are positive. \ (This procedure is not used especially often, +so it is not part of \MF's inner loop, but we might as well allow for +an external C routine.) + +@p ifdef('FIXPT')@/ +function make_scaled(@!p,@!q:integer):scaled; +@z +@x + if negative then make_scaled:=-(f+n)@+else make_scaled:=f+n; + end; +end; +@y + if negative then make_scaled:=-(f+n)@+else make_scaled:=f+n; + end; +end;@/ +endif('FIXPT') +@z + +@x [7.119] Do floor_scaled, floor_unscaled, round_unscaled, round_fraction in C. +@p function floor_scaled(@!x:scaled):scaled; + {$2^{16}\lfloor x/2^{16}\rfloor$} +var @!be_careful:integer; {temporary register} +begin if x>=0 then floor_scaled:=x-(x mod unity) +else begin be_careful:=x+1; + floor_scaled:=x+((-be_careful) mod unity)+1-unity; + end; +end; +@# +function floor_unscaled(@!x:scaled):integer; + {$\lfloor x/2^{16}\rfloor$} +var @!be_careful:integer; {temporary register} +begin if x>=0 then floor_unscaled:=x div unity +else begin be_careful:=x+1; floor_unscaled:=-(1+((-be_careful) div unity)); + end; +end; +@# +function round_unscaled(@!x:scaled):integer; + {$\lfloor x/2^{16}+.5\rfloor$} +var @!be_careful:integer; {temporary register} +begin if x>=half_unit then round_unscaled:=1+((x-half_unit) div unity) +else if x>=-half_unit then round_unscaled:=0 +else begin be_careful:=x+1; + round_unscaled:=-(1+((-be_careful-half_unit) div unity)); + end; +end; +@# +function round_fraction(@!x:fraction):scaled; + {$\lfloor x/2^{12}+.5\rfloor$} +var @!be_careful:integer; {temporary register} +begin if x>=2048 then round_fraction:=1+((x-2048) div 4096) +else if x>=-2048 then round_fraction:=0 +else begin be_careful:=x+1; + round_fraction:=-(1+((-be_careful-2048) div 4096)); + end; +end; +@y +@z + +@x [121] + square_rt:=half(q); +@y + square_rt:=halfp(q); +@z + +@x [126] +@p function pyth_sub(@!a,@!b:integer):integer; +label done; +var @!r:fraction; {register used to transform |a| and |b|} +@!big:boolean; {is the input dangerously near $2^{31}$?} +begin a:=abs(a); b:=abs(b); +if a<=b then @<Handle erroneous |pyth_sub| and set |a:=0|@> +else begin if a<fraction_four then big:=false + else begin a:=half(a); b:=half(b); big:=true; + end; + @<Replace |a| by an approximation to $\psqrt{a^2-b^2}$@>; + if big then a:=a+a; + end; +pyth_sub:=a; +end; +@y +@p function pyth_sub(@!a,@!b:integer):integer; +label done; +var @!r:fraction; {register used to transform |a| and |b|} +@!big:boolean; {is the input dangerously near $2^{31}$?} +begin a:=abs(a); b:=abs(b); +if a<=b then @<Handle erroneous |pyth_sub| and set |a:=0|@> +else begin if a<fraction_four then big:=false + else begin a:=halfp(a); b:=halfp(b); big:=true; + end; + @<Replace |a| by an approximation to $\psqrt{a^2-b^2}$@>; + if big then a:=a+a; + end; +pyth_sub:=a; +end; +@z + +@x [133] +@ @<Increase |k| until |x| can...@>= +begin z:=((x-1) div two_to_the[k])+1; {$z=\lceil x/2^k\rceil$} +while x<fraction_four+z do + begin z:=half(z+1); k:=k+1; + end; +y:=y+spec_log[k]; x:=x-z; +end +@y +@ @<Increase |k| until |x| can...@>= +begin z:=((x-1) div two_to_the[k])+1; {$z=\lceil x/2^k\rceil$} +while x<fraction_four+z do + begin z:=halfp(z+1); k:=k+1; + end; +y:=y+spec_log[k]; x:=x-z; +end +@z + +@x [142] +@<Set variable |z| to the arg...@>= +while x>=fraction_two do + begin x:=half(x); y:=half(y); + end; +z:=0; +if y>0 then + begin while x<fraction_one do + begin double(x); double(y); + end; + @<Increase |z| to the arg of $(x,y)$@>; + end +@y +@<Set variable |z| to the arg...@>= +while x>=fraction_two do + begin x:=halfp(x); y:=halfp(y); + end; +z:=0; +if y>0 then + begin while x<fraction_one do + begin double(x); double(y); + end; + @<Increase |z| to the arg of $(x,y)$@>; + end +@z + +@x [150] +@p procedure init_randoms(@!seed:scaled); +var @!j,@!jj,@!k:fraction; {more or less random integers} +@!i:0..54; {index into |randoms|} +begin j:=abs(seed); +while j>=fraction_one do j:=half(j); +k:=1; +for i:=0 to 54 do + begin jj:=k; k:=j-k; j:=jj; + if k<0 then k:=k+fraction_one; + randoms[(i*21)mod 55]:=j; + end; +new_randoms; new_randoms; new_randoms; {``warm up'' the array} +end; +@y +@p procedure init_randoms(@!seed:scaled); +var @!j,@!jj,@!k:fraction; {more or less random integers} +@!i:0..54; {index into |randoms|} +begin j:=abs(seed); +while j>=fraction_one do j:=halfp(j); +k:=1; +for i:=0 to 54 do + begin jj:=k; k:=j-k; j:=jj; + if k<0 then k:=k+fraction_one; + randoms[(i*21)mod 55]:=j; + end; +new_randoms; new_randoms; new_randoms; {``warm up'' the array} +end; +@z + +@x [9.153] Increase memory size. +@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=255 {largest allowable value in a |quarterword|} +@d min_halfword==0 {smallest allowable value in a |halfword|} +@d max_halfword==@"FFFFFFF {largest allowable value in a |halfword|} +@z + +@x [9.155] Don't bother to subtract zero. +@d ho(#)==#-min_halfword + {to take a sixteen-bit item from a halfword} +@d qo(#)==#-min_quarterword {to read eight bits from a quarterword} +@d qi(#)==#+min_quarterword {to store eight bits in a quarterword} +@y +@d ho(#)==# +@d qo(#)==# +@d qi(#)==# +@z + +@x [9.156] memory_word is defined externally. +@!two_halves = packed record@;@/ + @!rh:halfword; + case two_choices of + 1: (@!lh:halfword); + 2: (@!b0:quarterword; @!b1:quarterword); + end; +@!four_quarters = packed record@;@/ + @!b0:quarterword; + @!b1:quarterword; + @!b2:quarterword; + @!b3:quarterword; + end; +@!memory_word = record@;@/ + case three_choices of + 1: (@!int:integer); + 2: (@!hh:two_halves); + 3: (@!qqqq:four_quarters); + end; +@y +@=#include "texmfmem.h";@> +@z + +@x [10.159] mem is dynamically allocated. +@!mem : array[mem_min..mem_max] of memory_word; {the big dynamic storage area} +@y +@!mem : ^memory_word; {the big dynamic storage area} +@z + +@x [10.167] Fix an unsigned/signed problem in getnode. +if r>p+1 then @<Allocate from the top of node |p| and |goto found|@>; +@y +if r>intcast(p+1) then @<Allocate from the top of node |p| and |goto found|@>; +@z + +% [11.178] Change the word `free' so that it doesn't conflict with the +% standard C library routine of the same name. Also change arrays that +% use mem_max, since that's a variable now, effectively disabling the feature. +@x +are debugging.) + +@<Glob...@>= +@!debug @!free: packed array [mem_min..mem_max] of boolean; {free cells} +@t\hskip1em@>@!was_free: packed array [mem_min..mem_max] of boolean; +@y +are debugging.) + +@d free==free_arr +@<Glob...@>= +@!debug @!free: packed array [0..1] of boolean; {free cells; this loses} +@t\hskip1em@>@!was_free: packed array [0..1] of boolean; {this loses too} +@z + +@x [11.182] Eliminate unsigned comparisons to zero. +repeat if (p>=lo_mem_max)or(p<mem_min) then clobbered:=true + else if (rlink(p)>=lo_mem_max)or(rlink(p)<mem_min) then clobbered:=true +@y +repeat if (p>=lo_mem_max) then clobbered:=true + else if (rlink(p)>=lo_mem_max) then clobbered:=true +@z + +@x [12.194] Do `fix_date_and_time' in C. +@ The following procedure, which is called just before \MF\ initializes its +input and output, establishes the initial values of the date and time. +@^system dependencies@> +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. + +Note that the values are |scaled| integers. Hence \MF\ can no longer +be used after the year 32767. + +@p procedure fix_date_and_time; +begin internal[time]:=12*60*unity; {minutes since midnight} +internal[day]:=4*unity; {fourth day of the month} +internal[month]:=7*unity; {seventh month of the year} +internal[year]:=1776*unity; {Anno Domini} +end; +@y +@ The following procedure, which is called just before \MF\ initializes its +input and output, establishes the initial values of the date and time. +It is calls an externally defined |date_and_time|, even though it could +be done from Pascal. +The external procedure also sets up interrupt catching. +@^system dependencies@> + +Note that the values are |scaled| integers. Hence \MF\ can no longer +be used after the year 32767. + +@p procedure fix_date_and_time; +begin + date_and_time(internal[time],internal[day],internal[month],internal[year]); + internal[time] := internal[time] * unity; + internal[day] := internal[day] * unity; + internal[month] := internal[month] * unity; + internal[year] := internal[year] * unity; +end; +@z + +@x [12.198] Change class to c_class to avoid C++ keyword. +@d max_class=20 {the largest class number} +@y +@d max_class=20 {the largest class number} +@d class==c_class +@z + +@x [12.199] Allow tab and form feed as input. +for k:=127 to 255 do char_class[k]:=invalid_class; +@y +for k:=127 to 255 do char_class[k]:=invalid_class; +char_class[tab]:=space_class; +char_class[form_feed]:=space_class; +@z + +@x [232] Use halfp. +@p procedure init_big_node(@!p:pointer); +var @!q:pointer; {the new node} +@!s:small_number; {its size} +begin s:=big_node_size[type(p)]; q:=get_node(s); +repeat s:=s-2; @<Make variable |q+s| newly independent@>; +name_type(q+s):=half(s)+x_part_sector; link(q+s):=null; +until s=0; +link(q):=p; value(p):=q; +end; +@y +@p procedure init_big_node(@!p:pointer); +var @!q:pointer; {the new node} +@!s:small_number; {its size} +begin s:=big_node_size[type(p)]; q:=get_node(s); +repeat s:=s-2; @<Make variable |q+s| newly independent@>; +name_type(q+s):=halfp(s)+x_part_sector; link(q+s):=null; +until s=0; +link(q):=p; value(p):=q; +end; +@z + + [20.329] |valid_range| uses |abs|, which we have defined as a C +% macro. Some C preprocessors cannot expand the giant argument here. +% So we add a temporary. +@x +@p procedure edge_prep(@!ml,@!mr,@!nl,@!nr:integer); +var @!delta:halfword; {amount of change} +@y +@p procedure edge_prep(@!ml,@!mr,@!nl,@!nr:integer); +var @!delta:halfword; {amount of change} +temp:integer; +@z + +@x +if not valid_range(m_min(cur_edges)+m_offset(cur_edges)-zero_field) or@| + not valid_range(m_max(cur_edges)+m_offset(cur_edges)-zero_field) then +@y +temp := m_offset (cur_edges) - zero_field; +if not valid_range (m_min (cur_edges) + temp) + or not valid_range (m_max (cur_edges) + temp) +then +@z + +@x [442] Use halfp. +@<Compute a good coordinate at a diagonal transition@>= +begin if cur_pen=null_pen then pen_edge:=0 +else if cur_path_type=double_path_code then @<Compute a compromise |pen_edge|@> +else if right_type(q)<=switch_x_and_y then pen_edge:=diag_offset(right_type(q)) +else pen_edge:=-diag_offset(right_type(q)); +if odd(right_type(q)) then a:=good_val(b,pen_edge+half(cur_gran)) +else a:=good_val(b-1,pen_edge+half(cur_gran)); +end +@y +@<Compute a good coordinate at a diagonal transition@>= +begin if cur_pen=null_pen then pen_edge:=0 +else if cur_path_type=double_path_code then @<Compute a compromise |pen_edge|@> +else if right_type(q)<=switch_x_and_y then pen_edge:=diag_offset(right_type(q)) +else pen_edge:=-diag_offset(right_type(q)); +if odd(right_type(q)) then a:=good_val(b,pen_edge+halfp(cur_gran)) +else a:=good_val(b-1,pen_edge+halfp(cur_gran)); +end +@z + +@x [24.509] i18n fix +print(" ("); print_int(info(h)); print(" offset"); +if info(h)<>1 then print_char("s"); +@y +print(" ("); print_int(info(h)); +if info(h)<>1 then print(" offsets") +else print(" offset"); +@z + +% [25.530] |make_fraction| and |take_fraction| arguments are too long for +% some preprocessors, when they were defined as macros, just as in the +% previous change. +@x + alpha:=take_fraction(take_fraction(major_axis, + make_fraction(gamma,beta)),n_cos)@| + -take_fraction(take_fraction(minor_axis, + make_fraction(delta,beta)),n_sin); + alpha:=(alpha+half_unit) div unity; + gamma:=pyth_add(take_fraction(major_axis,n_cos), + take_fraction(minor_axis,n_sin)); +@y + alpha := make_fraction (gamma, beta); + alpha := take_fraction (major_axis, alpha); + alpha := take_fraction (alpha, n_cos); + alpha := (alpha+half_unit) div unity; + gamma := take_fraction (minor_axis, n_sin); + gamma := pyth_add (take_fraction (major_axis, n_cos), gamma); +@z + +@x [556] +@p procedure cubic_intersection(@!p,@!pp:pointer); +label continue, not_found, exit; +var @!q,@!qq:pointer; {|link(p)|, |link(pp)|} +begin time_to_go:=max_patience; max_t:=2; +@<Initialize for intersections at level zero@>; +loop@+ begin continue: + if delx-tol<=stack_max(x_packet(xy))-stack_min(u_packet(uv)) then + if delx+tol>=stack_min(x_packet(xy))-stack_max(u_packet(uv)) then + if dely-tol<=stack_max(y_packet(xy))-stack_min(v_packet(uv)) then + if dely+tol>=stack_min(y_packet(xy))-stack_max(v_packet(uv)) then + begin if cur_t>=max_t then + begin if max_t=two then {we've done 17 bisections} + begin cur_t:=half(cur_t+1); cur_tt:=half(cur_tt+1); return; + end; + double(max_t); appr_t:=cur_t; appr_tt:=cur_tt; + end; + @<Subdivide for a new level of intersection@>; + goto continue; + end; + if time_to_go>0 then decr(time_to_go) + else begin while appr_t<unity do + begin double(appr_t); double(appr_tt); + end; + cur_t:=appr_t; cur_tt:=appr_tt; return; + end; + @<Advance to the next pair |(cur_t,cur_tt)|@>; + end; +exit:end; +@y +@p procedure cubic_intersection(@!p,@!pp:pointer); +label continue, not_found, exit; +var @!q,@!qq:pointer; {|link(p)|, |link(pp)|} +begin time_to_go:=max_patience; max_t:=2; +@<Initialize for intersections at level zero@>; +loop@+ begin continue: + if delx-tol<=stack_max(x_packet(xy))-stack_min(u_packet(uv)) then + if delx+tol>=stack_min(x_packet(xy))-stack_max(u_packet(uv)) then + if dely-tol<=stack_max(y_packet(xy))-stack_min(v_packet(uv)) then + if dely+tol>=stack_min(y_packet(xy))-stack_max(v_packet(uv)) then + begin if cur_t>=max_t then + begin if max_t=two then {we've done 17 bisections} + begin cur_t:=halfp(cur_t+1); cur_tt:=halfp(cur_tt+1); return; + end; + double(max_t); appr_t:=cur_t; appr_tt:=cur_tt; + end; + @<Subdivide for a new level of intersection@>; + goto continue; + end; + if time_to_go>0 then decr(time_to_go) + else begin while appr_t<unity do + begin double(appr_t); double(appr_tt); + end; + cur_t:=appr_t; cur_tt:=appr_tt; return; + end; + @<Advance to the next pair |(cur_t,cur_tt)|@>; + end; +exit:end; +@z + +@x [561] +@ @<Descend to the previous level...@>= +begin cur_t:=half(cur_t); cur_tt:=half(cur_tt); +if cur_t=0 then return; +bisect_ptr:=bisect_ptr-int_increment; three_l:=three_l-tol_step; +delx:=stack_dx; dely:=stack_dy; tol:=stack_tol; uv:=stack_uv; xy:=stack_xy;@/ +goto not_found; +end +@y +@ @<Descend to the previous level...@>= +begin cur_t:=halfp(cur_t); cur_tt:=halfp(cur_tt); +if cur_t=0 then return; +bisect_ptr:=bisect_ptr-int_increment; three_l:=three_l-tol_step; +delx:=stack_dx; dely:=stack_dy; tol:=stack_tol; uv:=stack_uv; xy:=stack_xy;@/ +goto not_found; +end +@z + +@x [27.564] The window functions are defined externally, in C. +@p function init_screen:boolean; +begin init_screen:=false; +end; +@# +procedure update_screen; {will be called only if |init_screen| returns |true|} +begin @!init wlog_ln('Calling UPDATESCREEN');@+tini {for testing only} +end; +@y +{These functions/procedures are defined externally in C.} +@z + +@x [27.567] +@p procedure blank_rectangle(@!left_col,@!right_col:screen_col; + @!top_row,@!bot_row:screen_row); +var @!r:screen_row; +@!c:screen_col; +begin @{@+for r:=top_row to bot_row-1 do + for c:=left_col to right_col-1 do + screen_pixel[r,c]:=white;@+@}@/ +@!init wlog_cr; {this will be done only after |init_screen=true|} +wlog_ln('Calling BLANKRECTANGLE(',left_col:1,',', + right_col:1,',',top_row:1,',',bot_row:1,')');@+tini +end; +@y +{Same thing.} +@z + +@x [27.568] +@p procedure paint_row(@!r:screen_row;@!b:pixel_color;var @!a:trans_spec; + @!n:screen_col); +var @!k:screen_col; {an index into |a|} +@!c:screen_col; {an index into |screen_pixel|} +begin @{ k:=0; c:=a[0]; +repeat incr(k); + repeat screen_pixel[r,c]:=b; incr(c); + until c=a[k]; + b:=black-b; {$|black|\swap|white|$} + until k=n;@+@}@/ +@!init wlog('Calling PAINTROW(',r:1,',',b:1,';'); + {this is done only after |init_screen=true|} +for k:=0 to n do + begin wlog(a[k]:1); if k<>n then wlog(','); + end; +wlog_ln(')');@+tini +end; +@y +{Same thing} +@z + +@x [28.585] Check for overflow of the serial numbers. +@d s_scale=64 {the serial numbers are multiplied by this factor} +@d new_indep(#)== {create a new independent variable} + begin type(#):=independent; serial_no:=serial_no+s_scale; + value(#):=serial_no; + end +@y +@d s_scale=64 {the serial numbers are multiplied by this factor} +@d max_indep_vars==@'177777777 {$2^{25}-1$} +@d max_serial_no==@'17777777700 {|max_indep_vars*s_scale|} +@d new_indep(#)== {create a new independent variable} + begin if serial_no=max_serial_no then + overflow("independent variables",max_indep_vars); + type(#):=independent; serial_no:=serial_no+s_scale; + value(#):=serial_no; + end +@z + +@x [596] Use halfp. +@ @<Contribute a term from |q|, multiplied by~|f|@>= +begin if tt=dependent then v:=take_fraction(f,value(q)) +else v:=take_scaled(f,value(q)); +if abs(v)>half(threshold) then + begin s:=get_node(dep_node_size); info(s):=qq; value(s):=v; + if abs(v)>=coef_bound then if watch_coefs then + begin type(qq):=independent_needing_fix; fix_needed:=true; + end; + link(r):=s; r:=s; + end; +q:=link(q); qq:=info(q); +end +@y +@ @<Contribute a term from |q|, multiplied by~|f|@>= +begin if tt=dependent then v:=take_fraction(f,value(q)) +else v:=take_scaled(f,value(q)); +if abs(v)>halfp(threshold) then + begin s:=get_node(dep_node_size); info(s):=qq; value(s):=v; + if abs(v)>=coef_bound then if watch_coefs then + begin type(qq):=independent_needing_fix; fix_needed:=true; + end; + link(r):=s; r:=s; + end; +q:=link(q); qq:=info(q); +end +@z + +@x [31.631] l.13346 - Add datastructures for file:line:error. +@!line_stack : array[1..max_in_open] of integer; +@y +@!line_stack : array[1..max_in_open] of integer; +@!source_filename_stack : ^str_number; +@!full_source_filename_stack : ^str_number; +@z + +@x [38.768] Area and extension rules. +@ The file names we shall deal with for illustrative purposes 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 +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: + +@<Glob...@>= +@!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 for illustrative purposes have the +following structure: If the name contains `\./', 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: + +@<Glob...@>= +@!area_delimiter:pool_pointer; {the most recent `\./', if any} +@!ext_delimiter:pool_pointer; {the most recent `\..', if any} +@z + +@x [38.769] MF area directories. +@d MF_area=="MFinputs:" +@.MFinputs@> +@y +In C, the default paths are specified separately. +@z + +@x [38.770] filenames: quoted +begin area_delimiter:=0; ext_delimiter:=0; +@y +begin area_delimiter:=0; ext_delimiter:=0; quoted_filename:=false; +@z + +@x [38.771] more_name +begin if c=" " then more_name:=false +else begin if (c=">")or(c=":") then +@y +begin +if c="""" then begin + quoted_filename:=not quoted_filename; + more_name:=true; + end +else if ((c=" ")or(c=tab)) and stop_at_space and (not quoted_filename) then + more_name:=false +else begin + if IS_DIR_SEP (c) then +@z + +@x [38.771] more_name + else if (c=".")and(ext_delimiter=0) then ext_delimiter:=pool_ptr; +@y + else if c="." then ext_delimiter:=pool_ptr; +@z + +@x [38.772] end_name +@p procedure end_name; +begin if str_ptr+3>max_str_ptr then + begin if str_ptr+3>max_strings then + overflow("number of strings",max_strings-init_str_ptr); +@:METAFONT capacity exceeded number of strings}{\quad number of strings@> + max_str_ptr:=str_ptr+3; + end; +@y +@p procedure end_name; +var must_quote:boolean; {whether we need to quote a string} +@!j,@!s,@!t: pool_pointer; {running indices} +begin if str_ptr+3>max_str_ptr then + begin if str_ptr+3>max_strings then + overflow("number of strings",max_strings-init_str_ptr); +@:METAFONT capacity exceeded number of strings}{\quad number of strings@> + max_str_ptr:=str_ptr+3; + end; +str_room(6); {room for quotes, if they are needed} +if area_delimiter<>0 then begin + {maybe quote |cur_area|} + must_quote:=false; + s:=str_start[str_ptr]; + t:=area_delimiter+1; + j:=s; + while (not must_quote) and (j<>t) do begin + must_quote:=str_pool[j]=' '; incr(j); + end; + if must_quote then begin + for j:=pool_ptr-1 downto t do str_pool[j+2]:=str_pool[j]; + str_pool[t+1]:=""""; + for j:=t-1 downto s do str_pool[j+1]:=str_pool[j]; + str_pool[s]:=""""; + if ext_delimiter<>0 then ext_delimiter:=ext_delimiter+2; + area_delimiter:=area_delimiter+2; + pool_ptr:=pool_ptr+2; + end; + s:=area_delimiter+1; + end +else begin + s:=str_start[str_ptr]; + end; +{maybe quote |cur_name|} +if ext_delimiter=0 then t:=pool_ptr else t:=ext_delimiter; +must_quote:=false; +j:=s; +while (not must_quote) and (j<>t) do begin + must_quote:=str_pool[j]=" "; incr(j); + end; +if must_quote then begin + for j:=pool_ptr-1 downto t do str_pool[j+2]:=str_pool[j]; + str_pool[t+1]:=""""; + for j:=t-1 downto s do str_pool[j+1]:=str_pool[j]; + str_pool[s]:=""""; + if ext_delimiter<>0 then ext_delimiter:=ext_delimiter+2; + pool_ptr:=pool_ptr+2; + end; +if ext_delimiter<>0 then begin + {maybe quote |cur_ext|} + s:=ext_delimiter; + t:=pool_ptr; + must_quote:=false; + j:=s; + while (not must_quote) and (j<>t) do begin + must_quote:=str_pool[j]=' '; incr(j); + end; + if must_quote then begin + str_pool[t+1]:=""""; + for j:=t-1 downto s do str_pool[j+1]:=str_pool[j]; + str_pool[s]:=""""; + pool_ptr:=pool_ptr+2; + end; + end; +@z + +@x [38.773] print_file_name: quote if spaces in names. +begin slow_print(a); slow_print(n); slow_print(e); +@y +var must_quote: boolean; {whether to quote the filename} +@!j:pool_pointer; {index into |str_pool|} +begin +must_quote:=false; +if a<>0 then begin + j:=str_start[a]; + while (not must_quote) and (j<>str_start[a+1]) do begin + must_quote:=str_pool[j]=" "; incr(j); + end; +end; +if n<>0 then begin + j:=str_start[n]; + while (not must_quote) and (j<>str_start[n+1]) do begin + must_quote:=str_pool[j]=" "; incr(j); + end; +end; +if e<>0 then begin + j:=str_start[e]; + while (not must_quote) and (j<>str_start[e+1]) do begin + must_quote:=str_pool[j]=" "; incr(j); + end; +end; +if must_quote then slow_print(""""); +if a<>0 then + for j:=str_start[a] to str_start[a+1]-1 do + if so(str_pool[j])<>"""" then + print(so(str_pool[j])); +if n<>0 then + for j:=str_start[n] to str_start[n+1]-1 do + if so(str_pool[j])<>"""" then + print(so(str_pool[j])); +if e<>0 then + for j:=str_start[e] to str_start[e+1]-1 do + if so(str_pool[j])<>"""" then + print(so(str_pool[j])); +if must_quote then slow_print(""""); +@z + +@x [38.774] have append_to_name skip quotes. +@d append_to_name(#)==begin c:=#; incr(k); + if k<=file_name_size then name_of_file[k]:=xchr[c]; + end +@y +@d append_to_name(#)==begin c:=#; if not (c="""") then begin incr(k); + if k<=file_name_size then name_of_file[k]:=xchr[c]; + end end +@z + +@x [38.774] (pack_file_name) malloc and null terminate name_of_file. +for j:=str_start[a] to str_start[a+1]-1 do append_to_name(so(str_pool[j])); +@y +if name_of_file then libc_free (name_of_file); +name_of_file := xmalloc_array (ASCII_code, length(a)+length(n)+length(e)+1); +for j:=str_start[a] to str_start[a+1]-1 do append_to_name(so(str_pool[j])); +@z +@x +for k:=name_length+1 to file_name_size do name_of_file[k]:=' '; +@y +name_of_file[name_length + 1] := 0; +@z + +@x [38.775] The default base. +@d base_default_length=18 {length of the |MF_base_default| string} +@d base_area_length=8 {length of its area part} +@y +@d base_area_length=0 {no fixed area in C} +@z + +@x [38.776] Where `plain.base' is. +@!MF_base_default:packed array[1..base_default_length] of char; + +@ @<Set init...@>= +MF_base_default:='MFbases:plain.base'; +@y +@!base_default_length: integer; +@!MF_base_default: ^char; + +@ We set the name of the default format file and the length of that name +in \.{texmfmp.c}, since we want them to depend on the name of the +program. +@z + +@x [38.778] Change to pack_buffered_name as with pack_file_name. +for j:=1 to n do append_to_name(xord[MF_base_default[j]]); +@y +if name_of_file then libc_free (name_of_file); +name_of_file := xmalloc_array (ASCII_code, n + (b-a+1) + base_ext_length + 1); +for j:=1 to n do append_to_name(xord[MF_base_default[j]]); +@z +% @x [38.778] Set program name to match format. +% for j:=a to b do append_to_name(buffer[j]); +% @y +% for j:=a to b do append_to_name(buffer[j]); +% name_of_file[k+1]:=0; +% kpse_reset_program_name(name_of_file+1); +% @z +@x [38.778] Change to pack_buffered_name as with pack_file_name. +for k:=name_length+1 to file_name_size do name_of_file[k]:=' '; +@y +name_of_file[name_length + 1] := 0; +@z + +@x [38.779] Base file opening: do path searching for the default, not plain. + pack_buffered_name(0,loc,j-1); {try first without the system file area} + if w_open_in(base_file) then goto found; + pack_buffered_name(base_area_length,loc,j-1); + {now try the system base file area} + if w_open_in(base_file) then goto found; +@y + pack_buffered_name(0,loc,j-1); + if w_open_in(base_file) then goto found; +@z +@x + wterm_ln('Sorry, I can''t find that base;',' will try PLAIN.'); +@y + wterm ('Sorry, I can''t find the base `'); + fputs (stringcast(name_of_file + 1), stdout); + wterm ('''; will try `'); + fputs (MF_base_default + 1, stdout); + wterm_ln ('''.'); +@z +@x + wterm_ln('I can''t find the PLAIN base file!'); +@.I can't find PLAIN...@> +@y + wterm ('I can''t find the base file `'); + fputs (MF_base_default + 1, stdout); + wterm_ln ('''!'); +@.I can't find the base...@> +@z + +@x [38.780] make_name_string + make_name_string:=make_string; + end; +@y + make_name_string:=make_string; + end; + {At this point we also set |cur_name|, |cur_ext|, and |cur_area| to + match the contents of |name_of_file|.} + k:=1; + begin_name; + stop_at_space:=false; + while (k<=name_length)and(more_name(name_of_file[k])) do + incr(k); + stop_at_space:=true; + end_name; +@z + +@x [38.781] Make scan_file_name ignore leading tabs as well as spaces. +while buffer[loc]=" " do incr(loc); +@y +while (buffer[loc]=" ")or(buffer[loc]=tab) do incr(loc); +@z + +@x [38.782] `logname' is declared in <unistd.h> on some systems. +`\.{.base}' and `\.{.tfm}' in the names of \MF's output files. +@y +`\.{.base}' and `\.{.tfm}' in the names of \MF's output files. +@d log_name == texmf_log_name +@z + +@x [38.786] prompt_file_name: avoid empty filenames. +var @!k:0..buf_size; {index into |buffer|} +@y +var @!k:0..buf_size; {index into |buffer|} +@!saved_cur_name:str_number; {to catch empty terminal input} +@z + +@x [38.786] prompt_file_name: avoid empty filenames. +clear_terminal; prompt_input(": "); @<Scan file name in the buffer@>; +if cur_ext="" then cur_ext:=e; +@y +saved_cur_name:=cur_name; +clear_terminal; prompt_input(": "); @<Scan file name in the buffer@>; +if cur_ext="" then cur_ext:=e; +if length(cur_name)=0 then cur_name:=saved_cur_name; +@z + +@x [38.787] <Scan file name...> needs similar leading tab treatment. +while (buffer[k]=" ")and(k<last) do incr(k); +@y +while ((buffer[k]=" ")or(buffer[k]=tab))and(k<last) do incr(k); +@z + +@x [38.788] Adjust for C string conventions. +@!months:packed array [1..36] of char; {abbreviations of month names} +@y +@!months:^char; +@z + +@x [38.788] Set correct filename for recorder. +if job_name=0 then job_name:="mfput"; +@.mfput@> +pack_job_name(".log"); +@y +if job_name=0 then job_name:=get_job_name("mfput"); +@.mfput@> +pack_job_name(".fls"); +recorder_change_filename(stringcast(name_of_file+1)); +pack_job_name(".log"); +@z + +@x [38.790] +begin wlog(banner); +slow_print(base_ident); print(" "); +print_int(round_unscaled(internal[day])); print_char(" "); +months:='JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC'; +@y +begin wlog(banner); +wlog (version_string); +slow_print(base_ident); print(" "); +print_int(round_unscaled(internal[day])); print_char(" "); +months := ' JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC'; +@z + +@x [38.790] l.15898 - Print TCX name, if given. +end +@y +if translate_filename then begin + wlog_cr; + wlog('('); + fputs(translate_filename, log_file); + wlog(')'); +end; +end +@z + +@x [38.793] (start_input) a_open_in of input file needs path specifier. +begin @<Put the desired file name in |(cur_name,cur_ext,cur_area)|@>; +if cur_ext="" then cur_ext:=".mf"; +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,MF_area,cur_ext); + if a_open_in(cur_file) then goto done; + end; +@y Don't assume a single . in filenames. +var temp_str: str_number; k: integer; +begin @<Put the desired file name in |(cur_name,cur_ext,cur_area)|@>; +pack_cur_name; +loop@+begin + begin_file_reading; {set up |cur_file| and new level of input} + if cur_ext = ".mf" then begin + cur_ext := ""; + pack_cur_name; + end; + {Kpathsea tries all the various ways to get the file.} + if open_in_name_ok(stringcast(name_of_file+1)) + and a_open_in(cur_file, kpse_mf_format) then + goto done; +@z + +@x [38.793] l.15938 - The job name may have been given on the command line. + begin job_name:=cur_name; open_log_file; +@y + begin job_name:=get_job_name(cur_name); open_log_file; +@z + +@x [38.793] Can't return name to string pool because of editor option? +if name=str_ptr-1 then {we can conserve string pool space now} + begin flush_string(name); name:=cur_name; + end; +@y +@z + +@x [866] Use halfp. +@<Change node |q|...@>= +begin tx:=x_coord(q); ty:=y_coord(q); +txx:=left_x(q)-tx; tyx:=left_y(q)-ty; +txy:=right_x(q)-tx; tyy:=right_y(q)-ty; +a_minus_b:=pyth_add(txx-tyy,tyx+txy); a_plus_b:=pyth_add(txx+tyy,tyx-txy); +major_axis:=half(a_minus_b+a_plus_b); minor_axis:=half(abs(a_plus_b-a_minus_b)); +if major_axis=minor_axis then theta:=0 {circle} +else theta:=half(n_arg(txx-tyy,tyx+txy)+n_arg(txx+tyy,tyx-txy)); +free_node(q,knot_node_size); +q:=make_ellipse(major_axis,minor_axis,theta); +if (tx<>0)or(ty<>0) then @<Shift the coordinates of path |q|@>; +end +@y +@<Change node |q|...@>= +begin tx:=x_coord(q); ty:=y_coord(q); +txx:=left_x(q)-tx; tyx:=left_y(q)-ty; +txy:=right_x(q)-tx; tyy:=right_y(q)-ty; +a_minus_b:=pyth_add(txx-tyy,tyx+txy); a_plus_b:=pyth_add(txx+tyy,tyx-txy); +major_axis:=halfp(a_minus_b+a_plus_b); minor_axis:=halfp(abs(a_plus_b-a_minus_b)); +if major_axis=minor_axis then theta:=0 {circle} +else theta:=half(n_arg(txx-tyy,tyx+txy)+n_arg(txx+tyy,tyx-txy)); +free_node(q,knot_node_size); +q:=make_ellipse(major_axis,minor_axis,theta); +if (tx<>0)or(ty<>0) then @<Shift the coordinates of path |q|@>; +end +@z + +@x [42.965] A C casting problem. +if (m_min(cur_edges)+tx<=0)or(m_max(cur_edges)+tx>=8192)or@| + (n_min(cur_edges)+ty<=0)or(n_max(cur_edges)+ty>=8191)or@| +@y +if (intcast(m_min(cur_edges))+tx<=0)or(m_max(cur_edges)+tx>=8192)or@| + (intcast(n_min(cur_edges))+ty<=0)or(n_max(cur_edges)+ty>=8191)or@| +@z + +@x [44.1023] if batchmode, MakeTeX... scripts should be silent. +mode_command: begin print_ln; interaction:=cur_mod; +@y +mode_command: begin print_ln; interaction:=cur_mod; +if interaction = batch_mode +then kpse_make_tex_discard_errors := 1 +else kpse_make_tex_discard_errors := 0; +@z + +% [45.1120] `threshold' is both a function and a variable. Since the +% function is used much less often than the variable, we'll change that +@x +@p function threshold(@!m:integer):scaled; +var @!d:scaled; {lower bound on the smallest interval size} +begin excess:=min_cover(0)-m; +if excess<=0 then threshold:=0 +else begin repeat d:=perturbation; + until min_cover(d+d)<=m; + while min_cover(d)>m do d:=perturbation; + threshold:=d; +@y +@p function threshold_fn(@!m:integer):scaled; +var @!d:scaled; {lower bound on the smallest interval size} +begin excess:=min_cover(0)-m; +if excess<=0 then threshold_fn:=0 +else begin repeat d:=perturbation; + until min_cover(d+d)<=m; + while min_cover(d)>m do d:=perturbation; + threshold_fn:=d; +@z + +@x [45.1121] Change the call to the threshold function. +begin d:=threshold(m); perturbation:=0; +@y +begin d:=threshold_fn(m); perturbation:=0; +@z + +@x [1122] +@ @<Replace an interval...@>= +begin repeat p:=link(p); info(p):=m; +decr(excess);@+if excess=0 then d:=0; +until value(link(p))>l+d; +v:=l+half(value(p)-l); +if value(p)-v>perturbation then perturbation:=value(p)-v; +r:=q; +repeat r:=link(r); value(r):=v; +until r=p; +link(q):=p; {remove duplicate values from the current list} +end +@y +@ @<Replace an interval...@>= +begin repeat p:=link(p); info(p):=m; +decr(excess);@+if excess=0 then d:=0; +until value(link(p))>l+d; +v:=l+halfp(value(p)-l); +if value(p)-v>perturbation then perturbation:=value(p)-v; +r:=q; +repeat r:=link(r); value(r):=v; +until r=p; +link(q):=p; {remove duplicate values from the current list} +end +@z + +@x [45.1133] Use C macros to do the TFM writing, to avoid casting(?) problems. +@d tfm_out(#)==write(tfm_file,#) {output one byte to |tfm_file|} + +@p procedure tfm_two(@!x:integer); {output two bytes to |tfm_file|} +begin tfm_out(x div 256); tfm_out(x mod 256); +end; +@# +procedure tfm_four(@!x:integer); {output four bytes to |tfm_file|} +begin if x>=0 then tfm_out(x div three_bytes) +else begin x:=x+@'10000000000; {use two's complement for negative values} + x:=x+@'10000000000; + tfm_out((x div three_bytes) + 128); + end; +x:=x mod three_bytes; tfm_out(x div unity); +x:=x mod unity; tfm_out(x div @'400); +tfm_out(x mod @'400); +end; +@# +procedure tfm_qqqq(@!x:four_quarters); {output four quarterwords to |tfm_file|} +@y +The default definitions for |tfm_two| and |tfm_four| don't work. +I don't know why not. Some casting problem? + +@d tfm_out(#) == put_byte (#, tfm_file) +@d tfm_two(#) == put_2_bytes (tfm_file, #) +@d tfm_four(#) == put_4_bytes (tfm_file, #) + +@p procedure tfm_qqqq(@!x:four_quarters); {output four quarterwords to |tfm_file|} +@z + +@x [47.1134] print_file_name +print_nl("Font metrics written on "); slow_print(metric_file_name); +@y +print_nl("Font metrics written on "); print_file_name(0,metric_file_name,0); +@z + +@x [47.1152] declare gf_buf as a pointer, for dynamic allocated +@!gf_buf:array[gf_index] of eight_bits; {buffer for \.{GF} output} +@y +@!gf_buf:^eight_bits; {dynamically-allocated buffer for \.{GF} output} +@z + +@x [47.1154] omit write_gf +@<Declare generic font output procedures@>= +procedure write_gf(@!a,@!b:gf_index); +var k:gf_index; +begin for k:=a to b do write(gf_file,gf_buf[k]); +end; +@y +In C, we use a macro to call |fwrite| or |write| directly, writing all +the bytes to be written in one shot. Much better than writing four +bytes at a time. +@z + +@x [47.1163] C needs k to be 0..256 instead of 0..255. +procedure init_gf; +var @!k:eight_bits; {runs through all possible character codes} +@y +procedure init_gf; +var @!k:0..256; {runs through all possible character codes} +@z + +@x [47.1169] Fix signed/unsigned comparison problem in C. +if prev_m-m_offset(cur_edges)+x_off>gf_max_m then + gf_max_m:=prev_m-m_offset(cur_edges)+x_off +@y +if prev_m-intcast(m_offset(cur_edges))+x_off>gf_max_m then + gf_max_m:=prev_m-m_offset(cur_edges)+x_off +@z + +@x [47.1182] print_file_name +print_nl("Output written on "); slow_print(output_file_name); +@y +print_nl("Output written on "); print_file_name(0,output_file_name,0); +@z + +@x [47.1182] i18n fix +print(" ("); print_int(total_chars); print(" character"); +if total_chars<>1 then print_char("s"); +@y +print(" ("); print_int(total_chars); +if total_chars<>1 then print(" characters") +else print(" character"); +@z + +@x [48.1185] INI = VIR. +base_ident:=" (INIMF)"; +@y +if ini_version then base_ident:=" (INIMF)"; +@z + +@x [48.1186] Add base_engine. +@!w: four_quarters; {four ASCII codes} +@y +@!w: four_quarters; {four ASCII codes} +@!base_engine: ^text_char; +@z + +@x [48.1187] Add base_engine. +@!w: four_quarters; {four ASCII codes} +@y +@!w: four_quarters; {four ASCII codes} +@!base_engine: ^text_char; +@!dummy_xord: ASCII_code; +@!dummy_xchr: text_char; +@!dummy_xprn: ASCII_code; +@z + +@x [48.1188] Reading and writing of `base_file' is done in C. +@d dump_wd(#)==begin base_file^:=#; put(base_file);@+end +@d dump_int(#)==begin base_file^.int:=#; put(base_file);@+end +@d dump_hh(#)==begin base_file^.hh:=#; put(base_file);@+end +@d dump_qqqq(#)==begin base_file^.qqqq:=#; put(base_file);@+end +@y +@z + +@x [48.1189] +@d undump_wd(#)==begin get(base_file); #:=base_file^;@+end +@d undump_int(#)==begin get(base_file); #:=base_file^.int;@+end +@d undump_hh(#)==begin get(base_file); #:=base_file^.hh;@+end +@d undump_qqqq(#)==begin get(base_file); #:=base_file^.qqqq;@+end +@y +@z + +@x [48.1190] Dump engine name. +dump_int(@$);@/ +@y +dump_int(@"57324D46); {Web2C \MF's magic constant: "W2MF"} +{Align engine to 4 bytes with one or more trailing NUL} +x:=strlen(engine_name); +base_engine:=xmalloc_array(text_char,x+4); +strcpy(base_engine, engine_name); +for k:=x to x+3 do base_engine[k]:=0; +x:=x+4-(x mod 4); +dump_int(x);dump_things(base_engine[0], x); +libc_free(base_engine);@/ +dump_int(@$);@/ +@<Dump |xord|, |xchr|, and |xprn|@>; +@z + +@x [48.1191] Avoid Pascal file convention. +x:=base_file^.int; +if x<>@$ then goto off_base; {check that strings are the same} +undump_int(x); +if x<>mem_min then goto off_base; +undump_int(x); +if x<>mem_top then goto off_base; +@y +undump_int(x); +if x<>@"57324D46 then goto off_base; {not a base file} +undump_int(x); +if (x<0) or (x>256) then goto off_base; {corrupted base file} +base_engine:=xmalloc_array(text_char, x); +undump_things(base_engine[0], x); +base_engine[x-1]:=0; {force string termination, just in case} +if strcmp(engine_name, base_engine) then + begin wake_up_terminal; + wterm_ln('---! ', stringcast(name_of_file+1), ' was written by ', base_engine); + libc_free(base_engine); + goto off_base; +end; +libc_free(base_engine); +undump_int(x); +if x<>@$ then begin {check that strings are the same} + wake_up_terminal; + wterm_ln('---! ', stringcast(name_of_file+1), ' doesn''t match ', pool_name); + goto off_base; +end; +@<Undump |xord|, |xchr|, and |xprn|@>; +undump_int(x); +if x<>mem_min then goto off_base; +{Now we deal with dynamically allocating the memory. We don't provide + all the fancy features \.{tex.ch} does---all that matters is enough to + run the trap test with a memory size of 3000.} +@+init +if ini_version then begin + {We allocated this at start-up, but now we need to reallocate.} + libc_free (mem); +end; +@+tini +undump_int (mem_top); {Overwrite whatever we had.} +if mem_max < mem_top then mem_max:=mem_top; {Use at least what we dumped.} +if mem_min+1100>mem_top then goto off_base; +mem:=xmalloc_array (memory_word, mem_max - mem_min + 1); +@z + +@x [48.1199] l.22725 - - Allow command line to override dumped value. +undump(batch_mode)(error_stop_mode)(interaction); +@y +undump(batch_mode)(error_stop_mode)(interaction); +if interaction_option<>unspecified_mode then interaction:=interaction_option; +@z + +@x [48.1199] eof is like C feof here. +undump_int(x);@+if (x<>69069)or eof(base_file) then goto off_base +@y +undump_int(x);@+if (x<>69069)or feof(base_file) then goto off_base +@z + +@x [48.1200] Eliminate probably-wrong word `preloaded' from base_idents. +print(" (preloaded base="); print(job_name); print_char(" "); +@y +print(" (base="); print(job_name); print_char(" "); +@z + +@x [49.1204] Dynamic allocation. +@p begin @!{|start_here|} +@y +@d const_chk(#) == begin if # < inf@&# then # := inf@&# else + if # > sup@&# then # := sup@&# end +{|setup_bound_var| stuff duplicated in \.{tex.ch}.} +@d setup_bound_var(#) == bound_default := #; setup_bound_var_end +@d setup_bound_var_end(#) == bound_name := #; setup_bound_var_end_end +@d setup_bound_var_end_end(#) == + setup_bound_variable (address_of (#), bound_name, bound_default); + +@p begin @!{|start_here|} + {See comments in \.{tex.ch} for why the name has to be duplicated.} + setup_bound_var (250000)('main_memory')(main_memory); + {|memory_word|s for |mem| in \.{INIMF}} + setup_bound_var (3000)('buf_size')(buf_size); + setup_bound_var (79)('error_line')(error_line); + setup_bound_var (50)('half_error_line')(half_error_line); + setup_bound_var (79)('max_print_line')(max_print_line); + setup_bound_var (16384)('gf_buf_size')(gf_buf_size); + if error_line > ssup_error_line then error_line := ssup_error_line; + + const_chk (main_memory); + {|mem_top| is an index, |main_memory| is a size} + mem_top := mem_min + main_memory - 1; + mem_max := mem_top; + const_chk (buf_size); + + buffer:=xmalloc_array (ASCII_code, buf_size); + gf_buf:=xmalloc_array (eight_bits, gf_buf_size); + source_filename_stack:=xmalloc_array (str_number, max_in_open); + full_source_filename_stack:=xmalloc_array (str_number, max_in_open); + +@+init +if ini_version then begin + mem:=xmalloc_array (memory_word, mem_top - mem_min + 1); +end; +@+tini +@z + +@x [49.1204] Only do get_strings_started if ini. +@!init if not get_strings_started then goto final_end; +init_tab; {initialize the tables} +init_prim; {call |primitive| for each primitive} +init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr;@/ +max_str_ptr:=str_ptr; max_pool_ptr:=pool_ptr; fix_date_and_time; +tini@/ +@y 22833 +@!init +if ini_version then begin +if not get_strings_started then goto final_end; +init_tab; {initialize the tables} +init_prim; {call |primitive| for each primitive} +init_str_ptr:=str_ptr; init_pool_ptr:=pool_ptr;@/ +max_str_ptr:=str_ptr; max_pool_ptr:=pool_ptr; fix_date_and_time; +end; +tini@/ +@z + +@x +end_of_MF: close_files_and_terminate; +final_end: ready_already:=0; +@y +close_files_and_terminate; +final_end: do_final_end; +@z + +% [49.1205] close_files_and_terminate: Print new line before +% termination; switch to editor if necessary. +@x + slow_print(log_name); print_char("."); + end; + end; +@y + print_file_name(0,log_name,0); print_char("."); + end; + end; +print_ln; +if (edit_name_start<>0) and (interaction>batch_mode) then + call_edit(str_pool,edit_name_start,edit_name_length,edit_line); +@z + +@x [49.1209] Only do dump if ini. + begin @!init store_base_file; return;@+tini@/ +@y + begin + @!init if ini_version then begin store_base_file; return;end;@+tini@/ +@z + +%@x [49.1211] l.23002 - Handle %&base line. +%if (base_ident=0)or(buffer[loc]="&") then +%@y +%if (base_ident=0)or(buffer[loc]="&")or dump_line then +%@z + +@x [51.1214] Add editor-switch variable to globals. +This section should be replaced, if necessary, by any special +modifications of the program +that are necessary to make \MF\ 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. +@^system dependencies@> +@y +Here are the variables used to hold ``switch-to-editor'' information. +@^system dependencies@> + +@<Global...@>= +@!edit_name_start: pool_pointer; +@!edit_name_length,@!edit_line: integer; +@!xprn: array[ASCII_code] of ASCII_code; {use \.{\^\^} notation?} +@!stop_at_space: boolean; {whether |more_name| returns false for space} + +@ The |edit_name_start| will be set to point into |str_pool| somewhere after +its beginning if \MF\ is supposed to switch to an editor on exit. + +@<Set init...@>= +edit_name_start:=0; +stop_at_space:=true; + +@ Dumping the |xord|, |xchr|, and |xprn| arrays. We dump these always +in the format, so a TCX file loaded during format creation can set a +default for users of the format. + +@<Dump |xord|, |xchr|, and |xprn|@>= +dump_things(xord[0], 256); +dump_things(xchr[0], 256); +dump_things(xprn[0], 256); + +@ Undumping the |xord|, |xchr|, and |xprn| arrays. This code is more +complicated, because we want to ensure that a TCX file specified on +the command line will override whatever is in the format. Since the +tcx file has already been loaded, that implies throwing away the data +in the format. Also, if no |translate_filename| is given, but +|eight_bit_p| is set we have to make all characters printable. + +@<Undump |xord|, |xchr|, and |xprn|@>= +if translate_filename then begin + for k:=0 to 255 do undump_things(dummy_xord, 1); + for k:=0 to 255 do undump_things(dummy_xchr, 1); + for k:=0 to 255 do undump_things(dummy_xprn, 1); + end +else begin + undump_things(xord[0], 256); + undump_things(xchr[0], 256); + undump_things(xprn[0], 256); + if eight_bit_p then + for k:=0 to 255 do + xprn[k]:=1; +end; +@z |