Current File : //usr/share/texlive/texmf-dist/doc/etex/base/webmerge.tex |
% This is webmerge.tex
% Copyright (C) 1995,96 by the NTS team; all rights are reserved.
%
\def\fileversion{V 1.1}
\def\filedate{29 Mar 96}
%
% In order to generate e-TeX several change files have to be applied
% (one after the other) to tex.web. This can be done efficiently with
% programs such as PATCHWEB or TIE. If neither of these is available
% the present program WEBMERGE can be used to merge several change
% files into one change file that can then be used with TANGLE.
%
% If the web file or one of the change files contains tab of form feed
% characters there is, however, a problem that can't be solved in a
% satisfactory way. This should not be a real problem since the
% original tex.web and etex.ch don't contain these characters and there
% is no real good reason why the system-dependent change files should
% (except may be that some editors insist on converting sequences of
% space characters into tabs).
%
% The problem is due to the fact that (an unmodified) TeX cannot write
% tabs or form feeds to the output file. Therefore WEBMERGE offers two
% alternatives: if \ifallowtabs is false (by default) then tab and form
% feed characters are invalid and lead to error messages; if this
% happens one may use the command
% \allowtabstrue
% to allow tab and form feed as valid input characters. If they are
% written to the output file they will appear as '^^I' and '^^L'. This
% must then be changed manually with a suitable editor before the output
% from WEBMERGE can be used as input for TANGLE.
%
% Typically three change files are required to generate e-TeX, e.g.,
% 1. etex.ch (system independent changes for e-TeX)
% 2. tex.ch (system dependent changes for TeX)
% 3. tex.ech (additional system dependent changes for e-TeX)
% The sequence commands (to be used with plain TeX)
% \input webmerge
% \webfile{tex.web} % web file
% \changefile{etex.ch} % 1. change file
% \changefile{tex.ch} % 2. change file
% \changefile{tex.ech} % 3. change file
% \outfile{etex.ch} % output file, start processing
% creates a combined change file `etex.ch'.
% Webmerge uses a temporary file with default name `tmp.tmp',
% the command
% \tempfile{<temp name>}
% can be used to change that name.
%
% Webmerge is slow, therefore PATCHWEB or TIE should be used whenever
% possible. The program checks for correct change files and gives error
% messages similar to those of TANGLE and WEAVE. The error recovery is,
% however, rather limited.
%
% In case of problems please contact:
% Peter Breitenlohner peb@mppmu.mpg.de
% We make @ signs act like letters, temporarily.
\catcode`\@=11
\newif\ifallowtabs % initially false
\def\allowtabs{\catcode`\^^I=12 \catcode`\^^L=12 }
\def\forbidtabs{\catcode`\^^I=15 \catcode`\^^L=15 }
\toksdef\toks@ii=2
% First we redefine plain.tex's \loop to allow the construction
% \loop ... \if... \else ... \repeat
%
\def\@iterate{\@body \expandafter\@iterate\fi}
%
% and to allow nested loops such as
% \loop{... \loop ... \if... \repeat ... \if...}\repeat
% where the braces do not imply grouping
%
\def\loop#1\repeat{%
\toks@\expandafter{\@body}%
\toks@ii\expandafter{\@@body}%
\edef\@@body{\def\noexpand\@body{\the\toks@}%
\def\noexpand\@@body{\the\toks@ii}}%
\def\@body{#1}\@iterate \@@body}
\let\@body=\empty
\let\@@body=\empty
%
\def\@msg{\immediate\write\sixt@@n}
\@msg{*** webmerge \fileversion\space <\filedate> ***}
%
% Conceptually the web file (web_0) is combined with the first change
% file, ch_1, in order to produce a ficticious web file web_1. Then
% web_1 is combined with ch_2 in order to produce web_2 etc.
% The logic of merging is that of TANGLE and WEAVE.
% With several change files there may, however, be changes on top of
% changes, i.e., a line changed by one change file may be changed again
% by another change file.
% The program below uses quite a few control sequences, many of them
% constructed dynamically.
% The most important ones are \<i>read (<i>=0,1,...) used to obtain the
% next line from web_<i>. For reasons of efficiency they are \let to
% either \<i>w (changing=false), \<i>c (changing=true), of \<i>e (file
% has ended).
% \<i>g is used to obtain the next line from ch_<i> and test for
% @x/@y/@z,
% \<i>prime (prime the change buffer) scans for the next @x from ch_<i>,
% and \<i>match discards matching lines from web_<i-1> and ch_<i> until
% an @y is found.
\def\tempfile#1{\def\t@n{#1}}
\def\t@n{tmp.tmp} % default tempfile name
\newread\t@r % read tempfile
\newwrite\t@w % write tempfile
\def\t@o{\immediate\write\t@w} % write to tempfile
\newwrite\o@w % write output file
\def\o@o{\immediate\write\o@w} % write to outfile
\newcount\@nch % number of change files
\newcount\@num % number of active changes
\newcount\@res % result from @x/@y/@z test
\begingroup % \@pct expands to `% '
\lccode`\1=`\%
\lowercase{\endgroup \def\@pct{1 }}
%
% We need macros to define read streams, count registers, and control
% sequences dynamically (inside \edef)
\def\@nrd#1{\ifx#1\relax \csname newread\endcsname#1\fi}
\def\@nct#1{\ifx#1\relax \csname newcount\endcsname#1\else #1\z@ \fi}
\def\@cs#1{\csname#1\endcsname}
\def\@csi#1{\csname\@i#1\endcsname}
\def\@dcs#1{\expandafter\def\csname#1\endcsname}
\def\@ecsi#1{\expandafter\edef\csname\@i#1\endcsname}
\def\@ncsi#1{\expandafter\noexpand\csname\@i#1\endcsname}
\def\@read{\expandafter\noexpand\csname\@ii read\endcsname}
\def\@ifx{\noexpand\ifx}
\def\@ifnum{\noexpand\ifnum}
\def\@ifeof{\noexpand\ifeof\@csi r}
\def\@else{\noexpand\else}
\def\@fi{\noexpand\fi}
\def\@loop{\noexpand\loop}
\def\@repeat{\noexpand\repeat}
\def\@expa{\noexpand\expandafter\noexpand}
\def\@expai#1{\expandafter\@expa\csname\@i#1\endcsname}
\def\webfile#1{% define webfile
\ifnum\@nch=\m@ne \@nch\z@ \@dcs{0n}{#1}%
\else \@msg{\string\webfile{#1} ignored (out of order)}%
\fi}
\def\changefile#1{% define a changefile
\ifnum\@nch<\z@
\@msg{\string\changefile{#1} ignored (missing \string\webfile)}%
\else \ifnum\@nch>8 \@msg{\string\changefile{#1} ignored (too many)}%
\else \advance\@nch\@ne \@dcs{\number\@nch n}{#1}%
\fi \fi}
\def\outfile#1{% define outfile and process
\ifnum\@nch<\@ne
\@msg{\string\outfile{#1} ignored (missing \string\changefile)}%
\else \def\o@n{#1}\@init \@merge \@done
\fi}
\def\@init{% initialize
\@msg{}\@msg{webmerge \fileversion\space <\filedate>}%
\immediate\openout\o@w=\o@n
\o@o{\@pct This is \o@n, a WEB change file produced by webmerge.tex}%
\begingroup
\def\do##1{\catcode`##1=12 }\dospecials
\ifallowtabs \allowtabs \else \forbidtabs \fi
\endlinechar=\m@ne
\count@\z@
\loop \edef\@i{\number\count@}\@@init
\ifnum\count@<\@nch \advance\count@\@ne \let\@ii\@i
\repeat
\o@o{}%
\@msg{out=\o@n, merging ...}}
%
% Here now is the quite complicated macro \@@init
% its main purpose is to dynamically construct
% the macro \<i>read that returns the next line of web_<i> in \@web
% as well as various auxiliary macros \<i>...
%
\def\@@init{% initialize input file <i>
\edef\x{% define \read streams and \count registers
\noexpand\@nrd\@ncsi r% \newread\<i>r
\noexpand\@nct\@ncsi l% \newcount\<i>l (line number)
\noexpand\@nct\@ncsi s% \newcount\<i>s (status)
}\x \@csi l\z@ \@csi s\z@ % \<i>l=0 \<i>s=0
\openin\@csi r\@csi n % \openin\<i>r=\<i>n
\@msg{\ifeof\@csi runable to open input file
\else \ifnum\@i=\z@ web\else change \@i\fi =\fi \@csi n}%
\ifnum\count@=\z@ % <i>=0 for web file
\let\@web\relax
%%
%% \def\0w{% return web_0 line (file not yet ended)
%% \read\0r to\@web \0s=0 % read from web_0, mark as unchanged
%% \ifeof\0r \0e \else \advance\0l by 1 \fi}
%%
\@ecsi w{% return web_0 line (file not yet ended)
\read\@csi rto\@web \@csi s\z@
\@ifeof \@ncsi e\@else \advance\@csi l\@ne \@fi}%
%%
%% \def\0e{% return web_0 line (file has ended)
%% \let\0read=\0e \let\@web=\relax}
%%
\@ecsi e{% return web_0 line (file has ended)
\let\@ncsi{read}\@ncsi e\let\@web\relax}%
%%
%% \ifeof\0r \0e \else \let\0read=\0w
%%
\ifeof\@csi r\@csi e%
\else \edef\x{\let\@ncsi{read}\@ncsi w}\x
\fi
\o@o{\@pct to be applied to \@csi n}%
\o@o{\@pct combining the changes (one after the other) from}%
\else % <i>=1,2,3,... for change files
\o@o{\@pct \@i. \@csi n}%
%%
%% \def\<i>g#1{% read change file and test for @x/@y/@z
%% \ifeof\<i>r \let\<i>x=\relax \@res=#1
%% \else \@res=0 \read\<i>r to\<i>x
%% \advance\<i>l by 1 \expandafter\@test\<i>x ab\@#1<i>
%% \fi}
%%
\@ecsi g##1{% read change file and test for @x/@y/@z
\@ifeof \let\@ncsi x\relax \@res##1%
\@else \@res\z@ \read\@csi rto\@ncsi x%
\advance\@csi l\@ne \@expa\@test\@ncsi xab\noexpand\@##1\@i
\@fi}%
%%
%% \def\<i>w{% return web_i line (changing is false)
%% \<i-1>read % get web_<i-1> line
%% \ifx\@web\<i>x % test for match
%% \expandafter\<i>match % match lines from web_i-i and ch_i
%% \fi} % else return web_<i-1> line
%%
\@ecsi w{% return web_i line (changing is false)
\@read \@ifx\@web\@ncsi x\@expai{match}\@fi}%
%%
%% \def\<i>c{% return web_i line (changing is true)
%% \<i>g 3 % get ch_i line and test for @z
%% \ifnum\@res=3 % @z found
%% \@echg <i> % deactivate a change
%% \<i>prime % prime the change buffer
%% \expandafter\<i>read % read again, now from web_<i-1>
%% \else \@mod\<i>x % return ch_i line, mark as changed (\0s=1)
%% \fi}
%%
\@ecsi c{% return web_i line (changing is true)
\@ncsi g\thr@@
\@ifnum\@res=\thr@@
\noexpand\@echg\@i \@ncsi{prime}\@expai{read}%
\@else \noexpand\@mod\@ncsi x%
\@fi}%
%%
%% \def\<i>e{% return web_i line (change file has ended)
%% \<i-1>read} % return web_<i-1> line
%%
\@ecsi e{% return web_i line (change file has ended)
\@read}%
%%
%% \def\<i>prime{% prime the change buffer
%% \loop
%% \<i>g 1 % get ch_i line and test for @x
%% \ifnum\@res=1 \else \repeat % repeat until found
%% \loop
%% \<i>g 0 % get ch_i line
%% \ifx\<i>x\empty \repeat % repeat until not blank line
%% \ifx\<x>\relax \let\<i>read=\<i>e % change file has ended
%% \else \let\<i>read=\<i>w %
%%
\@ecsi{prime}{% prime the change buffer
\@loop \@ncsi g\@ne \@ifnum\@res=\@ne \@else \@repeat
\@loop \@ncsi g\z@ \@ifx\@ncsi x\noexpand\empty \@repeat
\@ifx\@ncsi x\relax \let\@ncsi{read}\@ncsi e%
\@else \let\@ncsi{read}\@ncsi w%
\@fi}%
%%
%% \def\<i>match{% match lines from web_<i-1> and ch_<i>
%% \ifx\@web\relax % web_<i-1> and ch_<i> have ended
%% \let\<i>read=\<i>e \<i> % indicate web_<i> has ended
%% \else \@bchg % activate a change
%% \loop \@chg % write a matching line to output (maybe)
%% \<i>g 2 % get ch_i line and test for @y
%% \ifnum\@res=2 \@endm <i> % end of match found
%% \else \<i-1>read % get web_<i-1> line
%% \ifx\@web\relax % test for end of web file
%% \@err <i>{Web file ended during change}
%% \fi
%% \ifx\@web\<i>x % test for matching lines
%% \else \advance\<i>s by 1 \fi % count mismatches
%% \repeat
%% \let\<i>read=\<i>c % now changing is true
%% \fi
%% \<i>read} % get next web_<i> line again
%%
\@ecsi{match}{% match lines from web_<i-1> and ch_<i>
\@ifx\@web\relax
\let\@ncsi{read}\@ncsi e%
\@else \noexpand\@bchg
\@loop \noexpand\@chg \@ncsi g\tw@
\@ifnum\@res=\tw@ \noexpand\@endm\@i%
\@else \@read
\@ifx\@web\relax
\noexpand\@err\@i{Web file ended during change}%
\@fi
\@ifx\@web\@ncsi x\@else \advance\@csi s\@ne \@fi
\@repeat
\let\@ncsi{read}\@ncsi c%
\@fi
\@ncsi{read}}%
%%
%% \<i>prime % prime the change buffer
%%
\@csi{prime}% prime the change buffer
\fi}
\def\@done{%terminate
\count@\z@
\loop \edef\@i{\number\count@}%
\ifnum\count@=\z@ \else % change file
\ifeof\@csi r\else \@@err{Change file entry didn't match}\fi
\fi
\closein\@csi r% close input file <i>
\ifnum\count@<\@nch \advance\count@\@ne
\repeat
\endgroup
\immediate\closeout\o@w
\@nch\m@ne % prepare for next \webfile
\@msg{... done}\@msg{}}
\catcode`\0=11 % for \0s
\def\@merge{% process
\@num\z@
\expandafter\loop\csname\number\@nch read\endcsname % read web_<n>
\ifnum\0s=\@ne \t@o{\@web}\fi
\ifx\@web\relax
\else
\repeat}
\def\@chg{\ifnum\0s=\z@ \o@o{\@web}\fi}
\def\@mod{\0s\@ne \let\@web}
\catcode`\0=12
\def\@err#1{\def\@i{#1}\@@err}
\def\@@err#1{\@msg{! #1}%
\@msg{ ... change file \@i\space (\@csi n) line \the\@csi l}}
\def\@test#1#2#3\@{\if#1@ \csname set@#2\endcsname \fi \@eat}
\def\@eat#1#2{}
\def\set@x{\@res\@ne \expandafter\@xyz}
\def\set@y{\@res\tw@ \expandafter\@xyz}
\def\set@z{\@res\thr@@ \expandafter\@xyz}
\let\set@X=\set@x
\let\set@Y=\set@y
\let\set@Z=\set@z
\def\@xyz\@eat#1#2{%
\ifnum#1=\@res
\else \@err#2{Extra \@@xyz{\@res} ignored (expecting \@@xyz#1)}%
\@res\z@
\fi}
\def\@@xyz#1{@\ifcase#1\or x\or y\or z\fi}
\def\@endm#1{%
\ifnum\csname#1s\endcsname>\z@
\@err#1{Hmm... \the\@csi s of the preceding lines failed to match}%
\fi
\csname#1s\endcsname\z@}
\def\@bchg{% activate a change
\ifnum\@num=\z@ % first change activated, start writing to temp
\immediate\openout\t@w=\t@n\space
\o@o{@x l.\number\csname 0l\endcsname}
\fi
\advance\@num\@ne}
\def\@echg#1{% deactivate a change
\expandafter\ifx\csname#1x\endcsname\relax
\@err#1{Change file ended during change}%
\fi
\advance\@num\m@ne
\ifnum\@num=\z@ % last change deactivated
\t@o{@z}%
\immediate\closeout\t@w % close temp file
\openin\t@r=\t@n\space
\o@o{@y}
\loop \read\t@r to\t@x \o@o{\t@x}% copy temp to output
\ifeof\t@r \closein\t@r
\else
\repeat
\fi}
\@nch=\m@ne
\catcode`\@=12 % at signs are no longer letters
\endinput