%%%  WEB Change File for TANGLE (Win32 version)
%%%  Time-stamp: "97/01/03 18:17:47 mik"
%%%
%%%  Copyright (C) 1991, 92, 93, 94, 95, 96, 97
%%%	Christian Schenk  <cschenk@berlin.snafu.de>
%%%
%%%  This file is part of MiKTeX.
%%%
%%%  MiKTeX is free software; you can redistribute it and/or
%%%  modify it under the terms of the GNU General Public License as
%%%  published by the Free Software Foundation; either version 2, or (at
%%%  your option) any later version.
%%%  
%%%  MiKTeX is distributed in the hope that it will be useful, but
%%%  WITHOUT ANY WARRANTY; without even the implied warranty of
%%%  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
%%%  General Public License for more details.
%%%  
%%%  You should have received a copy of the GNU General Public License
%%%  along with MiKTeX; if not, write to the Free Software
%%%  Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307,
%%%  USA.

@x
\def\title{TANGLE}
@y
\def\MiKTeX{MiK\TeX}
\def\title{TANGLE for Win32 (\MiKTeX\ 1.06)}
@z

%% **     2  ******************************************************************

@x
@<Error handling procedures@>@/
procedure initialize;
@y
@<External C routines@>@/
@<Error handling procedures@>@/
procedure initialize;
@z

%% **     3  ******************************************************************

@x
@d stat==@{ {change this to `$\\{stat}\equiv\null$'
  when gathering usage statistics}
@d tats==@t@>@} {change this to `$\\{tats}\equiv\null$'
  when gathering usage statistics}
@y
@d stat==
@d tats==
@z

%% **     8  ******************************************************************

@x
@ The following parameters are set big enough to handle \TeX, so they
should be sufficient for most applications of \.{TANGLE}.

@<Constants...@>=
@!buf_size=100; {maximum length of input line}
@!max_bytes=45000; {|1/ww| times the number of bytes in identifiers,
  strings, and module names; must be less than 65536}
@!max_toks=50000; {|1/zz| times the number of bytes in compressed \PASCAL\ code;
  must be less than 65536}
@!max_names=4000; {number of identifiers, strings, module names;
  must be less than 10240}
@!max_texts=2000; {number of replacement texts, must be less than 10240}
@!hash_size=353; {should be prime}
@!longest_name=400; {module names shouldn't be longer than this}
@!line_length=72; {lines of \PASCAL\ output have at most this many characters}
@!out_buf_size=144; {length of output buffer, should be twice |line_length|}
@!stack_size=50; {number of simultaneous levels of macro expansion}
@!max_id_length=12; {long identifiers are chopped to this length, which must
  not exceed |line_length|}
@!unambig_length=7; {identifiers must be unique if chopped to this length}
  {note that 7 is more strict than \PASCAL's 8, but this can be varied}
@y
@ The following parameters are set big enough to handle \TeX, so they
should be sufficient for most applications of \.{TANGLE}.

@<Constants...@>=
@!buf_size=1024; {maximum length of input line}
@!max_bytes=65535; {|1/ww| times the number of bytes in identifiers,
  strings, and module names; must be less than 65536}
@!max_toks=65535; {|1/zz| times the number of bytes in compressed \PASCAL\ code;
  must be less than 65536}
@!max_names=10239; {number of identifiers, strings, module names;
  must be less than 10240}
@!max_texts=10239; {number of replacement texts, must be less than 10240}
@!hash_size=353; {should be prime}
@!longest_name=400; {module names shouldn't be longer than this}
@!line_length=72; {lines of \PASCAL\ output have at most this many characters}
@!out_buf_size=144; {length of output buffer, should be twice |line_length|}
@!stack_size=500; {number of simultaneous levels of macro expansion}
@!max_id_length=12; {long identifiers are chopped to this length, which must
  not exceed |line_length|}
@!unambig_length=12; {identifiers must be unique if chopped to this length}
  {note that 7 is more strict than \PASCAL's 8, but this can be varied}
@z

%% **    12  ******************************************************************

@x
@d text_char == char {the data type of characters in text files}
@d first_text_char=0 {ordinal number of the smallest element of |text_char|}
@d last_text_char=255 {ordinal number of the largest element of |text_char|}
@y
@d text_char == char {the data type of characters in text files}
@d first_text_char=-128 {ordinal number of the smallest element of |text_char|}
@d last_text_char=127 {ordinal number of the largest element of |text_char|}
@z

%% **    21  ******************************************************************

@x
@ Different systems have different ways of specifying that the output on a
certain file will appear on the user's terminal. Here is one way to do this
on the \PASCAL\ system that was used in \.{TANGLE}'s initial development:
@^system dependencies@>

@<Set init...@>=
rewrite(term_out,'TTY:'); {send |term_out| output to the terminal}
@y
@ Different systems have different ways of specifying that the output on a
certain file will appear on the user's terminal.
@^system dependencies@>

@<Set init...@>=
term_out:=output;
@z

%% **    24  ******************************************************************

@x
@ The following code opens the input files.  Since these files were listed
in the program header, we assume that the \PASCAL\ runtime system has
already checked that suitable file names have been given; therefore no
additional error checking needs to be done.
@^system dependencies@>

@p procedure open_input; {prepare to read |web_file| and |change_file|}
begin reset(web_file); reset(change_file);
end;

@ The main output goes to |Pascal_file|, and string pool constants are
written to the |pool| file.

@<Globals...@>=
@!Pascal_file: text_file;
@!pool: text_file;

@ The following code opens |Pascal_file| and |pool|.
Since these files were listed in the program header, we assume that the
\PASCAL\ runtime system has checked that suitable external file names have
been given.
@^system dependencies@>

@<Set init...@>=
rewrite(Pascal_file); rewrite(pool);
@y
@ The following code opens the input files.
@^system dependencies@>

@p procedure open_input; {prepare to read |web_file| and |change_file|}
begin if (c4pargc < 3) then
  fatal_error('Usage: tangle inputfile changefile outputfile poolfile');
c4p_fopen (web_file,c4p_argv[1],c4p_r_mode,true); reset(web_file);
c4p_fopen (change_file,c4p_argv[2],c4p_r_mode,true); reset(change_file);
end;

@ The main output goes to |Pascal_file|, and string pool constants are
written to the |pool| file.

@<Globals...@>=
@!Pascal_file: text_file;
@!pool: text_file;

@ The following code opens |Pascal_file| and |pool|.
@^system dependencies@>

@<Set init...@>=
if (c4pargc < 5) then
  fatal_error('Usage: tangle inputfile changefile outputfile poolfile');
c4p_fopen(Pascal_file,c4p_argv[3],c4p_wb_mode,true); rewrite(Pascal_file);
c4p_fopen(pool,c4p_argv[4],c4p_wb_mode,true); rewrite(pool);
@z

%% **    34  ******************************************************************

@x
@ The |jump_out| procedure just cuts across all active procedure levels
and jumps out of the program. This is the only non-local |goto| statement
in \.{TANGLE}. It is used when no recovery from a particular error has
been provided.

Some \PASCAL\ compilers do not implement non-local |goto| statements.
@^system dependencies@>
In such cases the code that appears at label |end_of_TANGLE| should be
copied into the |jump_out| procedure, followed by a call to a system procedure
that terminates the program.

@d fatal_error(#)==begin new_line; print(#); error; mark_fatal; jump_out;
  end

@<Error handling...@>=
procedure jump_out;
begin goto end_of_TANGLE;
end;
@y
@ The |jump_out| procedure just cuts across all active procedure levels
and jumps out of the program.

@d fatal_error(#)==begin new_line; print(#); error; mark_fatal; jump_out;
  end

@d goto_eof_TANGLE==c4p_longjmp(jump_env, 1)

@<Error handling...@>=
procedure jump_out;
begin goto_eof_TANGLE;
end;
@z

%% **   105  ******************************************************************

@x
@ @<Contribution is \.*...@>=
((t=ident)and(v=3)and@|
 (((out_contrib[1]="D")and(out_contrib[2]="I")and(out_contrib[3]="V")) or@|
 ((out_contrib[1]="M")and(out_contrib[2]="O")and(out_contrib[3]="D")) ))or@|
@^uppercase@>
 ((t=misc)and((v="*")or(v="/")))
@y
@ @<Contribution is \.*...@>=
((t=ident)and(v=3)and@|
 (((out_contrib[1]="d")and(out_contrib[2]="i")and(out_contrib[3]="v")) or@|
 ((out_contrib[1]="m")and(out_contrib[2]="o")and(out_contrib[3]="d")) ))or@|
@^uppercase@>
 ((t=misc)and((v="*")or(v="/")))
@z

%% **   110  ******************************************************************

@x
@ @<If previous output was \.{DIV}...@>=
if (out_ptr=break_ptr+3)or
 ((out_ptr=break_ptr+4)and(out_buf[break_ptr]=" ")) then
@^uppercase@>
  if ((out_buf[out_ptr-3]="D")and(out_buf[out_ptr-2]="I")and
    (out_buf[out_ptr-1]="V"))or @/
     ((out_buf[out_ptr-3]="M")and(out_buf[out_ptr-2]="O")and
    (out_buf[out_ptr-1]="D")) then@/ goto bad_case
@y
@ @<If previous output was \.{DIV}...@>=
if (out_ptr=break_ptr+3)or
 ((out_ptr=break_ptr+4)and(out_buf[break_ptr]=" ")) then
@^uppercase@>
  if ((out_buf[out_ptr-3]="d")and(out_buf[out_ptr-2]="i")and
    (out_buf[out_ptr-1]="v"))or @/
     ((out_buf[out_ptr-3]="m")and(out_buf[out_ptr-2]="o")and
    (out_buf[out_ptr-1]="d")) then@/ goto bad_case
@z

%% **   114  ******************************************************************

@x
@ @<Cases like \.{<>}...@>=
and_sign: begin out_contrib[1]:="A"; out_contrib[2]:="N"; out_contrib[3]:="D";
@^uppercase@>
  send_out(ident,3);
  end;
not_sign: begin out_contrib[1]:="N"; out_contrib[2]:="O"; out_contrib[3]:="T";
  send_out(ident,3);
  end;
set_element_sign: begin out_contrib[1]:="I"; out_contrib[2]:="N";
  send_out(ident,2);
  end;
or_sign: begin out_contrib[1]:="O"; out_contrib[2]:="R"; send_out(ident,2);
  end;
@y
@ @<Cases like \.{<>}...@>=
and_sign: begin out_contrib[1]:="a"; out_contrib[2]:="n"; out_contrib[3]:="d";
@^uppercase@>
  send_out(ident,3);
  end;
not_sign: begin out_contrib[1]:="n"; out_contrib[2]:="o"; out_contrib[3]:="t";
  send_out(ident,3);
  end;
set_element_sign: begin out_contrib[1]:="i"; out_contrib[2]:="n";
  send_out(ident,2);
  end;
or_sign: begin out_contrib[1]:="o"; out_contrib[2]:="r"; send_out(ident,2);
  end;
@z

%% **   116  ******************************************************************

@x
@ Single-character identifiers represent themselves, while longer ones
appear in |byte_mem|. All must be converted to uppercase,
with underlines removed. Extremely long identifiers must be chopped.

(Some \PASCAL\ compilers work with lowercase letters instead of
uppercase. If this module of \.{TANGLE} is changed, it's also necessary
to change from uppercase to lowercase in the modules that are
listed in the index under ``uppercase''.)
@^system dependencies@>
@^uppercase@>

@d up_to(#)==#-24,#-23,#-22,#-21,#-20,#-19,#-18,#-17,#-16,#-15,#-14,
  #-13,#-12,#-11,#-10,#-9,#-8,#-7,#-6,#-5,#-4,#-3,#-2,#-1,#

@<Cases related to identifiers@>=
"A",up_to("Z"): begin out_contrib[1]:=cur_char; send_out(ident,1);
  end;
"a",up_to("z"): begin out_contrib[1]:=cur_char-@'40; send_out(ident,1);
  end;
identifier: begin k:=0; j:=byte_start[cur_val]; w:=cur_val mod ww;
  while (k<max_id_length)and(j<byte_start[cur_val+ww]) do
    begin incr(k); out_contrib[k]:=byte_mem[w,j]; incr(j);
    if out_contrib[k]>="a" then out_contrib[k]:=out_contrib[k]-@'40
    else if out_contrib[k]="_" then decr(k);
    end;
  send_out(ident,k);
  end;
@y
@ Single-character identifiers represent themselves, while longer ones
appear in |byte_mem|. All must be converted to lowercase,
with underlines removed. Extremely long identifiers must be chopped.
@^system dependencies@>
@^uppercase@>

@d up_to(#)==#-24,#-23,#-22,#-21,#-20,#-19,#-18,#-17,#-16,#-15,#-14,
  #-13,#-12,#-11,#-10,#-9,#-8,#-7,#-6,#-5,#-4,#-3,#-2,#-1,#

@<Cases related to identifiers@>=
"A",up_to("Z"): begin out_contrib[1]:=cur_char+@'40; send_out(ident,1);
  end;
"a",up_to("z"): begin out_contrib[1]:=cur_char; send_out(ident,1);
  end;
identifier: begin k:=0; j:=byte_start[cur_val]; w:=cur_val mod ww;
  while (k<max_id_length)and(j<byte_start[cur_val+ww]) do
    begin incr(k); out_contrib[k]:=byte_mem[w,j]; incr(j);
    if (out_contrib[k]>="A")and(out_contrib[k]<="Z") then
       out_contrib[k]:=out_contrib[k]+@'40
    else if out_contrib[k]="_" then decr(k);
    end;
  send_out(ident,k);
  end;
@z

%% **   119  ******************************************************************

@x
  if cur_char="e" then cur_char:="E";
@^uppercase@>
  if cur_char="E" then goto get_fraction
@y
  if cur_char="E" then cur_char:="e";
@^uppercase@>
  if cur_char="e" then goto get_fraction
@z

%% **   120  ******************************************************************

@x
if (out_contrib[k]="E")and((cur_char="+")or(cur_char="-")) then
@^uppercase@>
  begin if k<line_length then incr(k);
  out_contrib[k]:=cur_char; cur_char:=get_output;
  end
else if cur_char="e" then cur_char:="E";
until (cur_char<>"E")and((cur_char<"0")or(cur_char>"9"));
@y
if (out_contrib[k]="e")and((cur_char="+")or(cur_char="-")) then
@^uppercase@>
  begin if k<line_length then incr(k);
  out_contrib[k]:=cur_char; cur_char:=get_output;
  end
else if cur_char="E" then cur_char:="e";
until (cur_char<>"e")and((cur_char<"0")or(cur_char>"9"));
@z

%% **   180  ******************************************************************

@x
reset(term_in,'TTY:','/I'); {open |term_in| as the terminal, don't do a |get|}
@y
term_in:=input;
@z

%% **   182  ******************************************************************

@x
@* The main program.
We have defined plenty of procedures, and it is time to put the last
pieces of the puzzle in place. Here is where \.{TANGLE} starts, and where
it ends.
@^system dependencies@>

@p begin initialize;
@<Initialize the input system@>;
print_ln(banner); {print a ``banner line''}
@y
@* The main program.
We have defined plenty of procedures, and it is time to put the last
pieces of the puzzle in place. Here is where \.{TANGLE} starts, and where
it ends.
@^system dependencies@>

@p begin
if (c4p_setjmp(jump_env) <> 0) then goto end_of_TANGLE;
initialize;
@<Initialize the input system@>;
print(banner); miktex_banner(term_out); new_line; {print a ``banner line''}
@z

%% **   187  ******************************************************************

@x
@ Some implementations may wish to pass the |history| value to the
operating system so that it can be used to govern whether or not other
programs are started. Here we simply report the history to the user.
@^system dependencies@>

@<Print the job |history|@>=
case history of
spotless: print_nl('(No errors were found.)');
harmless_message: print_nl('(Did you see the warning message above?)');
error_message: print_nl('(Pardon me, but I think I spotted something wrong.)');
fatal_message: print_nl('(That was a fatal error, my friend.)');
end {there are no other cases}
@y
@ The value of |history| is passed to the operating system via |@!c4p_exit()|.
@^system dependencies@>

@<Print the job |history|@>=
case history of
spotless: print_nl('(No errors were found.)');
harmless_message: print_nl('(Did you see the warning message above?)');
error_message: print_nl('(Pardon me, but I think I spotted something wrong.)');
fatal_message: print_nl('(That was a fatal error, my friend.)');
end; {there are no other cases}
new_line;
c4p_exit (history);
@z

%% **   188  ******************************************************************

@x
which introduce new modules, can be inserted here; then only the index
itself will get a new module number.
@^system dependencies@>
@y
which introduce new modules, can be inserted here; then only the index
itself will get a new module number.
@^system dependencies@>

@ @<External C routines@>=
procedure miktex_banner (var f:text_file); forward;

@ @<Globals...@>=
@!jump_env : c4p_jmp_buf;
@z
