summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/mp.ch
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/web2c/mp.ch')
-rw-r--r--Build/source/texk/web2c/mp.ch2136
1 files changed, 2136 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/mp.ch b/Build/source/texk/web2c/mp.ch
new file mode 100644
index 00000000000..dd4a9ce9a49
--- /dev/null
+++ b/Build/source/texk/web2c/mp.ch
@@ -0,0 +1,2136 @@
+% $Id: mp.ch,v 1.50 2005/06/22 17:06:13 olaf Exp $
+% mp.ch for C compilation with web2c. Public domain.
+%
+% Derived from mf.ch and John Hobby's mp.ch (the change file for the C
+% version of mf).
+%
+% Numbers of the form [pp.nnn] refer to the corresponding part and
+% module number of mp.web as implementors who read this change file are
+% much better advised to use a current WEB listing of MetaPost.
+%
+% (more recent changes in ChangeLog)
+% Revision 0.62 Jan 4 '95 by John Hobby
+% (Changes that only affect the banner line are not listed here)
+%
+% Updated for web2c-6.1/kpathsea-2.6 Jan 29 '95 by Ulrik Vieth
+% according to the current version of mf.ch.
+%
+% - produce separate binaries for INIMP, VIRMF (no '-I' flag!)
+% - make a big version of MP by default (cf. TeX and METAFONT)
+% - allow any input characters (0..255) (cf. TeX and METAFONT)
+% - use web2c/kpathsea routines in file opening and searching
+%
+% Updated for Revision 0.63 Apr 12 '95 by Ulrik Vieth
+%
+% - fixed the parsing routines for ps_tab_file to be able to handle
+% comments and blank lines in dvipsk's version of psfonts.map.
+% - increased max_read_files to 30 to avoid running out of read files
+% when reading stops before reaching EOF. (Better solution: write
+% a 'closefrom' macro that reads to EOF, which causes file closing.)
+
+@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{\MP\ changes for C}
+\def\glob{13}\def\gglob{20, 25} % these are defined in module 1
+\font\mc=cmr9
+@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('INIMP')
+@d tini==endif('INIMP')
+@z
+
+% [1.11] Compile-time constants. Although we only change a few of
+% these, listing them all makes the patch file for a big MetaPost simpler.
+% 16K for BSD I/O; file_name_size is set from the system constant.
+@x
+@<Constants...@>=
+@!mem_max=30000; {greatest index in \MP's internal |mem| array;
+ must be strictly less than |max_halfword|;
+ must be equal to |mem_top| in \.{INIMP}, 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}
+@!emergency_line_length=255;
+ {\ps\ output lines can be this long in unusual circumstances}
+@!stack_size=30; {maximum number of simultaneous input sources}
+@!max_read_files=4; {maximum number of simultaneously open \&{readfrom} files}
+@!max_strings=2500; {maximum number of strings; must not exceed |max_halfword|}
+@!string_vacancies=9000; {the minimum number of characters that should be
+ available for the user's identifier names and strings,
+ after \MP's own error messages are stored}
+@!strings_vacant=1000; {the minimum number of strings that should be available}
+@!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 \MP's own strings, which is currently about 22000}
+@!font_max=50; {maximum font number for included text fonts}
+@!font_mem_size=10000; {number of words for \.{TFM} information for text fonts}
+@!file_name_size=40; {file names shouldn't be longer than this}
+@!pool_name='MPlib:MP.POOL ';
+ {string of length |file_name_size|; tells where the string pool appears}
+@.MPlib@>
+@!ps_tab_name='MPlib:PSFONTS.MAP ';
+ {string of length |file_name_size|; locates font name translation table}
+@!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
+@d ssup_max_strings = 32767 {max value allowed by \.{TANGLE}}
+
+@<Constants...@>=
+@!max_internal=300; {maximum number of internal quantities}
+@!emergency_line_length=255;
+ {\ps\ output lines can be this long in unusual circumstances}
+@!stack_size=300; {maximum number of simultaneous input sources}
+@!max_read_files=30; {maximum number of simultaneously open \&{readfrom} files}
+@!strings_vacant=1000; {the minimum number of strings that should be available}
+@!font_max=50; {maximum font number for included text fonts}
+@!font_mem_size=10000; {number of words for \.{TFM} information for text fonts}
+@!pool_name=TEXMF_POOL_NAME;
+ {string of length |file_name_size|; tells where the string pool appears}
+@!engine_name=TEXMF_ENGINE_NAME; {the name of this engine}
+@.MPlib@>
+@!ps_tab_name='psfonts.map';
+ {string of length |file_name_size|; locates font name translation table}
+@!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=50; {maximum number of \&{fontdimen} parameters}
+@#
+@!inf_main_memory = 3000;
+@!sup_main_memory = 8000000;
+
+@!inf_max_strings = 2500;
+@!sup_max_strings = ssup_max_strings;
+
+@!inf_buf_size = 500;
+@!sup_buf_size = 300000;
+
+@!inf_path_size = 1000;
+@!sup_path_size = 300000;
+
+@!inf_pool_size = 32000;
+@!sup_pool_size = 10000000;
+@!inf_pool_free = 1000;
+@!sup_pool_free = sup_pool_size;
+@!inf_string_vacancies = 8000;
+@!sup_string_vacancies = sup_pool_size - 23000;
+@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 \.{INIMP};
+ 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}
+@d max_write_files=4 {maximum number of simultaneously open \&{write} files}
+@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=25 {maximum number of input files and error insertions that
+ can be going on simultaneously}
+@d param_size=1500 {maximum number of simultaneous macro parameters}
+@d max_write_files=10 {maximum number of simultaneously open \&{write} files}
+@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 \.{INIMP}? Set in \.{lib/texmfmp.c}}
+@!dump_option:boolean; {was the dump name option used?}
+@!dump_line:boolean; {was a \.{\%\AM mem} 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 \.{INIMP};
+ must be substantially larger than |mem_min|
+ and not greater than |mem_max|}
+@!extra_mem_top:integer; {|mem_max:=mem_top+extra_mem_top| except in \.{INIMP}}
+@!mem_max:integer; {greatest index in \MP's internal |mem| array;
+ must be strictly less than |max_halfword|;
+ must be equal to |mem_top| in \.{INIMP}, otherwise |>=mem_top|}
+@!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}
+@!pool_size:integer; {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 \MP's own strings, which is currently about 22000}
+@!string_vacancies:integer; {the minimum number of characters that should be
+ available for the user's identifier names and strings,
+ after \MP's own error messages are stored}
+@!buf_size:integer; {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|}
+@!path_size:integer; {maximum number of knots between breakpoints of a path}
+@!pool_free:integer;{minimum pool space free after format loaded}
+@!max_strings:integer; {maximum number of strings; must not exceed |max_halfword|}
+@!parse_first_line_p:c_int_type; {parse the first line for options}
+@!file_line_error_style_p:c_int_type; {file:line:error style messages.}
+@!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.23]
+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 sets the |xchr|, |xord|, and |xprn| arrays. We overwrite the
+ |xchr| assignments from module 21, just in case someone wants to do strange
+ character translations. 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 original version of \MF\ was prepared
+extends 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
+\MP\ to undertake appropriate corrective action.
+@:PASCAL H}{\ph@>
+@^system dependencies@>
+
+\MP'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_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);
+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.
+@d no_file_path = -1
+@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] l.888 - 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 \MP\ 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| (via the macro |flush|). |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.38] Dynamically allocate pool arrays.
+@!str_pool:packed array[pool_pointer] of pool_ASCII_code; {the characters}
+@!str_start : array[str_number] of pool_pointer; {the starting pointers}
+@!next_str : array[str_number] of str_number; {for linking strings in order}
+@y
+@!str_pool:^pool_ASCII_code; {the characters}
+@!str_start : ^pool_pointer; {the starting pointers}
+@!next_str : ^str_number; {for linking strings in order}
+@z
+
+@x [4.44] One more array.
+@!str_ref:array[str_number] of 0..max_str_ref;
+@y
+@!str_ref:^str_ref_type; {web2c only does |^identifier|}
+@z
+
+@x [4.64] l.1430 -- Do not hardwire printable ASCII.
+ (k<" ")or(k>"~")
+@y
+ not xprn[k]
+@z
+
+% [4.66] 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_mppool_format) then
+@z
+
+@x [4.66,67,68] Make `MP.POOL' lowercase in messages.
+else bad_pool('! I can''t read MP.POOL.')
+@y
+else bad_pool('! I can''t read ', pool_name, '; bad path?')
+@z
+@x
+begin if eof(pool_file) then bad_pool('! MP.POOL has no check sum.');
+@.MP.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.');
+@.MP.POOL has no check sum@>
+read(pool_file,m); read(pool_file,n); {read two digits of string length}
+@z
+@x
+ bad_pool('! MP.POOL line doesn''t begin with two digits.');
+@y
+ bad_pool('! ', pool_name, ' line doesn''t begin with two digits.');
+@z
+@x
+ bad_pool('! MP.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('! MP.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.69] 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.76] Print rest of banner, eliminate misleading `no preloaded'
+@ By popular demand, \MP\ prints the banner line only on the transcript file.
+Thus there is nothing special to be printed here.
+
+@<Initialize the output...@>=
+update_terminal;
+@y
+@ Here is the very first thing that \MP\ prints: a headline that identifies
+the version number and base name. The |term_offset| variable is temporarily
+incorrect, but the discrepancy is not serious since we assume that the banner
+and mem identifier together will occupy at most |max_print_line|
+character positions.
+
+@<Initialize the output...@>=
+wterm (banner);
+wterm (version_string);
+if mem_ident>0 then print(mem_ident); print_ln;
+if translate_filename then begin
+ wterm('(');
+ fputs(translate_filename, stdout);
+ wterm_ln(')');
+end;
+update_terminal;
+@z
+
+@x [6.83] l.1815 - 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.83] l.1825 - 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.83] l.1822 - 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.84] l.1824 - 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.90] Eliminate non-local goto.
+@<Error hand...@>=
+procedure jump_out;
+begin goto end_of_MP;
+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.92] l.1956 - 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.93] 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 \MP\ 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@>
+ print(input_stack[file_ptr].name_field);
+ print(" at line "); print_int(true_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:=length(edit_file.name_field);
+ edit_line:=true_line;
+ jump_out;
+@z
+
+@x [7.111] Do half and halfp in cpascal.h.
+@d half(#)==(#) div 2
+@d halfp(#)==(#) div 2
+@y
+@z
+
+@x [7.122-7.130] 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.
+
+@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
+@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
+operands are positive. \ (This procedure is not used especially often,
+so it is not part of \MP'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 \MP'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.134] 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 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 [9.168] 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.170] 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.171] 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 [9.174] 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.184] 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.193] 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}
+@t\hskip1em@>@!was_free: packed array [0..1] of boolean;
+@z
+
+@x [11.197] 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.212] Do `fix_date_and_time' in C.
+@ The following procedure, which is called just before \MP\ 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 \MP\ 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 \MP\ 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 \MP\ 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 Replace class with 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.217] 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 [18.300,301] l.6451 - Make path_size a configuration option.
+@!delta_x,@!delta_y,@!delta:array[0..path_size] of scaled; {knot differences}
+@!psi:array[1..path_size] of angle; {turning angles}
+
+@ @<Other local variables for |make_choices|@>=
+@!k,@!n:0..path_size; {current and final knot numbers}
+@y
+@!delta_x:^scaled; {knot differences}
+@!delta_y:^scaled; {knot differences}
+@!delta:^scaled; {knot differences}
+@!psi:^angle; {turning angles}
+
+@ @<Other local variables for |make_choices|@>=
+@!k,@!n:0..sup_path_size; {current and final knot numbers}
+@z
+
+@x [18.304] l.6544 - Make path_size a configuration option.
+@!theta:array[0..path_size] of angle; {values of $\theta_k$}
+@!uu:array[0..path_size] of fraction; {values of $u_k$}
+@!vv:array[0..path_size] of angle; {values of $v_k$}
+@!ww:array[0..path_size] of fraction; {values of $w_k$}
+
+@ Our immediate problem is to get the ball rolling by setting up the
+first equation or by realizing that no equations are needed, and to fit
+this initialization into a framework suitable for the overall computation.
+
+@<Declare the procedure called |solve_choices|@>=
+@t\4@>@<Declare subroutines needed by |solve_choices|@>@;
+procedure solve_choices(@!p,@!q:pointer;@!n:halfword);
+label found,exit;
+var @!k:0..path_size; {current knot number}
+@y
+@!theta:^angle; {values of $\theta_k$}
+@!uu:^fraction; {values of $u_k$}
+@!vv:^angle; {values of $v_k$}
+@!ww:^fraction; {values of $w_k$}
+
+@ Our immediate problem is to get the ball rolling by setting up the
+first equation or by realizing that no equations are needed, and to fit
+this initialization into a framework suitable for the overall computation.
+
+@<Declare the procedure called |solve_choices|@>=
+@t\4@>@<Declare subroutines needed by |solve_choices|@>@;
+procedure solve_choices(@!p,@!q:pointer;@!n:halfword);
+label found,exit;
+var @!k:0..sup_path_size; {current knot number}
+@z
+
+@x [27.585] l.11443 - data for file:line:error style.
+@!mpx_name : array[0..max_in_open] of halfword;
+@y
+@!mpx_name : array[0..max_in_open] of halfword;
+@!source_filename_stack : ^str_number;
+@!full_source_filename_stack : ^str_number;
+@z
+
+@x [35.745] 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. Note that these variables
+cannot be of type |pool_pointer| because a string pool compaction could occur
+while scanning a file name.
+
+@<Glob...@>=
+@!area_delimiter:integer;
+ {most recent `\.>' or `\.:' relative to |str_start[str_ptr]|}
+@!ext_delimiter:integer; {the relevant `\..', if any}
+@y
+@ The file names we shall deal with 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. Note that these variables
+cannot be of type |pool_pointer| because a string pool compaction could occur
+while scanning a file name.
+
+@<Glob...@>=
+@!area_delimiter:integer; {most recent `\./' relative to |str_start[str_ptr]|}
+@!ext_delimiter:integer; {the relevant `\..', if any}
+@z
+
+@x [35.746] MP and MF area directories.
+@d MP_area=="MPinputs:"
+@.MPinputs@>
+@d MF_area=="MFinputs:"
+@.MFinputs@>
+@d MP_font_area=="TeXfonts:"
+@.TeXfonts@>
+@y
+In C, the default paths are specified separately.
+@z
+
+@x [35.747] filenames: quoted
+area_delimiter:=-1; ext_delimiter:=-1;
+@y
+area_delimiter:=-1; ext_delimiter:=-1; quoted_filename:=false;
+@z
+
+@x [35.748] (more_name) Generalize directory separators.
+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 [still 35.748] Last (not first) . is extension.
+ else if (c=".")and(ext_delimiter<0) then
+@y
+ else if (c=".") then
+@z
+
+@x [35.749] (end_name) Quote name if necessary.
+procedure end_name;
+var a,@!n,@!e:pool_pointer; {length of area, name, and extension}
+begin e:=pool_ptr-str_start[str_ptr]; {total length}
+@y
+procedure end_name;
+var a,@!n,@!e:pool_pointer; {length of area, name, and extension}
+@!j,@!s,@!t:pool_pointer; {running indices}
+@!must_quote:boolean; {whether we need to quote a string}
+begin str_room(6); {room for quotes}
+if area_delimiter<>-1 then begin
+ {maybe quote |cur_area|}
+ must_quote:=false;
+ s:=str_start[str_ptr];
+ t:=str_start[str_ptr]+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<>-1 then ext_delimiter:=ext_delimiter+2;
+ area_delimiter:=area_delimiter+2;
+ pool_ptr:=pool_ptr+2;
+ end;
+ s:=str_start[str_ptr]+area_delimiter+1;
+ end
+else begin
+ s:=str_start[str_ptr];
+ end;
+{maybe quote |cur_name|}
+if ext_delimiter=-1 then t:=pool_ptr else t:=str_start[str_ptr]+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<>-1 then ext_delimiter:=ext_delimiter+2;
+ pool_ptr:=pool_ptr+2;
+ end;
+if ext_delimiter<>-1 then begin
+ {maybe quote |cur_ext|}
+ s:=str_start[str_ptr]+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;
+e:=pool_ptr-str_start[str_ptr]; {total length}
+@z
+
+@x [35.750] print_file_name
+begin print(a); print(n); 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_stop(a)) 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_stop(n)) 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_stop(e)) do begin
+ must_quote:=str_pool[j]=" "; incr(j);
+ end;
+end;
+if must_quote then print("""");
+if a>0 then
+ for j:=str_start[a] to str_stop(a)-1 do
+ if so(str_pool[j])<>"""" then
+ print(so(str_pool[j]));
+if n>0 then
+ for j:=str_start[n] to str_stop(n)-1 do
+ if so(str_pool[j])<>"""" then
+ print(so(str_pool[j]));
+if e>0 then
+ for j:=str_start[e] to str_stop(e)-1 do
+ if so(str_pool[j])<>"""" then
+ print(so(str_pool[j]));
+if must_quote then print("""");
+@z
+
+@x [35.751] macro used in pack_file_name
+@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 [35.751] (pack_file_name) malloc and null terminate name_of_file.
+for j:=str_start[a] to str_stop(a)-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_stop(a)-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 [35.752] default mem area is nonexistent
+@d mem_default_length=15 {length of the |MP_mem_default| string}
+@d mem_area_length=6 {length of its area part}
+@y
+@d mem_area_length=0 {no fixed area in C}
+@z
+
+@x [35.753] Where `plain.mem' is.
+@!MP_mem_default:packed array[1..mem_default_length] of char;
+
+@ @<Set init...@>=
+MP_mem_default:='MPlib:plain.mem';
+@.MPlib@>
+@.plain@>
+@^system dependencies@>
+@y
+@!mem_default_length: integer;
+@!MP_mem_default: ^char;
+@!troff_mode:boolean; {has the user requested \.{troff} mode?}
+
+@ 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 [35.755] Change to pack_buffered_name as with pack_file_name.
+for j:=1 to n do append_to_name(xord[MP_mem_default[j]]);
+@y
+if name_of_file then libc_free (name_of_file);
+name_of_file := xmalloc_array (ASCII_code, n+(b-a+1)+mem_ext_length+1);
+for j:=1 to n do append_to_name(xord[MP_mem_default[j]]);
+@z
+% @x [35.755] 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);{set search path to match format}
+% @z
+@x [35.755] 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 [35.756] Mem file opening: only try once, with path searching.
+ pack_buffered_name(0,loc,j-1); {try first without the system file area}
+ if w_open_in(mem_file) then goto found;
+ pack_buffered_name(mem_area_length,loc,j-1);
+ {now try the system mem file area}
+ if w_open_in(mem_file) then goto found;
+@y
+ pack_buffered_name(0,loc,j-1);
+ if w_open_in(mem_file) then goto found;
+@z
+
+@x [still 35.756] Replace `PLAIN' in error messages with `default'.
+ wterm_ln('Sorry, I can''t find that mem file;',' will try PLAIN.');
+@y
+ wterm ('Sorry, I can''t find the mem file `');
+ fputs (stringcast(name_of_file + 1), stdout);
+ wterm ('''; will try `');
+ fputs (MP_mem_default + 1, stdout);
+ wterm_ln ('''.');
+@z
+@x
+ wterm_ln('I can''t find the PLAIN mem file!');
+@.I can't find PLAIN...@>
+@y
+ wterm ('I can''t find the mem file `');
+ fputs (MP_mem_default + 1, stdout);
+ wterm_ln ('''!');
+@.I can't find the mem...@>
+@z
+
+@x [35.758] Make scan_file_name ignore leading tabs as well as spaces.
+@p procedure scan_file_name;
+label done;
+begin begin_name;
+while buffer[loc]=" " do incr(loc);
+@y
+@p procedure scan_file_name;
+label done;
+begin begin_name;
+while (buffer[loc]=" ")or(buffer[loc]=tab) do incr(loc);
+@z
+
+@x [35.760] `logname' is declared in <unistd.h> on some systems.
+`\.{.mem}' and `\.{.tfm}' in order to make the names of \MP's output files.
+@y
+`\.{.mem}' and `\.{.tfm}' in order to make the names of \MP's output files.
+@d log_name == texmf_log_name
+@z
+
+@x [35.763] 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 [35.763] 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 [35.764] <Scan file name...> needs similar leading tab treatment.
+@ @<Scan file name in the buffer@>=
+begin begin_name; k:=first;
+while (buffer[k]=" ")and(k<last) do incr(k);
+@y
+@ @<Scan file name in the buffer@>=
+begin begin_name; k:=first;
+while ((buffer[k]=" ")or(buffer[k]=tab))and(k<last) do incr(k);
+@z
+
+@x [35.765] Adjust for C string conventions.
+@!months:packed array [1..36] of char; {abbreviations of month names}
+@y
+@!months:^char;
+@z
+
+@x [35.765] Change name of recorder file.
+if job_name=0 then job_name:="mpout";
+pack_job_name(".log");
+@y
+if job_name=0 then job_name:=get_job_name("mpout");
+pack_job_name(".fls");
+recorder_change_filename(stringcast(name_of_file+1));
+pack_job_name(".log");
+@z
+
+@x [35.767]
+begin wlog(banner);
+print(mem_ident); print(" ");
+print_int(round_unscaled(internal[day])); print_char(" ");
+months:='JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC';
+@y
+begin wlog(banner);
+wlog (version_string);
+print(mem_ident); print(" ");
+print_int(round_unscaled(internal[day])); print_char(" ");
+months := ' JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC';
+@z
+
+@x [35.767] l.14341 - Print name of TCX, if given.
+end
+@y
+if translate_filename then begin
+ wlog_cr;
+ wlog('(');
+ fputs(translate_filename, log_file);
+ wlog(')');
+end;
+end
+@z
+
+@x [35.768] (try_extension) a_open_in of input file needs path specifier.
+begin pack_file_name(cur_name,cur_area,ext);
+in_name:=cur_name; in_area:=cur_area;
+if a_open_in(cur_file) then try_extension:=true
+else begin if str_vs_str(ext,".mf")=0 then in_area:=MF_area
+ else in_area:=MP_area;
+ pack_file_name(cur_name,in_area,ext);
+ try_extension:=a_open_in(cur_file);
+ end;
+@y
+{|cur_ext| will be \.{two} if the filename was \.{one.two}.}
+begin pack_file_name(cur_name,cur_area,cur_ext);
+in_name:=cur_name; in_area:=cur_area;
+{The extension is not relevant for determining whether we're allowed to
+ open the file.}
+if not open_in_name_ok(stringcast(name_of_file+1)) then try_extension:=false
+else if str_vs_str(ext,".mf")=0 then
+ try_extension:=a_open_in(cur_file, kpse_mf_format)
+else try_extension:=a_open_in(cur_file, kpse_mp_format);
+@z
+
+@x [35.770] We need another loop variable.
+@p procedure start_input; {\MP\ will \.{input} something}
+label done;
+@y
+@p procedure start_input; {\MP\ will \.{input} something}
+label done;
+var j:integer;
+@z
+
+@x [still 35.770] Kpathsea already tries with no extension.
+ if cur_ext="" then
+ if try_extension(".mp") then goto done
+ else if try_extension("") then goto done
+ else if try_extension(".mf") then goto done
+ else do_nothing
+ else if try_extension(cur_ext) then goto done;
+@y
+ if try_extension(".mp") then goto done
+ else if try_extension(".mf") then goto done
+ else do_nothing;
+@z
+
+@x [still 35.770] Allow jobname to be `one.two'.
+ begin job_name:=cur_name; str_ref[job_name]:=max_str_ref;
+@y
+ begin
+ j:=1;
+ begin_name;
+ stop_at_space:=false;
+ while (j<=name_length)and(more_name(name_of_file[j])) do
+ incr(j);
+ stop_at_space:=true;
+ end_name;
+ job_name:=get_job_name(cur_name);
+ str_ref[job_name] := max_str_ref;
+@z
+
+@x [35.771] Cannot return name to string pool, for the e option?
+flush_string(name); name:=cur_name; cur_name:=0
+@y
+@z
+
+@x [35.774] (copy_old_name) Allocate old_file_name dynamically.
+for j:=str_start[s] to str_stop(s)-1 do
+@y
+if old_file_name then libc_free (old_file_name);
+old_file_name := xmalloc_array (ASCII_code, length(s)+1);
+for j:=str_start[s] to str_stop(s)-1 do
+@z
+@x [still 35.774] Avoid blanking rest of nonexistent array.
+for k:=old_name_length+1 to file_name_size do @+old_file_name[k]:=' ';
+@y
+old_file_name[old_name_length + 1] := 0;
+@z
+
+@x [35.775] Declare old_file_name as a regular C string.
+@!old_file_name : packed array[1..file_name_size] of char;
+@y
+@!old_file_name : ^text_char;
+@z
+
+@x [35.776] [Unique to MP] Path selector for |a_open_in| of mpx file.
+if not a_open_in(cur_file) then
+ begin end_file_reading;
+@y
+if not a_open_in(cur_file,no_file_path) then
+ begin end_file_reading;
+@z
+
+@x [35.777] [Unique to MP] Invoke |makempx|.
+copy_old_name(name)
+{System-dependent code should be added here}
+@y
+copy_old_name (name);
+if not call_make_mpx (stringcast(old_file_name+1), stringcast(name_of_file+1))
+ then goto not_found
+@z
+
+@x [35.778] [Unique to MP] Fix help message for our implementation.
+ ("try running it manually through MPtoTeX, TeX, and DVItoMP");
+@y
+ ("try running it manually through MPto -tex, TeX, and DVItoMP.");
+@z
+
+@x [35.782] [Unique to MP] Path selector for |a_open_in| of readfrom file.
+if not a_open_in(rd_file[n]) then goto not_found;
+@y
+if not open_in_name_ok(stringcast(name_of_file+1))
+ or not a_open_in(rd_file[n],kpse_mp_format) then goto not_found;
+@z
+
+@x [35.783] The Amiga needs a different open_write_file.
+@ Open |wr_file[n]| using file name~|s| and update |wr_fname[n]|.
+
+@p procedure open_write_file(s:str_number; n:readf_index);
+begin str_scan_file(s);
+pack_cur_name;
+while not a_open_out(wr_file[n]) do
+ prompt_file_name("file name for write output","");
+wr_fname[n]:=s;
+add_str_ref(s);
+end;
+@y
+@ Open |wr_file[n]| using file name~|s| and update |wr_fname[n]|.
+The Amiga operating system does not permit write access to a file that is
+currently opened in read mode. To avoid disaster, we look for the file to
+be opened in the list of |read_from| files and close it if present.
+
+@d amiga==ifdef('AMIGA')
+@d agima==endif('AMIGA')
+@f amiga==begin
+@f agima==end
+
+@p procedure open_write_file(s:str_number; n:readf_index);
+amiga@;
+label done;
+var @!n0:readf_index; {Scratch variable}
+agima@;
+begin
+ str_scan_file(s);
+ pack_cur_name;
+amiga@;
+ for n0:=0 to read_files-1 do begin
+ if rd_fname[n0]<>0 then begin
+ if str_vs_str(s,rd_fname[n0])=0 then begin
+ a_close(rd_file[n0]);
+ delete_str_ref(rd_fname[n0]);
+ rd_fname[n0]:=0;
+ if n0=read_files-1 then read_files:=n0;
+ goto done;
+ end;
+ end;
+ end;
+done: do_nothing;
+agima@;
+while not open_out_name_ok(stringcast(name_of_file+1))
+ or not a_open_out(wr_file[n]) do
+ prompt_file_name("file name for write output","");
+wr_fname[n]:=s;
+add_str_ref(s);
+{If on first line of input, log file is not ready yet, so don't log.}
+if log_opened then begin
+ old_setting:=selector;
+ if (internal[tracing_online]<=0) then
+ selector:=log_only {Show what we're doing in the log file.}
+ else selector:=term_and_log; {Show what we're doing.}
+ print_nl("write");
+ print_int(n);
+ print(" = `");
+ print_file_name(cur_name,cur_area,cur_ext);
+ print("'."); print_nl(""); print_ln;
+ selector:=old_setting;
+end;
+end;
+@z
+
+@x [41.1040] 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
+
+@x [42.1151] Fix `threshold' conflict with local variable name.
+@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;
+ end;
+end;
+@y
+@p function compute_threshold(@!m:integer):scaled;
+var @!d:scaled; {lower bound on the smallest interval size}
+begin excess:=min_cover(0)-m;
+if excess<=0 then compute_threshold:=0
+else begin repeat d:=perturbation;
+ until min_cover(d+d)<=m;
+ while min_cover(d)>m do d:=perturbation;
+ compute_threshold:=d;
+ end;
+end;
+@z
+
+@x [42.1152] Change the call to the threshold function.
+begin d:=threshold(m); perturbation:=0;
+@y
+begin d:=compute_threshold(m); perturbation:=0;
+@z
+
+@x [45.1164] Writing the tfm file.
+@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 don't work. Why not? So use C macros.
+
+@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 [42.1165] print_file_name
+print_nl("Font metrics written on "); print(metric_file_name); print_char(".");
+@y
+print_nl("Font metrics written on ");
+print_file_name(0,metric_file_name,0);
+print_char(".");
+@z
+
+% [43.1182] [See TeX module 564] Reading tfm files. As a special case,
+% whenever we open a tfm file for input, we read its first byte into
+% "tfm_temp" right away.
+@x
+@d tfget==get(tfm_infile)
+@d tfbyte==tfm_infile^
+@y
+@d tfget==tfm_temp:=getc(tfm_infile)
+@d tfbyte==tfm_temp
+@z
+
+% [43.1186] [See TeX module 575] We only want `eof' on the TFM file
+% to be true if we previously had EOF, not if we're at EOF now.
+% This is like `feof', and unlike our implementation of `eof' elsewhere.
+@x
+if eof(tfm_infile) then goto bad_tfm;
+@y
+if feof(tfm_infile) then goto bad_tfm;
+@z
+
+% [43.1188] [See TeX module 563] TFM file opening.
+@x
+if cur_area="" then cur_area:=MP_font_area;
+if cur_ext="" then cur_ext:=".tfm";
+pack_cur_name;
+@y
+if cur_ext="" then cur_ext:=".tfm";
+pack_cur_name;
+@z
+
+@x [43.1195] [Unique to MP] Path selector for |a_open_in| of ps_tab_file.
+begin name_of_file:=ps_tab_name;
+if a_open_in(ps_tab_file) then
+@y
+begin
+name_length := strlen (ps_tab_name);
+name_of_file := xmalloc_array (ASCII_code, name_length+1);
+strcpy (stringcast(name_of_file+1), ps_tab_name); {copy the string}
+if a_open_in(ps_tab_file, kpse_fontmap_format) then
+@z
+
+@x [43.1198] Allow blank lines and comment lines in |ps_tab_file|.
+@ @<Read at most |lmax| characters from |ps_tab_file| into string |s|...@>=
+str_room(lmax);
+j:=lmax;
+loop @+begin if eoln(ps_tab_file) then
+ fatal_error("The psfont map file is bad!");
+ read(ps_tab_file,c);
+ if c=' ' then goto done;
+@y
+@ If we encounter the end of line before we have started reading
+characters from |ps_tab_file|, we have found an entirely blank
+line and we skip over it. Otherwise, we abort if the line ends
+prematurely. If we encounter a comment character, we also skip
+over the line, since recent versions of \.{dvips} allow comments
+in the font map file.
+
+@<Read at most |lmax| characters from |ps_tab_file| into string |s|...@>=
+str_room(lmax);
+j:=lmax;
+loop @+begin if eoln(ps_tab_file) then
+ if j=lmax then begin flush_cur_string;
+ goto common_ending; {skip over blank line}
+ end
+ else fatal_error("The psfonts.map file is bad!");
+ read(ps_tab_file,c);
+ if ((c='%')or(c='*')or(c=';')or(c='#')) then begin flush_cur_string;
+ goto common_ending; {skip over comment line}
+ end;
+ if ((c=' ')or(c=tab)) then goto done;
+@z
+
+@x [43.1199] Allow tabs as field seperators in |ps_tab_file|.
+repeat if eoln(ps_tab_file) then fatal_error("The psfont map file is bad!");
+ read(ps_tab_file,c);
+until c<>' ';
+repeat decr(j);
+ if j<0 then fatal_error("The psfont map file is bad!");
+ append_char(xord[c]);
+ if eoln(ps_tab_file) then c:=' ' @+else read(ps_tab_file,c);
+until c=' ';
+@y
+repeat if eoln(ps_tab_file) then fatal_error("The psfonts.map file is bad!");
+ read(ps_tab_file,c);
+until ((c<>' ')and(c<>tab));
+repeat decr(j);
+ if j<0 then fatal_error("The psfonts.map file is bad!");
+ append_char(xord[c]);
+ if eoln(ps_tab_file) then c:=' ' @+else read(ps_tab_file,c);
+until ((c=' ')or(c=tab));
+@z
+
+@x [44.1208] l.21341 - i18n fix
+ print(" output file");
+ if total_shipped>1 then print_char("s");
+@y
+ print(" output ");
+ if total_shipped>1 then print("files")
+ else print("file");
+@z
+
+@x [44.1208] print_file_name
+ print(first_file_name);
+@y
+ print_file_name(0,first_file_name,0);
+@z
+@x [44.1208 print_file_name
+ print(last_file_name);
+@y
+ print_file_name(0,last_file_name,0);
+@z
+
+@x [45.1279] INI = VIR.
+mem_ident:=" (INIMP)";
+@y
+if ini_version then mem_ident:=" (INIMP)";
+@z
+
+@x [45.1280] Add base_engine.
+@!w: four_quarters; {four ASCII codes}
+@y
+@!w: four_quarters; {four ASCII codes}
+@!base_engine: ^text_char;
+@z
+
+@x [45.1281] 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 [45.1282] Reading and writing of `mem_file' done in C.
+@d dump_wd(#)==begin mem_file^:=#; put(mem_file);@+end
+@d dump_int(#)==begin mem_file^.int:=#; put(mem_file);@+end
+@d dump_hh(#)==begin mem_file^.hh:=#; put(mem_file);@+end
+@d dump_qqqq(#)==begin mem_file^.qqqq:=#; put(mem_file);@+end
+@y
+@z
+
+@x [45.1283]
+@d undump_wd(#)==begin get(mem_file); #:=mem_file^;@+end
+@d undump_int(#)==begin get(mem_file); #:=mem_file^.int;@+end
+@d undump_hh(#)==begin get(mem_file); #:=mem_file^.hh;@+end
+@d undump_qqqq(#)==begin get(mem_file); #:=mem_file^.qqqq;@+end
+@y
+@z
+
+@x [45.1284]
+dump_int(@$);@/
+@y
+dump_int(@"57324D50); {Web2C \MP's magic constant: "W2MP"}
+{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 [45.1285] Avoid Pascal file convention.
+x:=mem_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<>@"57324D50 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;
+{Dynamic allocation \`a la \.{mf.ch}.}
+@+init
+if ini_version then begin
+ {We allocated these at start-up, but now we need to reallocate.}
+ libc_free (mem);
+ libc_free (str_ref);
+ libc_free (next_str);
+ libc_free (str_start);
+ libc_free (str_pool);
+end;
+@+tini
+undump_int(mem_top);
+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_max := mem_top + extra_mem_top;
+mem:=xmalloc_array (memory_word, mem_max - mem_min + 1);
+@z
+
+@x [45.1287] String pool undumping is dynamic.
+undump_size(0)(pool_size)('string pool size')(pool_ptr);
+undump_size(0)(max_strings-1)('max strings')(max_str_ptr);
+@y
+undump_size(0)(sup_pool_size-pool_free)('string pool size')(pool_ptr);
+if pool_size < pool_ptr + pool_free then
+ pool_size := pool_ptr+pool_free;
+undump_size(0)(sup_max_strings)('max strings')(max_str_ptr);
+@/
+str_ref:=xmalloc_array (str_ref_type, max_strings);
+next_str:=xmalloc_array (str_number, max_strings);
+str_start:=xmalloc_array (pool_pointer, max_strings);
+str_pool:=xmalloc_array (pool_ASCII_code, pool_size);
+@z
+
+@x [45.1293] l.22667 - 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 [45.1293] eof is like feof here.
+undump_int(x);@+if (x<>69073)or eof(mem_file) then goto off_base
+@y
+undump_int(x);@+if (x<>69073)or feof(mem_file) then goto off_base
+@z
+
+@x [45.1294] Eliminate probably-wrong word `preloaded' from mem_idents.
+print(" (preloaded mem="); print(job_name); print_char(" ");
+print_int(round_unscaled(internal[year]) mod 100); print_char(".");
+@y
+print(" (mem="); print(job_name); print_char(" ");
+print_int(round_unscaled(internal[year])); print_char(".");
+@z
+
+@x [46.1298] 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 \.{INIMP}}
+ setup_bound_var (0)('extra_mem_top')(extra_mem_top);
+ {increase high mem in \.{VIRMP}}
+ setup_bound_var (100000)('pool_size')(pool_size);
+ setup_bound_var (75000)('string_vacancies')(string_vacancies);
+ setup_bound_var (3000)('buf_size')(buf_size);
+ setup_bound_var (2000)('path_size')(path_size);
+ setup_bound_var (5000)('pool_free')(pool_free); {min pool avail after fmt}
+ setup_bound_var (15000)('max_strings')(max_strings);
+ 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);
+ if error_line > ssup_error_line then error_line := ssup_error_line;
+
+ const_chk (main_memory);
+@+init
+ if ini_version then
+ extra_mem_top := 0;
+@+tini
+ if extra_mem_top>sup_main_memory then extra_mem_top:=sup_main_memory;
+ mem_top := mem_min + main_memory - 1;
+ mem_max := mem_top;
+
+ const_chk (pool_size);
+ const_chk (string_vacancies);
+ const_chk (buf_size);
+ const_chk (path_size);
+ const_chk (pool_free);
+ const_chk (max_strings);
+
+ buffer:=xmalloc_array (ASCII_code, buf_size);
+ delta_x:=xmalloc_array (scaled, path_size);
+ delta_y:=xmalloc_array (scaled, path_size);
+ delta:=xmalloc_array (scaled, path_size);
+ psi:=xmalloc_array (angle, path_size);
+ theta:=xmalloc_array (angle, path_size);
+ uu:=xmalloc_array (fraction, path_size);
+ vv:=xmalloc_array (angle, path_size);
+ ww:=xmalloc_array (fraction, path_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);
+
+ str_ref:=xmalloc_array (str_ref_type, max_strings);
+ next_str:=xmalloc_array (str_number, max_strings);
+ str_start:=xmalloc_array (pool_pointer, max_strings);
+ str_pool:=xmalloc_array (pool_ASCII_code, pool_size);
+end;
+@+tini
+@z
+
+@x [46.1298] Only do get_strings_started etc. 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_use:=str_ptr; init_pool_ptr:=pool_ptr;@/
+max_str_ptr:=str_ptr; max_pool_ptr:=pool_ptr;
+fix_date_and_time;
+@y
+@!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_use:=str_ptr; init_pool_ptr:=pool_ptr;@/
+max_str_ptr:=str_ptr; max_pool_ptr:=pool_ptr;
+fix_date_and_time;
+end;
+@z
+
+@x [46.1298] Set internal[prologues] in troff mode.
+history:=spotless; {ready to go!}
+@y
+history:=spotless; {ready to go!}
+if troff_mode then @+internal[prologues]:=unity;
+@z
+
+@x [46.1298] Call do_final_end.
+end_of_MP: close_files_and_terminate;
+final_end: ready_already:=0;
+@y
+close_files_and_terminate;
+final_end: do_final_end;
+@z
+
+@x [46.1299] Print new line before termination; maybe switch to editor.
+ 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 [46.1304] (final_cleanup) Only do dump if ini.
+ begin @!init store_mem_file; return;@+tini@/
+@y
+ begin @!init if ini_version then begin store_mem_file; return;end;@+tini@/
+@z
+
+@x [46.1306] l.22937 - Handle %&mem line
+if (mem_ident=0)or(buffer[loc]="&") then
+@y
+if (mem_ident=0)or(buffer[loc]="&")or dump_line then
+@z
+
+@x [46.1307] l.23004
+sys_random_seed := (internal[time] div unity)+internal[day];@/
+@y
+sys_random_seed := get_random_seed;@/
+@z
+
+@x [47.1307] Change read of integer.
+ read(term_in,m);
+ if m<0 then return
+ else if m=0 then
+ begin goto breakpoint;@\ {go to every label at least once}
+ breakpoint: m:=0; @{'BREAKPOINT'@}@\
+ end
+ else begin read(term_in,n);
+@y
+ m:=input_int (stdin);
+ if m<0 then return
+ else if m=0 then
+ begin goto breakpoint;@\ {go to every label at least once}
+ breakpoint: m:=0; @{'BREAKPOINT'@}@\
+ end
+ else begin n:=input_int (stdin);
+@z
+
+@x [47.1308]
+13: begin read(term_in,l); print_cmd_mod(n,l);
+@y
+13: begin l:=input_int (stdin); print_cmd_mod(n,l);
+@z
+
+@x [48.1309] Add editor-switch variable to globals.
+This section should be replaced, if necessary, by any special
+modification of the program
+that are necessary to make \MP\ 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 \MP\ 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;
+
+@ Web2c is deficient, and can only translate pointers to a type
+identifier, not a general type. Easier, if more annoying, to introduce
+this extra definition than to fix Web2c.
+
+@<Types...@> =
+@!str_ref_type = 0..max_str_ref;
+@z