% 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{} % 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 \read (=0,1,...) used to obtain the % next line from web_. For reasons of efficiency they are \let to % either \w (changing=false), \c (changing=true), of \e (file % has ended). % \g is used to obtain the next line from ch_ and test for % @x/@y/@z, % \prime (prime the change buffer) scans for the next @x from ch_, % and \match discards matching lines from web_ and ch_ 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 \read that returns the next line of web_ in \@web % as well as various auxiliary macros \... % \def\@@init{% initialize input file \edef\x{% define \read streams and \count registers \noexpand\@nrd\@ncsi r% \newread\r \noexpand\@nct\@ncsi l% \newcount\l (line number) \noexpand\@nct\@ncsi s% \newcount\s (status) }\x \@csi l\z@ \@csi s\z@ % \l=0 \s=0 \openin\@csi r\@csi n % \openin\r=\n \@msg{\ifeof\@csi runable to open input file \else \ifnum\@i=\z@ web\else change \@i\fi =\fi \@csi n}% \ifnum\count@=\z@ % =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 % =1,2,3,... for change files \o@o{\@pct \@i. \@csi n}% %% %% \def\g#1{% read change file and test for @x/@y/@z %% \ifeof\r \let\x=\relax \@res=#1 %% \else \@res=0 \read\r to\x %% \advance\l by 1 \expandafter\@test\x ab\@#1 %% \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\w{% return web_i line (changing is false) %% \read % get web_ line %% \ifx\@web\x % test for match %% \expandafter\match % match lines from web_i-i and ch_i %% \fi} % else return web_ line %% \@ecsi w{% return web_i line (changing is false) \@read \@ifx\@web\@ncsi x\@expai{match}\@fi}% %% %% \def\c{% return web_i line (changing is true) %% \g 3 % get ch_i line and test for @z %% \ifnum\@res=3 % @z found %% \@echg % deactivate a change %% \prime % prime the change buffer %% \expandafter\read % read again, now from web_ %% \else \@mod\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\e{% return web_i line (change file has ended) %% \read} % return web_ line %% \@ecsi e{% return web_i line (change file has ended) \@read}% %% %% \def\prime{% prime the change buffer %% \loop %% \g 1 % get ch_i line and test for @x %% \ifnum\@res=1 \else \repeat % repeat until found %% \loop %% \g 0 % get ch_i line %% \ifx\x\empty \repeat % repeat until not blank line %% \ifx\\relax \let\read=\e % change file has ended %% \else \let\read=\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\match{% match lines from web_ and ch_ %% \ifx\@web\relax % web_ and ch_ have ended %% \let\read=\e \ % indicate web_ has ended %% \else \@bchg % activate a change %% \loop \@chg % write a matching line to output (maybe) %% \g 2 % get ch_i line and test for @y %% \ifnum\@res=2 \@endm % end of match found %% \else \read % get web_ line %% \ifx\@web\relax % test for end of web file %% \@err {Web file ended during change} %% \fi %% \ifx\@web\x % test for matching lines %% \else \advance\s by 1 \fi % count mismatches %% \repeat %% \let\read=\c % now changing is true %% \fi %% \read} % get next web_ line again %% \@ecsi{match}{% match lines from web_ and ch_ \@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}}% %% %% \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 \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_ \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