% \iffalse meta-comment % \begin{macrocode} %<*textmerg|plain> \def\fileversion{2.01} \def\filedate{1994/06/20} \def\filename{textmerg.dtx} \def\Copyright{Copyright (C) 1992,1994 Mike Piff, University of Sheffield, England} % % \end{macrocode} % % This file is placed in the public domain. No provision is made for % support of the use of the facilities herein. % % For updates, contact your nearest CTAN site. % % \fi % % \changes{2.01}{1994/06/20}{First version for LaTeX2e} % \changes{2.01a}{2000/08/25}{rf10@cam.ac.uk --- relaxation of licence % terms} % %\CheckSum{349} % %\title{Text merges in \TeX\ and \LaTeX\thanks{This article originally %appeared in TUGboat Vol 13(4), 1992, p518--523.}} %\author{Mike Piff} %\maketitle %\thispagestyle{headings} %\begin{abstract} % In this article the author explains how to do some standard % and not so standard word processor text merges in \TeX\ documents, % using no other tools than \TeX\ itself. % A common application is to the mail merge or form letter, % where names and addresses are % stored in a file, together with other bits of information, and a standard % letter with variable fields embedded in it is customized for every name % from this file. % Another application is to the pretty-printing of the contents of a database. % % The macros described in |textmerg.sty| work equally % in both plain \TeX\ and \LaTeX. However, this has meant heavy use % of |\def| where |\newcommand| would have been preferable. %\end{abstract} % %\tableofcontents % %\section{Introduction} %It is often said that although \LaTeX\ is good at typesetting %mathematics, it is wholly unsuitable for common word processor functions %such as mail merges. %The latter are easy to achieve in most ordinary word processors, %but in its raw state \LaTeX\ is incapable of doing a mail merge, or, indeed, of %generating the same block of text over and over again but with different %parameters in each block, those parameters having been read from a subsidiary %merge file. %The latter file might possibly be the output from a database or any other %program. % %This article aims to show the reader that such a repetitive task need not be %as difficult as it at first appears. In \TeX, it is possible to hide many %details of a facility inside a subsidiary style file, so that the user is %unaware of what fearful processes are going on in the background. %It is then possible to present the end-user with an extremely simple %interface, perhaps simpler and more powerful than is available in other %systems. % %In earlier TUGboat articles %\cite{Bell:TB8-1-54,Garavelli:TB8-1-53,Lee:TB7-3-187,% %McKinstry:TB8-1-60} it was shown how a standard letter could be %customized by adding names and addresses from a separate file. I aim to show %that it is possible to achieve far more than this with a fairly compact %but general set of macros. % %\section{A simple example} %Suppose that we have a list of student names and examination grades, one %per student, and that we wish to send a letter to each student giving %his/her exam grade. %We must decide first what bits of information must be prepared in %our subsidiary file, by looking at an example letter and finding out which %items change from letter to letter. % %Suppose that one instance of our letter is the following, %a \LaTeX\ example. %\begin{verbatim} %\begin{letter}{Miss Iusta Mo\\ % 34 Winchester Road\\ % Sheffield\\ % England} % \opening{Dear Miss Mo,} % This letter is to inform you % that you obtained grade A in % your recent examinations. % \closing{Yours faithfully,} %\end{letter} %\end{verbatim} %We can see that we need to know the student's title, forename(s), surname, %address and grade to compose such a letter. % %One of the simplest ways of achieving this effect is to prepare a file %with lines of the form %\begin{verbatim} %\MyLetter{Mr}...{C} %\end{verbatim} %for each student and then simply |\input| it into a \LaTeX\ file in which %|\MyLetter| has been defined as having five parameters. %A problem with this approach is that we may not be able to coax the student %database into producing such a file. %Another problem is that we need something more subtle if there are fifty %parameters. For example, we might want to print out the %contents of the student database with one page per student, but it could be %that there are fifty information fields per student. Even worse, %the number of pieces of information per student might not be a %constant number, because, say, we are printing out fields from a related %file in which marks on individual examination papers are held. % %We shall tackle our simple example in a way that lends itself %to more generality later on, and in a form that most database programs %should be capable of handling. % %We thus prepare a subsidiary file |results.dat| %with records of five fields in it. %Each student is represented by five lines of this file, % \begin{macrocode} %<*results> Miss Iusta Mo 34 Winchester Road\\Sheffield\\England A Mr Arthur Minit 43 Sheffield Road\\Winchester\\England C % % \end{macrocode} %and the student records appear one after another in this file. %Thus both the field and record separators are carriage returns. % %\DescribeMacro{\Merge} %\DescribeMacro{\Fields} %\TeX\ itself needs to know three bits of information: %\begin{enumerate} % \item the name of the subsidiary file, % \item the fields to read, and % \item the template of the letter. %\end{enumerate} %We pass it this information in the following form % \begin{macrocode} %<*examp> \documentclass[12pt]{letter} \usepackage{textmerg} \begin{document} \Fields{\Title\Forenames\Surname \Address\Grade} \Merge{results.dat}{% \begin{letter}{\Title\ \Forenames\ \Surname\\\Address} \opening{Dear \Title\ \Surname,} This letter is to inform you that you obtained grade \Grade\ in your recent examinations. \closing{Yours faithfully,} \end{letter}} \end{document} % % \end{macrocode} %\LaTeX\ should %open the subsidiary file and, for each set of five parameters, %generate a letter in the |dvi| file. When it reaches the end of %the merge file, \LaTeX\ should terminate execution of the |\Merge| %command and presumably finish the document. % %\section{A few complications} % %Looking at the above example in a bit more generality, %we see that we are reading %records of $n$ fields from the merge file and placing them into a \TeX\ document %in such a way that they replace $n$ preassigned control sequences. %However, it may happen that the merge file is prepared by humans, who might %possibly have inserted some extra blank lines into the file. Again, it %could be that certain sorts of fields might be blank, %whereas others can never be blank. %Perhaps it would be better to build in some degree of error recovery. % %\DescribeMacro{\Fields} %We shall make the assumption that the first field in any record is definitely %a non-blank one and that we know beforehand whether each %of the others might conceivably be blank. %We make a modification to our |\Fields| statement. %It can contain not only %the field name control sequences but also the tokens |+| and~|-|, %with the following interpretation. A |+| indicates that all following %fields should be re-read until a non-blank result is obtained. %A |-| indicates that any following fields could conceivably be blank, %subject to the restriction that the very first field is always non-blank. % %Thus the command %\begin{verbatim} %\Fields{\a+\b\c-\d} %\end{verbatim} %would indicate that only |\d| is allowed to be blank, because the %|+| token has no effect. %In %\begin{verbatim} %\Fields{-\a\b+-\c+\d} %\end{verbatim} %the initial |-| token enables blank reading of data tokens, but the very %first data token is not permitted to be blank anyway. %Thus |\a| is read as a non-blank token and |\b| as a %possibly blank token. %The sequence |+-| now switches non-blank reading on and off again, %so |\c| is read as possibly blank. %Finally |\d| is non-blank. % %Another complication we allow is that the |\Fields| command can %appear several times in our file. The interpretation is that %the last occurrence of |\Fields| before we encounter the |\Merge| %command will indicate the fields to be read for every record. Any %occurrences of |\Fields| within the merged text indicate a new list %of fields to be read when that command is encountered. This lets us %do some conditional processing, such as\footnote{It is assumed that %{\tt\string\Mrs} expands to {\tt Mrs}.} %\begin{verbatim} % \ifx\Title\Mrs % \Fields{\MaidenName} % \fi %\end{verbatim} %and also gives us some flexibility about the field order later on. % %It should also be stressed that the undefined control sequences %appearing in the template need not %correspond exactly to the fields in the subsidiary file. %An example might be that the subsidiary file contains the text %\begin{verbatim} %Spriggs, Mr Abraham L %\end{verbatim} %and one field read is |\FullName|. %\TeX\ would then have to pre-process this %name to generate its several components as used in the template. %The command |\PreProcess| could be included at the start of the template. %\begin{verbatim} %\def\parse#1, #2 #3\endparse{% % \def\Surname{#1}\def\Title{#2}% % \def\Forenames{#3}} %\def\PreProcess{\expandafter % \parse\FullName\endparse} %\end{verbatim} % %An alternative and simpler looking approach to reading fields from a file %|\fil| might be to define each such field as %follows. %\begin{verbatim} %\def\Field#1{\def#1{\read\fil to#1#1}} %\Field\Name \Field\Address \Field\Mark %\end{verbatim} %The first time |\Name| is encountered, it reads its own expansion %from |\fil| and then expands itself. %Henceforth, it has acquired its new expansion. %The disadvantage is that |\Name| must appear in the %text before any subsidiary field such as |\Surname| %can be used. % %Finally we should consider the possibility that the second parameter of %|\Merge| might be too large to fit into memory. We can clearly handle %this problem by allowing the second parameter merely to consist of the text %|\input template|, so that the root file handles two subsidiary %files, one containing the template and the other containing the fields. % % %\section{A complicated example} % %We will next look at an example in which the template contains a table %of indeterminate length, albeit fixed width. So far our macros work %in either plain \TeX\ or in \LaTeX, but the way in which these %two packages handle %tables is slightly different. However, the only difference that need %concern us is that \LaTeX\ uses |\\| where plain \TeX\ uses |\cr|. % %\DescribeMacro{\MultiRead} %The example given here is in \LaTeX, but our style will work equally well %in plain \TeX. In our student letter we wish to insert a table of course %codes and marks. Since each student did a different number of courses, we %need some way of recognizing the end of the course list in the merge file. %The default will be to insert a blank line at the end of such a sub-list. %Thus, the following text appears before the close of the letter template. %\begin{verbatim} %Here are your marks on individual papers. %\begin{center} % \begin{tabular}{|lr|}\hline % Code&Mark\\\hline % \MultiRead{2}\\\hline % \end{tabular} %\end{center} %\end{verbatim} %The merge file now has the following structure. %\begin{verbatim} %Title %... %Grade %Code %Mark %... %Code %Mark %\end{verbatim} %$\langle${\it blank\/}$\rangle$ %\begin{verbatim} %Title %... %\end{verbatim} % %\DescribeMacro{\MarkEnd} %In other applications some of the fields in the table might possibly be blank. %We then let the user change the $\langle${\it blank\/}$\rangle$ %line marking the end of a list to some other string of his own choosing. %\begin{verbatim} %\MarkEnd{***} %\end{verbatim} % %There might be multiple tables in the same template, %with their data intermingled in the merge file with main fields. %The generalized |\Fields| command allows us %to order the merge file however we want. Thus we could have main fields, then %a table, followed by more main fields, and so on. % %\DescribeMacro{\Process} %A final complication is that the fields appearing in a table are essentially %anonymous. By this I mean that they are transferred into the table as %they are, without any pre-processing possible through appearing %in the template as control sequences. If we wish what appears in %the table to be different from what appears in the file, a mechanism is needed %to tell \TeX\ that a certain column has to be treated in a certain way. %The command %\begin{verbatim} %\Process{n}{\foo} %\end{verbatim} %will replace every field $\langle f\rangle$ read into column $n$ %by |\foo{|$\langle f\rangle$|}|. %It is even possible to do some numerical calculations by this method. % %Here is a \LaTeX\ example to illustrate the table processing %features of |textmerg.sty|. % \begin{macrocode} %<*example> \documentclass[12pt]{article} \usepackage{textmerg} \MarkEnd{***} \Process{2}{\Advance} \def\Advance#1{#1\addtocounter{page}{#1}} \Fields{+\Name\Verb} \begin{document} \Merge{silly.dat}{% Dear \Name,\par Here is a table to \Verb\ at: \Fields{\Width}% \begin{tabular}{*{\Width}c} \MultiRead\Width \end{tabular}.\par \Fields{\Adj}% That was \Adj! \clearpage} \end{document} % % \end{macrocode} %The effect of this file is not apparent until we see |silly.dat|. %It is listed here. % \begin{macrocode} %<*silly> Mike look 3 1 2 3 11 12 13 *** good Shelagh gaze 2 21 22 23 24 *** horrid % % \end{macrocode} % % The same can be done in plain \TeX. % \begin{macrocode} %<*plainexample> \input textmerg \MarkEnd{***} \Process{2}{\Advance} \def\Advance#1{#1\global\advance\count0by#1} \Fields{+\Name\Verb} \Merge{silly.dat}{% Dear \Name,\par Here is a table to \Verb\ at: \Fields{\Width}% \vbox{\halign{\hfil{} ## {}\hfil&&\hfil{} ## {}\hfil\cr \MultiRead\Width\cr }}.\par \Fields{\Adj}% That was \Adj! \vfill\eject} \end % % \end{macrocode} % % %\StopEventually{ %\begin{thebibliography}{GHWW84} %\bibitem[{Bel}87]{Bell:TB8-1-54} %Edwin~V. {Bell, II}. %\newblock {{AutoLetter: A \TeX\ form letter procedure}}. %\newblock {\em TUGBoat}, 8(1):54, April 1987. % %\bibitem[Gar87]{Garavelli:TB8-1-53} %John~S. Garavelli. %\newblock {{Form letter macros}}. %\newblock {\em TUGBoat}, 8(1):53, April 1987. % %\bibitem[Lee86]{Lee:TB7-3-187} %John Lee. %\newblock {{Form letters}}. %\newblock {\em TUGBoat}, 7(3):187, October 1986. % %\bibitem[McK87]{McKinstry:TB8-1-60} %Graeme McKinstry. %\newblock {{Form letters}}. %\newblock {\em TUGBoat}, 8(1):60, April 1987. %\end{thebibliography} %} % % \section{Identification} % % This package can only be used with \LaTeXe, so % an appropriate message is displayed when another % format is used\footnote{However, some code is inserted % to allow its use with plain \protect\TeX}. % \begin{macrocode} %<*textmerg> \NeedsTeXFormat{LaTeX2e}[1994/01/01] % \end{macrocode} % % Announce the package name and its version: % \begin{macrocode} \ProvidesPackage{textmerg}[\filedate] % \end{macrocode} % % And display it on the terminal (and the log file): % \begin{macrocode} \typeout{Package `textmerg' <\filedate>.} \typeout{\Copyright} % % \end{macrocode} % % The plain \TeX\ version will simply |\input| this package file. Thus % we need to know that it will understand everything in the file. % \begin{macrocode} %<*plain> \def\NeedsTeXFormat#1[#2]{} \def\ProvidesPackage#1[#2]{} \def\typeout#1{\immediate\write0{#1}} \input textmerg.sty % % \end{macrocode} % % %\section{Implementation of the simple case} % % \begin{macro}{\glet} %For convenience we define a frequently used combination here. % \begin{macrocode} %<*textmerg> \def\glet{\global\let} % \end{macrocode} % \end{macro} % % \begin{macro}{\MergeFile} % \begin{macro}{\InputFile} %The subsidiary merge file is defined next. A macro is then defined that %attempts to open it for reading. If that is unsuccessful, the file is %closed and an error message is issued. % \begin{macrocode} \newread\MergeFile \def\InputFile#1{% \openin\MergeFile=#1 \ifeof\MergeFile \errmessage{Empty merge file}% \closein\MergeFile \long\def\MakeTemplate##1{% \def\Template{}}% \else\GetInput\fi} % \end{macrocode} % \end{macro} % \end{macro} %The command |\MakeTemplate| will be used later to generate the body %of the form into which fields are inserted. We redefine it if the file %is empty so that it produces no text. % % \begin{macro}{\GetInput} %Because the conditional |\ifeof| does not return true until after an %unsuccessful read operation, a mechanism of looking ahead is used which is %similar to that found in Pascal. % \begin{macrocode} \def\GetInput{{\endlinechar=-1 \global\read\MergeFile to\InputBuffer}} % \end{macrocode} % \end{macro} % % \begin{macro}{\SeeIfEof} % \begin{macro}{\LookAgain} %We set up a mechanism for deciding whether or not we have %exhausted the merge file. It forces |\ifeof| to return true %by skipping over blank lines. % \begin{macrocode} \def\SeeIfEof{% \let\NextLook\relax \ifeof\MergeFile \else \ifx\InputBuffer\empty \LookAgain \fi \fi \NextLook} \def\LookAgain{\GetInput \let\NextLook\SeeIfEof} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\ifNonBlank} % \begin{macro}{\AllowBlank} % \begin{macro}{\DontAllowBlank} %We can now prepare to read actual fields from the merge file. A conditional %is used to indicate whether or not the field we are about to read is %allowed to be blank. We also set up a mechanism for changing its value. % \begin{macrocode} \newif\ifNonBlank \NonBlankfalse \def\AllowBlank{\global\NonBlankfalse} \def\DontAllowBlank{\global\NonBlanktrue} % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\ReadIn} % \begin{macro}{\MissingField} %Fields are actually read by means of the following command. Its only %parameter is the name of the control sequence into which the field is %read. % \begin{macrocode} \def\ReadIn#1{% \ifNonBlank\SeeIfEof\fi \ifeof\MergeFile \gdef#1{??}\MissingField \else \glet#1\InputBuffer \GetInput \fi} \def\MissingField{% \message{Missing field in file}} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\GlobalFields} % \begin{macro}{\Fields} %The |\Fields| command places its parameter into a token %register called |\GlobalFields|. %This command will be redefined by the |\Merge| command. % \begin{macrocode} \newtoks\GlobalFields \def\Fields#1{\GlobalFields{#1}} % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\ParseFields} % \begin{macro}{\EndParseFields} %When a field token list is read, %each individual token within it must be either %read as a field or interpreted as a blank/nonblank switch. %The next token is then read by tail recursion. %It is assumed that the final token in the list is |\EndParseFields|. %This must be defined to expand to something unlikely to be read as a %value of one of the fields, and so we |\let| it to |\ParseFields|. % \begin{macrocode} \def\ParseFields#1{% \ifx#1\EndParseFields \let\NextParse\relax \else \let\NextParse\ParseFields \ifx#1+\DontAllowBlank \else \ifx#1-\AllowBlank \else\ReadIn#1 \fi \fi \fi\NextParse} \let\EndParseFields\ParseFields % \end{macrocode} % \end{macro} % \end{macro} % % \begin{macro}{\ReadFields} %We apply this command to our token register after expanding it. % \begin{macrocode} \def\ReadFields#1{\expandafter\ParseFields \the#1\EndParseFields \AllowBlank} % \end{macrocode} % \end{macro} % % \begin{macro}{\Merge} % \begin{macro}{\MakeTemplate} %At long last we are ready to define the |\Merge| command itself. %The first parameter is the filename of the subsidiary file and the second %is the template or form into which fields are inserted. %Since a |\Fields| command within the |\Merge| text is meant %to act immediately on the token list that follows it, we redefine it %to operate in a different way. % \begin{macrocode} \long\def\Merge#1#2{\begingroup% \InputFile{#1}% \def\Fields##1{% \ParseFields##1\EndParseFields}% \MakeTemplate{#2}\Iterate} \long\def\MakeTemplate#1{\def\Template{#1}} % \end{macrocode} % \end{macro} % \end{macro} %The grouping keeps any changes to the definition of |\MakeTemplate| %local to this merge. %Thus several consecutive merges can be handled %within one document. %The |\endgroup| is supplied by the macro |\Iterate| %when the merge file has been exhausted. % % \begin{macro}{\Iteratecounter} % \begin{macro}{\Iterate} %|\Iterate| must read the fields which were declared before it was entered, %substitute them into its template and repeat itself using tail recursion %if the end of the merge file has not been encountered. % \begin{macrocode} \countdef\Iteratecounter=2 \Iteratecounter=0 \def\Iterate{% \global\advance\Iteratecounter by1 \ReadFields\GlobalFields \Template \SeeIfEof \ifeof\MergeFile \def\NextIteration{% \endgroup\closein\MergeFile}% \else \let\NextIteration\Iterate \fi \NextIteration} % \end{macrocode} % \end{macro} % \end{macro} %The point of the use of counter 2 in the above is that it is accessible to %the print driver for page selection. %Anyone who has started printing 150 letters, all with page number~1, %only to run out of paper half way, will appreciate the use of this artifice! % %\section{Implementation of merged tables} % % \begin{macro}{\MultiCount} % \begin{macro}{\MaxCount} % \begin{macro}{\ifStartOfList} %We set up two counters, one for the column we are reading and the %other for the total number of columns in the table. %We also need a conditional to mark the start of the table, so that %we terminate each row correctly with |\\| or |\cr|, or nothing at all %at the beginning of the first row. % \begin{macrocode} \newcount\MultiCount \newcount\MaxCount \newif\ifStartOfList % \end{macrocode} % \end{macro} % \end{macro} % \end{macro} % % \begin{macro}{\MultiRead} %The parameter to |\MultiRead| is the number of columns to read at a %time. This command passes control to |\NextRead| after initializing %certain parameters. % \begin{macrocode} \def\MultiRead#1{% \ifnum#1>0 \SelectCR \MakeEmpty{#1}% \global\StartOfListtrue \glet\NextRead\MRead \AllowBlank \global\MaxCount=#1 \NextRead \fi} % \end{macrocode} % \end{macro} % % \begin{macro}{\Emptyctr} % \begin{macro}{\MakeEmpty} %The command |\MakeEmpty| is required by the pre-pro\-cess\-ing %of each field. %The idea is that the command |\csname pr|$nn$|\endcsname|, %which we will loosely call |\pr|$nn$, %is executed on each field in column $nn$. However, most of these commands %will be undefined, and so we equate each of those that has not been %defined to |\empty|. % \begin{macrocode} \newcount\Emptyctr \def\MakeEmpty#1{\Emptyctr=0 \loop \advance\Emptyctr by1 \expandafter\ifx\csname pr\the\Emptyctr\endcsname\relax \expandafter\glet\csname pr\the\Emptyctr\endcsname\empty \fi \ifnum\Emptyctr<#1 \repeat} % \end{macrocode} % \end{macro} % \end{macro} %Note that, because of the way we are accessing it via |\csname|, %the first time |\pr|$nn$ is encountered it equates to |\relax|. % % \begin{macro}{\Process} %The command |\Process#1#2| defines |\pr#1| to mean |#2|. % \begin{macrocode} \def\Process#1#2{% \expandafter\def\csname pr#1\endcsname##1{#2{##1}}} % \end{macrocode} % \end{macro} % % \begin{macro}{\MarkEnd} %We need to know how the last row is to be recognized. The default is an %empty line in the merge file. % \begin{macrocode} \def\MarkEnd#1{\gdef\EndMarker{#1}} \MarkEnd{} % \end{macrocode} % \end{macro} % % \begin{macro}{\NextLine} % \begin{macro}{\NextField} %We collect each row in a token register. The full row is assembled %in |\NextLine| before being passed back to \TeX. Each field is %read in |\TempField| and then placed temporarily into %|\NextField|. % \begin{macrocode} \newtoks\NextLine \newtoks\NextField % \end{macrocode} % \end{macro} % \end{macro} %It is not necessary to do things this way; |\edef| can be used %instead, but that approach might expand tokens prematurely. % % \begin{macro}{\AppendNextField} %After the next field has been read, it is appended to |\NextLine|. % \begin{macrocode} \def\AppendNextField{% \global\advance\MultiCount1 \NextField=\expandafter{\TempField}% \edef\Append{\NextLine= {\the\NextLine&\csname pr\the\MultiCount\endcsname {\the\NextField}}}% \Append} % \end{macrocode} % \end{macro} % % \begin{macro}{\EndLine} % \begin{macro}{\FinishLine} %We need to insert the correct end marker after each row of the table. %The token |\cr| must be disguised a little before it is acceptable %in a \LaTeX\ document. % \begin{macrocode} \def\SelectCR{\glet\EndLine\\}% % %\def\SelectCR{\gdef\EndLine{\cr}}% %<*textmerg> \def\FinishLine{% \ifStartOfList \global\StartOfListfalse \else\EndLine\fi} % \end{macrocode} % \end{macro} % \end{macro} %This makes the assumption that if |\array| is defined then we must be %in \LaTeX. % % \begin{macro}{\StopProcessing} %We need a command to finish off a table. This should reset |\NextRead| %to |\AllowBlank| to terminate the tail recursion, %and also do some %error recovery in case the file ends prematurely in the middle of a row. % \begin{macrocode} \def\StopProcessing{% \global\MultiCount\MaxCount \glet\NextRead\AllowBlank} % \end{macrocode} % \end{macro} % % \begin{macro}{\MRead} %The command |\MRead| prepares to read a row of a table. It reads a %field from the merge file and checks to see whether the table has been %exhausted. % \begin{macrocode} \def\MRead{% \global\MultiCount=1 \ReadIn\TempField \ifx\TempField\EndMarker \StopProcessing \else \FinishLine \NextField=\expandafter{\TempField}% \edef\StartLine{\NextLine={\csname pr1\endcsname{\the\NextField}}}% \StartLine \ConstructNextRow \fi \NextRead} % \end{macrocode} % \end{macro} % % \begin{macro}{\ConstructNextRow} %Command |\ConstructNextRow| does most of the work of assembling a row of %the table. %It assembles |\MaxCount| fields %at a time into |\NextLine| unless an error is encountered. % \begin{macrocode} \def\ConstructNextRow{% \ifnum\MultiCount<\MaxCount \loop \ReadIn\TempField \ifx\TempField\EndMarker \glet\TempField\empty \StopProcessing \MissingField \else \ifeof\MergeFile \glet\TempField\empty \StopProcessing \MissingField \fi \fi \AppendNextField \ifnum\MultiCount<\MaxCount \repeat \fi \the\NextLine} % % \end{macrocode} % \end{macro} % % % \section{The documentation driver file} % % This is the driver file that produces this documentation. % We use the document class provided by the \LaTeXe\ distribution % for producing the documentation. % \begin{macrocode} %<*driver> \documentclass{ltxdoc} \RecordChanges \begin{document} \DocInput{textmerg.dtx} \PrintIndex \PrintChanges \end{document} % % \end{macrocode} % % % %\Finale % %\endinput