diff options
Diffstat (limited to 'support/makeprog')
28 files changed, 8229 insertions, 0 deletions
diff --git a/support/makeprog/copying b/support/makeprog/copying new file mode 100644 index 0000000000..9a17037581 --- /dev/null +++ b/support/makeprog/copying @@ -0,0 +1,249 @@ + + GNU GENERAL PUBLIC LICENSE + Version 1, February 1989 + + Copyright (C) 1989 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The license agreements of most software companies try to keep users +at the mercy of those companies. By contrast, our General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. The +General Public License applies to the Free Software Foundation's +software and to any other program whose authors commit to using it. +You can use it for your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Specifically, the General Public License is designed to make +sure that you have the freedom to give away or sell copies of free +software, that you receive source code or can get it if you want it, +that you can change the software or use pieces of it in new free +programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of a such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must tell them their rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any program or other work which +contains a notice placed by the copyright holder saying it may be +distributed under the terms of this General Public License. The +"Program", below, refers to any such program or work, and a "work based +on the Program" means either the Program or any work containing the +Program or a portion of it, either verbatim or with modifications. Each +licensee is addressed as "you". + + 1. You may copy and distribute verbatim copies of the Program's source +code as you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this +General Public License and to the absence of any warranty; and give any +other recipients of the Program a copy of this General Public License +along with the Program. You may charge a fee for the physical act of +transferring a copy. + + 2. You may modify your copy or copies of the Program or any portion of +it, and copy and distribute such modifications under the terms of Paragraph +1 above, provided that you also do the following: + + a) cause the modified files to carry prominent notices stating that + you changed the files and the date of any change; and + + b) cause the whole of any work that you distribute or publish, that + in whole or in part contains the Program or any part thereof, either + with or without modifications, to be licensed at no charge to all + third parties under the terms of this General Public License (except + that you may choose to grant warranty protection to some or all + third parties, at your option). + + c) If the modified program normally reads commands interactively when + run, you must cause it, when started running for such interactive use + in the simplest and most usual way, to print or display an + announcement including an appropriate copyright notice and a notice + that there is no warranty (or else, saying that you provide a + warranty) and that users may redistribute the program under these + conditions, and telling the user how to view a copy of this General + Public License. + + d) You may charge a fee for the physical act of transferring a + copy, and you may at your option offer warranty protection in + exchange for a fee. + +Mere aggregation of another independent work with the Program (or its +derivative) on a volume of a storage or distribution medium does not bring +the other work under the scope of these terms. + + 3. You may copy and distribute the Program (or a portion or derivative of +it, under Paragraph 2) in object code or executable form under the terms of +Paragraphs 1 and 2 above provided that you also do one of the following: + + a) accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of + Paragraphs 1 and 2 above; or, + + b) accompany it with a written offer, valid for at least three + years, to give any third party free (except for a nominal charge + for the cost of distribution) a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of + Paragraphs 1 and 2 above; or, + + c) accompany it with the information you received as to where the + corresponding source code may be obtained. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form alone.) + +Source code for a work means the preferred form of the work for making +modifications to it. For an executable file, complete source code means +all the source code for all modules it contains; but, as a special +exception, it need not include source code for modules which are standard +libraries that accompany the operating system on which the executable +file runs, or for standard header files or definitions files that +accompany that operating system. + + 4. You may not copy, modify, sublicense, distribute or transfer the +Program except as expressly provided under this General Public License. +Any attempt otherwise to copy, modify, sublicense, distribute or transfer +the Program is void, and will automatically terminate your rights to use +the Program under this License. However, parties who have received +copies, or rights to use copies, from you under this General Public +License will not have their licenses terminated so long as such parties +remain in full compliance. + + 5. By copying, distributing or modifying the Program (or any work based +on the Program) you indicate your acceptance of this license to do so, +and all its terms and conditions. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the original +licensor to copy, distribute or modify the Program subject to these +terms and conditions. You may not impose any further restrictions on the +recipients' exercise of the rights granted herein. + + 7. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of the license which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +the license, you may choose any version ever published by the Free Software +Foundation. + + 8. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to humanity, the best way to achieve this is to make it +free software which everyone can redistribute and change under these +terms. + + To do so, attach the following notices to the program. It is safest to +attach them to the start of each source file to most effectively convey +the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program 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 1, or (at your option) + any later version. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19xx name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the +appropriate parts of the General Public License. Of course, the +commands you use may be called something other than `show w' and `show +c'; they could even be mouse-clicks or menu items--whatever suits your +program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + program `Gnomovision' (a program to direct compilers to make passes + at assemblers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/support/makeprog/docu/mkprgdoc.bbl b/support/makeprog/docu/mkprgdoc.bbl new file mode 100644 index 0000000000..b7715448b2 --- /dev/null +++ b/support/makeprog/docu/mkprgdoc.bbl @@ -0,0 +1,38 @@ +\begin{thebibliography}{1} + +\bibitem{gnu:license} +Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +\newblock {\em {GNU} General Public License}, February 1989. +\newblock Version 1. + +\bibitem{kg-wr:tie} +Klaus Guntermann and Wolfgang R{\"u}lling. +\newblock Another approach to multiple changefiles. +\newblock {\em TUGboat}, 7(3):134, 1986. + +\bibitem{kg-js:cweb} +Klaus Guntermann and Joachim Schrod. +\newblock {\WEB} adapted to {C}. +\newblock {\em TUGboat}, 7(3):134--137, 1986. + +\bibitem{knuth:web} +Donald~E. Knuth. +\newblock The {\WEB} system of structured documentation. +\newblock Computer Science Report CS980, Stanford University, 1983. + +\bibitem{knuth:literate} +Donald~E. Knuth. +\newblock Literate programming. +\newblock {\em The Computer Journal}, 27(1):97--111, 1984. + +\bibitem{mittelbach:doc} +Frank Mittelbach. +\newblock The {\tt doc\/} style option. +\newblock {\em TUGboat}, 10(2):245--273, 1989. + +\bibitem{ramsey:spider} +Norman Ramsey. +\newblock Weaving a language-independent {\WEB}. +\newblock {\em Communications of the ACM}, 32(9):1051--1055, 1989. + +\end{thebibliography} diff --git a/support/makeprog/docu/mkprgdoc.tex b/support/makeprog/docu/mkprgdoc.tex new file mode 100644 index 0000000000..83ef7852e3 --- /dev/null +++ b/support/makeprog/docu/mkprgdoc.tex @@ -0,0 +1,408 @@ +% mkprgdoc.tex 06 Oct 90 +%------------------------------------------------------------ +% (c) 1988-1990 by J.Schrod. + +% +% documentation for the MAKEPROG system +% LaTeX +% + +% +% VERSION HISTORY +% +% DATE WHO REMARK +% 90-10-06 js added hint to doc2tex and tex2doc. +% MAKEPROG will be available at tuglib. +% 90-09-21 js new progdoc, progltx mentioned everywhere. +% 90-06-11 js rewritten in LaTeX. +% 89-02-01 js updated with comments of Klaus Guntermann. +% 88-12-05 js first version (for ftp/Bitnet). + +% author: +% js: Joachim Schrod <xitijsch@ddathd21.bitnet> +% contributors: +% (-: place enough for your name! :-) + + + + +\documentstyle{article} + + +%\advance\textheight by \baselineskip + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% local macros +% + +\makeatletter + + +% fonts + +\let\mc=\small % medium caps for acronyms +\font\tentex=cmtex10 % typewriter extended ASCII 10pt +\let\ttex=\tentex + + +% short hands + +\def\MAKEPROG{{\mc MAKEPROG}} +\def\CWEB{{\mc CWEB\/}} +\def\WEB{{\tt WEB\/}} +\def\TANGLE{{\tt TANGLE\/}} +\def\WEAVE{{\tt WEAVE\/}} +\def\TIE{{\tt TIE\/}} +\def\DVI{{\tt DVI\/}} +\def\spider{{\tt SpiderWEB\/}} + + +% footnotes on the titlepage without any marker + +\def\titlenote#1{% + \begingroup + \def\@makefntext##1{\noindent ##1}% + \footnotetext{% + \interlinepenalty\@M + \ignorespaces #1% + }% + \endgroup + } + + +% |...| verbatim from progdoc.doc. look there for the explanations. + +\catcode`\|=\active +\def|{% + \leavevmode + \hbox\bgroup + \let\par\space \setupverb@tim + \let|\egroup + } +\def\setupverb@tim{% + \let\do\@makeother \dospecials + \parskip\z@skip \parindent\z@ + \obeylines \obeyspaces \frenchspacing + \ttex + } + +% an inch symbol + +\def\inch{{\tt "\/}} + + + +\makeatother + +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + +\begin{document} + + + +\title{The MAKEPROG System of Documentation} +\author{Joachim Schrod} + +\date{October 1990} + +%%%%% moved below after footnotes -- the titlenote should be on the +%%%%% bottom of the page +% +%\titlenote{ +% \copyright{} Copyright 1988--1990 by Joachim Schrod. +% All rights reserved. +% } + +\maketitle + + + +\section{Introduction} + +In his article \cite{knuth:literate} {\sc D.~Knuth} has introduced +the concept of {\it Literate Programming}. This concept focused on +the following statement: ``Instead of imagining that our main task is +to instruct a {\sl computer\/} what to do, let us concentrate rather +on explaining to {\sl human beings\/} what we want a computer to +do.'' To support this for Pascal programs he has created the {\sl +\WEB{} system of structured documentation} \cite{knuth:web}, an +extension of Pascal. In \WEB{} a program is splitted into sections, +each section contains a documentation and a program part (both parts +can be empty).% + \footnote{% + In fact there is a third part, the macro part, which is not + important in this context. + } + Such a \WEB{} program is transformed by the \TANGLE{} processor into +a program source for the compiler and the \WEAVE{} processor produces +an output which can be fed into \TeX{} to get a fine looking +document. Both \TANGLE{} and \WEAVE{} support the inclusion of +changefiles, with these files lines of text can be replaced by other +lines. + +At the Technichal University of Darmstadt we have used this +concept---with \CWEB{} \cite{kg-js:cweb}---in many of our projects, +e.g.\ for our portable \DVI{} driver family. With the introduction of +\spider{} made by {\sc N.~Ramsey} \cite{ramsey:spider} a \WEB{} +generator became available which allows the generation of a new +\WEB{} system for a specific language. But even with \spider{} not +all languages may be supported because the lexical analysis is fixed +to languages with tokens separated by white space. E.g., +prettyprinting \TeX{} macros is a non-trivial task due to \TeX's +dynamic lexical analysis. Furthermore \spider{} does not run on as +many computer systems as \WEB{} does because it is implemented in +{\tt awk\/} that is not as widespread.% + \footnote{% + But please note that there exists {\tt gawk}, the {\tt awk\/} + implementation of the GNU project, which is free software and + may be ported to more systems. + } + (In my opinion this is one of the strongest drawbacks of \spider{}, +it should be rewritten in \WEB{} urgently.) Or people do not have +enough time to generate a \WEB{} system even if they should do so if +they plan to work longer with this specific formal language. + +\titlenote{\strut} +\titlenote{ + \copyright{} Copyright 1988--1990 by Joachim Schrod. + All rights reserved. + } + +To close this gap I have developed the \MAKEPROG{} system. It allows +to document programs with \TeX{} in a \WEB{} like fashion, the +program parts of the documentation file can be extracted to yield the +program file. During the extraction process the documentation file +can be altered with a changefile. Multiple changefiles may be used +with the help of \TIE{}~\cite{kg-wr:tie}. + +The \MAKEPROG{} system consists of two parts: (1)~the \MAKEPROG{} +processor which does the extraction and (2)~the macro files |progdoc| +(for usage with Plain \TeX{}) or |progltx| (as an article style +option with \LaTeX{}) which make formatting facilities available. +The \MAKEPROG{} processor is derived from \TANGLE{}, therefore there +should be no difficulties for any site running \WEB{} to install +\MAKEPROG{}. The macros are implemented with \MAKEPROG{}, the files +|progdoc.doc| resp.\ |progltx.doc| are the ultimate sources. +|progdoc.tex| or |progltx.sty| is only delivered to allow the +printing of |progdoc.doc|. + + + +\section{Distribution} + +\MAKEPROG{} 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~1, or (at your +option) any later version. + +\MAKEPROG{} is distributed in the hope that it will be useful, but +{\bf without any warranty\/}; without even the implied warranty of +{\bf merchantability\/} or {\bf fitness for a particular purpose}. See +the GNU General Public License~\cite{gnu:license} for more details. + +At the moment \MAKEPROG{} may be obtained from {\tt +LISTSERV@DHDURZ1\/} (filelist {\tt WEBWARE\/}) for the Bitnet folks, +from {\tt tuglib@science.utah.edu}, and it is included in the +BSD/UNIX distribution of Pierre MacKay (so it may be available via +anonymous ftp from {\tt june.cs.washington.edu}, too). + +If you want to get a floppy (DOS format, 5.25\inch{}~HD, 3.5\inch{} +HD or~DD) with the \MAKEPROG{} system you may contact me. But please +note that you will have to pay a handling charge of DM~40. (``When we +speak of free software, we are referring to freedom, not +price.''~\cite{gnu:license}) On my distribution disk is \MAKEPROG{}, +\TIE{}, and \WEB{}. + + + +\section{How to Use MAKEPROG} + +If you use \LaTeX{} you must include |progltx| as a document style +option, if you use Plain \TeX{} you must include the macro file with +|\input progdoc|. Afterwards you can structure your document into +sections and the sections into groups. The first section of a group +starts with |\chap|, it corresponds to the starred section (`|@*|') +of a \WEB{} program. |\chap| has one parameter, the title of the +section group. The parameter is ended by a dot. The dot is printed +by the macro. Every other section starts with |\sect|. These macros +produce a number in front of each section; this number is incremented +with each new section. Of course, in \LaTeX{} you may use the normal +\LaTeX{} sectioning markup if you prefer that one. + +Within a section one or more program part(s) can be specified with the +macros |\beginprog| and |\endprog|. Both macros must start at the +beginning of a line. If |\beginprog| does not start at the beginning +of a line verbatim typesetting will be switched on but no extraction +to the program file will result afterwards. After |\beginprog| the +rest of the line is ignored. If |\endprog| does not start at the +beginning of a line or if it is not followed by white space (blanks, +tabs, or end of line) neither verbatim typesetting nor extracting will +stop. + +Please note that even with \LaTeX{} |\beginprog| and |\endprog| must +be used for the markup of the program parts, instead of +|\begin{prog}| or |\end{prog}| which would be more ``\LaTeX{}-like.'' +The reason behind this is that I didn't want to maintain two versions +of the \MAKEPROG{} processor---but \MAKEPROG{} wants to see the +non-\LaTeX{}-like macros. But everybody is encouraged to change it by +himself. + +Outside of the program part---in the so called documentation +part---you can use the vertical bar to print small texts verbatim, +e.g.\ identifiers, macro names, etc. A vertical bar starts the +verbatim mode, the next vertical bar stops it. + +After you have finished writing your document you can print it with +\TeX{} and you can run the \MAKEPROG{} processor to extract all +program parts into a program file. During the extraction \MAKEPROG{} +will recognize change files like \TANGLE{} does. + + + +\section{Bells and Whistles} + +Within the documentation part you can use |\origvert| to get an +original vertical bar. |\vbar| is the character with the {\mc ASCII} +representation of a vertical bar in the actual font. + +If you use Plain \TeX{} and |progdoc| you may change the appearance +of the title page with the table of contents. This page is output as +the last page and it's layout is like in \WEB{}. Before the table of +contents the macro |\topofcontents| is executed. The default +definition of this macro supplies the title and glue on the top. The +header line above the table is typeset by |\HeaderToc|, each contents +line is typeset by |\tocline|. You can change the definitions within +|\topofcontents|. After the table the macro |\botofcontents| is +executed which usually just supplies glue, but may be redefined to +incorporate copyright notices etc. + +\smallskip + +If you document \TeX{} macros with \MAKEPROG{} and send them to +people who don't have \MAKEPROG{} and if you have {\mc LEX\/} +available, you may use |doc2tex| to transform the \MAKEPROG{} source +into a \TeX{} source. All documentation parts are turned into \TeX{} +comments and a paragraph is added which includes a hint to +\MAKEPROG{}. |tex2doc| converts such a file back to a \MAKEPROG{} +source. + + + +\section{Problems and Errors} + +The following deficiencies are known to me (some are not inherent +but there wasn't the time to do it until now): + % + \begin{itemize} + +\item \MAKEPROG{} does not rearrange the code as \TANGLE{} does. +This is the most important drawback. The support of stepwise +refinement is one of \WEB{}'s main advantages. + +\item \MAKEPROG{} does not prettyprint the program part because it +does not know anything about the target language and therefore +nothing about the lexical and syntactical structure of the program. +Instead it just prints the program part verbatim. But compared to +wide spread macros which set text verbatim it has the advantage +that you can print your program part if you have embedded tabs in it. +The macros will replace every tab with one to eight spaces according +to the current column. + +\item \MAKEPROG{} does not produce any index. This is impossible +because it does not know what an identifier looks like. (This can +even change inmidst a program, cf.~\TeX!) + +\item Because the documentation file is a \TeX{} file and \TeX{} does +not recognize change files the complete documentation (with a change +file) cannot be printed. \TIE{} must be used to create a new master +file which can be printed afterwards. + +\item The page breaking is not satisfying in all cases. I still have +to fiddle with the penalties. + +\item \MAKEPROG{} should insert the current date as a comment line in +front of the produced program file. The syntax of the comment line +(start and end of a comment) must be specifiable. + +\end{itemize} + +If you have found an error or if you have some remarks please contact +me. I~prefer electronic mail and I will acknowledge your mail within +a week. Send bug reports to: +% + \begin{quote} + Detig$\,\cdot\,$Schrod \TeX{}sys\\ + Joachim Schrod\\ + Kranichweg 1\\[.5\baselineskip] + D-6074 R\"odermark-Urberach\\ + FR Germany\\[.5\baselineskip] + Email: {\tt xitijsch@ddathd21.bitnet} + \end{quote} + + + +\section{Related Work} + +Frank Mittelbach and Rainer Sch\"opf have presented |doc| +\cite{mittelbach:doc}, a \LaTeX{} style option for documenting +\LaTeX{} styles. An accompanying \LaTeX{} input file (|docstrip|) +strips these documents. Although their approach seems to be similar +to the one presented here, it should be noticed that there are +several important differences. + +At first |doc| was created for documenting \LaTeX{} macros, while +\MAKEPROG{} was created to support the documentation of {\it formal +representations which are processed by a computer}, e.g.\ programs. +Therefore |doc| was able to supply specific (semantic dependent) help +for automatic creation of an index and of marginal notes whereas +\MAKEPROG{} must refer the user to the standard features available in +\LaTeX{} or Plain \TeX{} because it has no knowledge about the +semantic structure of the program. + +Furthermore \MAKEPROG{} supports the changefile mechanism which is +rarely needed for the documentation of \TeX{} macros because \TeX{} +is (almost) the same on all computer systems but programs in general +must be adapted to local circumstances, i.e., system dependent stuff +must be included. + + + + +\appendix + + + +\section{Installation} + +The first step is to install the \MAKEPROG{} processor. Because it +is derived from \TANGLE{} this should be rather easy. Just take your +local \TANGLE{} change file and you should have very few alterations +(perhaps much to delete). You will need to put the \MAKEPROG{} +processor somewhere where your local command processor will find +it---perhaps you will even need a command script around it. But this +will be the same as it was with \TANGLE{}. (To ease the installation +for many sites I have included changefiles for {\tt WEBtoC\/} and for +Turbo Pascal.) + +You can test your new program by feeding |progdoc.doc| resp.\ +|progltx.doc| through it. The output must be identical to +|progdoc.tex| resp.\ |progltx.sty|. Well, the main work is now done. +You still have to put |progdoc.tex| and |progltx.sty| in directories +where \TeX{} will find them and then have much fun. (Don't +worry---be happy$\,\ldots$) + + + + +\bibliographystyle{plain} +\bibliography{web,gnu,tug} + + + + +\end{document} diff --git a/support/makeprog/docu/primes.web b/support/makeprog/docu/primes.web new file mode 100644 index 0000000000..07554b0ef3 --- /dev/null +++ b/support/makeprog/docu/primes.web @@ -0,0 +1,414 @@ +% limbo material +\font\ninerm=amr9 +\let\mc=\ninerm % medium caps for names like PASCAL +\def\WEB{{\tt WEB}} +\def\PASCAL{{\mc PASCAL}} +\def\[{\ifhmode\ \fi$[\![$} +\def\]{$]\!]$\ } +\def\<{$\langle\,$} +\def\>{$\,\rangle$} +\def\Dijk{{2}} % unnecessary when combined with text of paper +\def\goto{{3}} % ditto +\hyphenation{Dijk-stra} % ditto +\def\sec{{\tensy x}} +\hsize=84mm + +@* Printing primes: An example of \WEB. +The following program is essentially the same as Edsger Dijkstra's +@^Dijkstra, Edsger@> ``first example of step-wise program composition,'' +found on pages 26--39 of his {\sl Notes on Structured Programming},$^{\Dijk}$ +but it has been translated into the \WEB\ language. @.WEB@> + +\[Double brackets will be used in what follows to enclose comments +relating to \WEB\ itself, because the chief purpose of this program +is to introduce the reader to the \WEB\ style of documentation. +\WEB\ programs are always broken into small sections, each +of which has a serial number; the present section is number~1.\] + +Dijkstra's program prints a table of the first thousand prime numbers. We +shall begin as he did, by reducing the entire program to its top-level +description. \[Every section in a \WEB\ program begins with optional {\it +commentary\/} about that section, and ends with optional {\it program +text\/} for the section. For example, you are now reading part of the +commentary in \sec1, and the program text for \sec1 immediately follows +the present paragraph. Program texts are specifications of \PASCAL\ +programs; they either use \PASCAL\ language directly, or they use angle +brackets to represent \PASCAL\ code that appears in other sections. For +example, the angle-bracket notation `\X2:Program to print $\ldots$ +numbers\X' is \WEB's way of saying the following: ``The \PASCAL\ text to +be inserted here is called `Program to print $\ldots$ numbers', and you +can find out all about it by looking at section~2.'' One of the main +characteristics of \WEB\ is that different parts of the program are +usually abbreviated, by giving them such an informal top-level +description.\] + +@p @<Program to print the first thousand prime numbers@> + +@ This program has no input, because we want to keep it rather simple. +The result of the program will be to produce a list of the first +thousand prime numbers, and this list will appear on the |output| file. + +Since there is no input, we declare the value |m=1000| as a compile-time +constant. The program itself is capable of generating the first +|m| prime numbers for any positive |m|, as long as the computer's +finite limitations are not exceeded. + +\[The program text below specifies the ``expanded meaning'' of `\X2:Program +to print $\ldots$ numbers\X'; notice that it involves the top-level +descriptions of three other sections. When those top-level descriptions +are replaced by their expanded meanings, a syntactically correct \PASCAL\ +program will be obtained.\] + +@<Program to print...@>= +program print_primes(output); +const @!m=1000; @<Other constants of the program@>@; +var @<Variables of the program@>@; +begin @<Print the first |m| prime numbers@>; +end. + +@* Plan of the program. +We shall proceed to fill out the rest of the program by making whatever +decisions seem easiest at each step; the idea will be to strive for +simplicity first and efficiency later, in order to see where this leads us. +The final program may not be optimum, but we want it to be reliable, +well motivated, and reasonably fast. + +Let us decide at this point to maintain a table that includes all of the +prime numbers that will be generated, and to separate the generation +problem from the printing problem. + +\[The \WEB\ description you are reading once again follows a pattern that +will soon be familiar: A typical section begins with comments and +ends with program text. The comments motivate and explain noteworthy +features of the program text.\] + +@<Print the first...@>= +@<Fill table |p| with the first |m| prime numbers@>; +@<Print table |p|@> + +@ How should table |p| be represented? Two possibilities suggest +themselves: We could construct a sufficiently large array +of boolean values in which the $k$th entry is |true| if and only if the +number~|k| is prime; or we could build an array of integers in which +the |k|th entry is the |k|th prime number. Let us choose the latter +alternative, by introducing an integer array called |p[1..m]|. + +In the documentation below, the notation `|p[k]|' will refer to the +|k|th element of array~|p|, while `$p_k$' will refer to the $k$th +prime number. If the program is correct, |p[k]| will either be +equal to $p_k$ or it will not yet have been assigned any value. + +\[Incidentally, our program will eventually make use of several +more variables as we refine the data structures. All of the sections +where variables are declared will be called `\X4:Variables of the +program\X'; the number `{\eightrm4}' in this name refers to the +present section, which is the first section to specify the +expanded meaning of `\<Variables of the program\>'. +The note `{\eightrm See also $\ldots$}' refers to all of the other +sections that have the same top-level description. The expanded meaning of +`\X4:Variables of the program\X' consists of all the program texts +for this name, not just the text found in~\sec4.\] + +@<Variables...@>=@!p:array[1..m] of integer; + {the first |m| prime numbers, in increasing order} + +@* The output phase. +Let's work on the second part of the program first. It's not as interesting +as the problem of computing prime numbers; but the job of printing must be +done sooner or later, and we might as well do it sooner, since it will +be good to have it done. \[And it is easier to learn \WEB\ when reading a +program that has comparatively few distracting complications.\] + +Since |p| is simply an array of integers, there is little difficulty +in printing the output, except that we need to decide upon a suitable +output format. Let us print the table on separate pages, with |rr| rows +and |cc| columns per page, where every column is |ww| character positions +wide. In this case we shall choose |rr=50|, |cc=4|, and |ww=10|, so that +the first 1000 primes will appear on five pages. The program will +not assume that |m| is an exact multiple of $|rr|\cdot|cc|$. +@^output format@> + +@<Other constants...@>= +@!rr=50; {this many rows will be on each page in the output} +@!cc=4; {this many columns will be on each page in the output} +@!ww=10; {this many character positions will be used in each column} + +@ In order to keep this program reasonably free of notations that +are uniquely \PASCAL esque, \[and in order to illustrate more of the +facilities of \WEB,\] a few macro definitions for low-level output +instructions are introduced here. All of the output-oriented commands +in the remainder of the program will be stated in terms of five +simple primitives called |print_string|, |print_integer|, |print_entry|, +|new_line|, and |new_page|. + +\[Sections of a \WEB\ program are allowed to contain {\it macro definitions\/} +between the opening comments and the closing program text. The +general format for each section is actually tripartite: commentary, +then definitions, then program. Any of the three parts may be absent; +for example, the present section contains no program text.\] + +\[Simple macros simply substitute a bit of \PASCAL\ code for an +identifier. Parametric macros are similar, but they also substitute +an argument wherever `\#' occurs in the macro definition. The first three +macro definitions here are parametric; the other two are simple.\] + +@d print_string(#)==write(#) {put a given string into the |output| file} +@d print_integer(#)==write(#:1) {put a given integer into the |output| + file, in decimal notation, using only as many digit positions as necessary} +@d print_entry(#)==write(#:ww) {like |print_integer|, but |ww| character + positions are filled, inserting blanks at the left} +@d new_line==write_ln {advance to a new line in the |output| file} +@d new_page==page {advance to a new page in the |output| file} + +@ Several variables are needed to govern the output process. When we begin +to print a new page, the variable |page_number| will be the ordinal number +of that page, and |page_offset| will be such that |p[page_offset]| is the +first prime to be printed. Similarly, |p[row_offset]| will be the first +prime in a given row. + +\[Notice the notation `$+\S$' below; this indicates that the present +section has the same name as a previous section, so the program text +will be appended to some text that was previously specified.\] + +@<Variables...@>= +@!page_number:integer; {one more than the number of pages printed so far} +@!page_offset:integer; {index into |p| for the first entry on the current page} +@!row_offset:integer; {index into |p| for the first entry in the current row} +@!c:0..cc; {runs through the columns in a row} + +@ Now that appropriate auxiliary variables have been introduced, the process +of outputting table~|p| almost writes itself. + +@<Print table |p|@>= +begin page_number:=1; page_offset:=1; +while page_offset<=m do + begin @<Output a page of answers@>; + page_number:=page_number+1; + page_offset:=page_offset+rr*cc; + end; +end + +@ A simple heading is printed at the top of each page. +@^output format@> @^page headings@> + +@<Output a page of answers@>= +begin print_string('The First '); +print_integer(m);@/ +print_string(' Prime Numbers --- Page '); +print_integer(page_number); +new_line; new_line; {there's a blank line after the heading} +for row_offset:=page_offset to page_offset+rr-1 do + @<Output a line of answers@>; +new_page; +end + +@ The first row will contain +$$\hbox{|p[1]|, |p[1+rr]|, |p[1+2*rr]|, \dots;}$$ +a similar pattern holds for each value of the |row_offset|. + +@<Output a line of answers@>= +begin for c:=0 to cc-1 do + if row_offset+c*rr<=m then print_entry(p[row_offset+c*rr]); +new_line; +end + +@* Generating the primes. +The remaining task is to fill table~|p| with the correct numbers. +Let us do this by generating its entries one at a time: Assuming that +we have computed all primes that are |j|~or less, we will advance |j| +to the next suitable value, and continue doing this until the +table is completely full. + +The program includes a provision to initialize the variables in certain +data structures that will be introduced later. + +@<Fill table |p|...@>= +@<Initialize the data structures@>; +while k<m do + begin @<Increase |j| until it is the next prime number@>; + k:=k+1; p[k]:=j; + end + +@ We need to declare the two variables |j| and~|k| that were just +introduced. + +@<Variables...@>= +@!j:integer; {all primes |<=j| are in table |p|} +@!k:0..m; {this many primes are in table |p|} + +@ So far we haven't needed to confront the issue of what a prime number +is. But everything else has been taken care of, so we must delve into +a bit of number theory now. + +By definition, a number is called prime if it is an integer greater +than~1 that is not evenly divisible by any smaller prime number. Stating +this another way, the integer |j>1| is not prime if and only if there +exists a prime number $p_n<j$ such that |j| is a multiple of~$p_n$. +@^prime number, definition of@> + +Therefore the section of the program that is called `\<Increase |j| until +it is the next prime number\>' could be coded very simply: +`\ignorespaces|repeat j:=j+1;|\unskip\ +\<Give to~|j_prime| the meaning: |j|~is a prime number\>; +\ignorespaces|until j_prime|\unskip'. +And to compute the boolean value |j_prime|, the following +would suffice: `\ignorespaces|j_prime:=true; for n:=1 to k do|\unskip\ +\<If |p[n]| divides |j|, set |j_prime:=false|\>'. + +@ However, it is possible to obtain a much more efficient algorithm by +using more facts of number theory. In the first place, we can speed +things up a bit by recognizing that $p_1=2$ and that all subsequent +primes are odd; therefore we can let |j| run through odd values only. +Our program now takes the following form: + +@<Increase |j| until...@>= +repeat j:=j+2; @<Update variables that depend on~|j|@>; +@<Give to |j_prime| the meaning: |j|~is a prime number@>; +until j_prime + +@ The |repeat| loop in the previous section introduces a boolean +variable |j_prime|, so that it will not be necessary to resort to +a |goto| statement. (We are following Dijkstra,$^\Dijk$ not Knuth.$^\goto$) +@^Dijkstra, Edsger@> @^Knuth, Donald E.@> + +@<Variables...@>= +@!j_prime:boolean; {is |j| a prime number?} + +@ In order to make the odd-even trick work, we must of course initialize +the variables |j|, |k|, and |p[1]| as follows. + +@<Init...@>= +j:=1; k:=1; p[1]:=2; + +@ Now we can apply more number theory in order to obtain further +economies. If |j| is not prime, its smallest prime factor $p_n$ will +be $\sqrt j$ or less. Thus if we know a number |ord| such that +$$p[|ord|]^2>j,$$ and if |j| is odd, we need only test for divisors +in the set $\{p[2], \ldots, p[|ord|-1]\}$. This is much faster than +testing divisibility by $\{p[2],\ldots,p[k]\}$, since |ord| tends +to be much smaller than~|k|. \ (Indeed, when |k| is large, the +celebrated ``prime number theorem'' implies that the value of |ord| +will be approximately $2\sqrt{k/\!\ln k}$.) + +Let us therefore introduce |ord| into the data structure. A moment's +thought makes it clear that |ord| changes in a simple way when |j| +increases, and that another variable |square| facilitates the +updating process. + +@<Variables...@>= +@!ord:2..ord_max; {the smallest index |>=2| such that $p_{ord}^2>j$} +@!square:integer; {$|square|=p_{ord}^2$} + +@ @<Init...@>= +ord:=2; square:=9; + +@ The value of |ord| will never get larger than a certain value +|ord_max|, which must be chosen sufficiently large. It turns out that +|ord| never exceeds~30 when |m=1000|. + +@<Other const...@>= +@!ord_max=30; {$p_{ord\_max}^2$ must exceed $p_m$} + +@ When |j| has been increased by~2, we must increase |ord| by unity +when $j=p_{ord}^2$, i.e., when |j=square|. + +@<Update variables that depend on~|j|@>= +if j=square then + begin ord:=ord+1; + @<Update variables that depend on~|ord|@>; + end + +@ At this point in the program, |ord| has just been increased by unity, +and we want to set $|square|:=p_{ord}^2$. A surprisingly subtle point +arises here: How do we know that $p_{ord}$ has already been computed, +i.e., that |ord<=k|? If there were a gap in the sequence of prime numbers, +such that $p_{k+1}>p_k^2$ for some~$k$, then this part of the program would +refer to the yet-uncomputed value |p[k+1]| unless some special test were +made. + +Fortunately, there are no such gaps. But no simple proof of this fact is +known. For example, Euclid's famous demonstration that there are +infinitely many prime numbers is strong enough to prove only that +$p_{k+1}<=p_1\ldots p_k+1$. Advanced books on number theory come to our +rescue by showing that much more is true; for example, ``Bertrand's +postulate'' @^Bertrand, Joseph, postulate@> states that $p_{k+1}<2p_k$ +for all~$k$. + +@<Update variables that depend on~|ord|@>= +square:=p[ord]*p[ord]; {at this point |ord<=k|} + +@* The inner loop. +Our remaining task is to determine whether or not a given integer~|j| is prime. +The general outline of this part of the program is quite simple, +using the value of |ord| as described above. + +@<Give to |j_prime|...@>= +n:=2; j_prime:=true; +while (n<ord) and j_prime do + begin @<If |p[n]| is a factor of~|j|, set |j_prime:=false|@>; + n:=n+1; + end + +@ @<Var...@>= +@!n:2..ord_max; {runs from 2 to |ord| when testing divisibility} + +@ Let's suppose that division is very slow or nonexistent on our +machine. We want to detect nonprime odd numbers, which are odd multiples +of the set of primes $\{p_2,\ldots,p_{ord}\}$. + +Since |ord_max| is small, it is reasonable to maintain an auxiliary table of +the smallest odd multiples that haven't already been used to show that +some~|j| is nonprime. In other words, our goal is to ``knock out'' all +of the odd multiples of each $p_n$ in the set $\{p_2,\ldots,p_{ord}\}$, +and one way to do this is to introduce an auxiliary table that serves as +a control structure for a set of knock-out procedures that are being +simulated in parallel. (The so-called ``sieve of Eratosthenes'' +@^Eratosthenes, sieve of@> generates primes by a similar method, but +it knocks out the multiples of each prime serially.) + +The auxiliary table suggested by these considerations is a |mult| +array that satisfies the following invariant condition: For |2<=n<ord|, +|mult[n]| is an odd multiple of $p_n$ such that $|mult|[n]<j+2p_n$. + +@<Var...@>= +@!mult:array[2..ord_max] of integer; {runs through multiples of primes} + +@ When |ord| has been increased, we need to initialize a new element of +the |mult| array. At this point $j=p[|ord|-1]^2$, so there is no +need for an elaborate computation. + +@<Update variables that depend on~|ord|@>= +mult[ord-1]:=j; + +@ The remaining task is straightforward, given the data structures +already prepared. Let us recapitulate the current situation: The +goal is to test whether or not |j|~is divisible by~$p_n$, without +actually performing a division. We know that $j$~is odd, and that +|mult[n]| is an odd multiple of~$p_n$ such that $|mult|[n]<j+2p_n$. +If |mult[n]<j|, we can increase |mult[n]| by $2p_n$ and the same +conditions will hold. On the other hand if |mult[n]>=j|, the +conditions imply that |j|~is divisible by~$p_n$ if and only if +|j=mult[n]|. + +@<If...@>= +while mult[n]<j do mult[n]:=mult[n]+p[n]+p[n]; +if mult[n]=j then j_prime:=false + +@* Index. +Every identifier used in this program is shown here together with a list +of the section numbers where that identifier appears. The section number +is underlined if the identifier was defined in that section. However, +one-letter identifiers are indexed only at their point of definition, +since such identifiers tend to appear almost everywhere. \[An index like +this is prepared automatically by the \WEB\ software, and it is appended +to the final section of the program. However, underlining of section +numbers is not automatic; the user is supposed to mark identifiers +at their point of definition in the \WEB\ source file.\] + +This index also refers to some of the places where key elements of the +program are treated. For example, the entries for `Output format' and +`Page headings' indicate where details of the output format are +discussed. Several other topics that appear in the documentation +(e.g., `Bertrand's postulate') have also been indexed. \[Special +instructions within a \WEB\ source file can be used to insert +essentially anything into the index.\] diff --git a/support/makeprog/docu/web.tex b/support/makeprog/docu/web.tex new file mode 100644 index 0000000000..aba26ddcb8 --- /dev/null +++ b/support/makeprog/docu/web.tex @@ -0,0 +1,1482 @@ +% Page layout +\input webmac +\output{\setbox0=\box255}\eject % get rid of spurious WEBMAC page + +\font\man=manfnt scaled \magstep3 +\font\CompJtitle=cmbx10 scaled\magstep4 +\font\CompJabstract=cmb10 +\font\tenssb=cmssdc10 +\font\tenss=cmss10 +\font\tenssi=cmssi10 +\font\eightss=cmss8 +\font\eightssi=cmssi8 +\font\eightssb=cmssbx10 scaled 800 +\font\eighttt=cmtt8 +\font\ninerm=cmr9 +\let\mc=\ninerm % medium caps for names like PASCAL + +\newdimen\pagewidth \newdimen\pageheight \newdimen\ruleht +\hsize=177mm \vsize=249mm +\parindent=1em % this is needed for WEB output +\pagewidth=\hsize \pageheight=\vsize \ruleht=1pt +\abovedisplayskip=11pt plus 3pt minus 8pt +\abovedisplayshortskip=0pt plus 3pt +\belowdisplayskip=11pt plus 3pt minus 8pt +\belowdisplayshortskip=6pt plus 3pt minus 3pt + +\newif\iftitle +\def\titlepage{\global\titletrue} % for pages without headlines + +\def\leftheadline{\hbox to \pagewidth{% + \vbox to 8pt{}\hss \eightrm D. E. KNUTH\hss}} +\def\rightheadline{\hbox to \pagewidth{% + \vbox to 8pt{}\hss \eightrm LITERATE PROGRAMMING\hss}} +\hoffset=-.25in \voffset=-.6in + +\newinsert\lefttop \newinsert\righttop +\count\lefttop=1000 \count\righttop=1000 +\dimen\lefttop=\maxdimen \dimen\righttop=\maxdimen +\skip\lefttop=25pt plus 3pt minus 3pt +\skip\righttop=\skip\lefttop +\def\leftfloat{\insert\lefttop\bgroup + \floatingpenalty=0 + \penalty0 + \vbox\bgroup} +\def\rightfloat{\insert\righttop\bgroup + \floatingpenalty=0 + \penalty0 + \vbox\bgroup} +\def\endfloat{\egroup\egroup} + +\def\onepageout#1{\shipout\vbox{ % here we define one page of output + \offinterlineskip % butt the boxes together + \vbox to 9mm{ % this part goes on top of the regular pages + \iftitle % the next is used for title pages + \global\titlefalse % reset the titlepage switch + \hbox to\pagewidth{\leaders\CJrule\hfill} + \else\ifodd\pageno \rightheadline\else\leftheadline\fi\fi + \vfill} % this completes the \vbox to 9mm + \vbox to \pageheight{ + #1 % now insert the main information + \boxmaxdepth=\maxdepth + } % this completes the \vbox to \pageheight + \baselineskip=7mm \lineskiplimit=0pt + \hbox to\pagewidth{% + \ifodd\pageno\hfil\tenss submitted to THE COMPUTER JOURNAL% + \tenssb\quad\folio + \else\tenssb\folio\quad + \tenss submitted to THE COMPUTER JOURNAL\hfil\fi} + } + \advancepageno} + +\output{\onepageout{\unvbox255}} + +\newbox\partialpage +\def\begindoublecolumns{\begingroup + \output={\global\setbox\partialpage=\vbox{\unvbox255}}\eject + \output={\doublecolumnout} \hsize=84mm \vsize=510mm} +\def\enddoublecolumns{\output={\balancecolumns}\eject + \endgroup \pagegoal=\vsize} + +\def\doublecolumnout{\dimen0=\pageheight + \advance\dimen0 by-\ht\partialpage \splittopskip=\topskip + \ifdim\ht\lefttop>0pt \setbox255=\vbox{\unvbox\lefttop + \setbox0=\lastbox\unvbox0\vskip\skip\lefttop\unvbox255}\fi + \setbox0=\vsplit255 to\dimen0 + \ifdim\ht\righttop>0pt \setbox255=\vbox{\unvbox\righttop + \setbox0=\lastbox\unvbox0\vskip\skip\righttop\unvbox255}\fi + \setbox2=\vsplit255 to\dimen0 + \onepageout\pagesofar + \unvbox255 \penalty\outputpenalty} +\def\pagesofar{\unvbox\partialpage + \wd0=\hsize \wd2=\hsize \hbox to\pagewidth{\box0\hfil\box2}} +\def\balancecolumns{\setbox0=\vbox{\unvbox255} \dimen0=\ht0 + \advance\dimen0 by\topskip \advance\dimen0 by-\baselineskip + \divide\dimen0 by2 \splittopskip=\topskip + {\vbadness=10000 \loop \global\setbox3=\copy0 + \global\setbox1=\vsplit3 to\dimen0 + \ifdim\ht3>\dimen0 \global\advance\dimen0 by1pt \repeat} + \setbox0=\vbox to\dimen0{\unvbox1} + \setbox2=\vbox to\dimen0{\unvbox3} + \pagesofar} + +\def\CJrule{\hrule height\ruleht} +\baselineskip=11pt +\parskip=0pt plus 1pt +\def\beginsection #1\par{\goodbreak\vskip9mm plus4mm minus 2mm + \vbox{\CJrule width \hsize \kern5pt} + \kern-3pt + \nointerlineskip + \leftline{\strut\bf#1} + \CJrule + \kern12pt\nobreak\noindent\ignorespaces} +\def\caption #1. #2.{\leftline{\def\TeX{T\kern-.2em\lower.5ex\hbox{E}X}% + \tenssb Figure #1.\enspace\tenss#2.}} + +\def\WEB{{\tt WEB}} +\def\PASCAL{{\mc PASCAL}} +\def\sec{{\tensy x}} +\def\<{$\langle\,$} +\def\>{$\,\rangle$} +\newbox\circlebox +\setbox\circlebox=\hbox{\man Y} +\def\encircle#1{\kern6pt\hbox to\wd\circlebox{\hss\tt#1\hss}\kern-\wd\circlebox + \raise10pt\copy\circlebox\kern6pt} + +\def\ttverbatim{\begingroup \tt \parindent=0pt \obeylines + \uncatcodespecials \catcode`/=0 \obeyspaces} +\let\endverbatim=\endgroup +{\obeyspaces\global\let =\ } % let active space = control space +\def\uncatcodespecials{\def\do##1{\catcode`##1=12 }\dospecials} +\def\cvdots{\kern3pt\qquad\smash\vdots} + +\newcount\refno \newif\ifshowit +\def\ref{\showittrue\makeref} +\def\silentref{\showitfalse\makeref} +\def\references{} % this will grow until it holds all the references +\def\makeref#1#2{\advance\refno by1 \edef#1{{\the\refno}}% + \toks0=\expandafter{\references}% + {\def\rm{\eightss}\def\sl{\eightssi}\def\bf{\eightssb}\def\tt{\eighttt}% + \def\TeX{T\kern-.2em\lower.5ex\hbox{E}\kern-.000em X}% + \xdef\references{\the\toks0 \noexpand\item{\the\refno.}#2\par}}% + \ifshowit\edef\next{\spacefactor=\the\spacefactor\space}% + $^{\the\refno}$\next\fi} +\hyphenation{Dijk-stra} +\hyphenchar\tentt=-1 % no hyphenation in the typewriter font + +\titlepage +\leftline{\kern13mm\CompJtitle Literate Programming} +\kern6mm +\CJrule +\kern4.5mm +\leftline{\kern13mm\bf Donald E. Knuth} +\kern2pt +\leftline{\kern13mm\eightrm Computer Science Department, Stanford University, + Stanford, CA 94305, USA} +\kern4mm +\CJrule +\kern6mm +\leftline{\kern13mm\vbox{\hsize=151mm\CompJabstract\noindent +The author and his associates have been experimenting for the past several +years with a programming language and documentation system called \WEB. +This paper presents \WEB\ by example, and discusses why the new +system appears to be an improvement over previous ones.}} +\bigskip\bigskip +\begindoublecolumns + +\beginsection A. INTRODUCTION + +The past ten years have witnessed substantial improvements in programming +methodology. This advance, carried out under the banner of ``structured +programming,'' has led to programs that are more reliable and easier to +comprehend; yet the results are not entirely satisfactory. My purpose +in the present paper is to propose another motto that may be appropriate +for the next decade, as we attempt to make further progress in the +state of the art. I believe that the time is ripe for significantly +better documentation of programs, and that we can best achieve this by +considering programs to be {\it works of literature}. Hence, my title: +``Literate Programming.'' + +Let us change our traditional attitude to the construction of programs: +Instead of imagining that our main task is to instruct a {\it computer\/} +what to do, let us concentrate rather on explaining to {\it human beings\/} +what we want a computer to do. + +The practitioner of literate programming can be regarded as an essayist, whose +main concern is with exposition and excellence of style. Such an author, +with thesaurus in hand, chooses the names of variables carefully and explains +what each variable means. He or she strives for a program that is +comprehensible because its concepts have been introduced in an order that +is best for human understanding, using a mixture of formal and informal +methods that re\"\i nforce each other. + +I dare to suggest that such advances in documentation are possible because +of the experiences I've had during the past several years while working +intensively on software development. By making use of several ideas that +have existed for a long time, and by applying them systematically in a +slightly new way, I've stumbled across a method of composing programs +that excites me very much. In fact, my enthusiasm is so great that I must warn +the reader to discount much of what I shall say as the ravings of a fanatic +who thinks he has just seen a great light. + +Programming is a very personal activity, so I can't be certain that what has +worked for me will work for everybody. Yet the impact of this new approach on +my own style has been profound, and my excitement has continued unabated +for more than two years. I~enjoy the new methodology so much that it is hard +for me to refrain from going back to every program that I've ever written +and recasting it in ``literate'' form. I~find myself unable to resist working +on programming tasks that I would ordinarily have assigned to student +research assistants; and why? Because it seems to me that at last I'm able +to write programs as they should be written. My programs are not only +explained better than ever before; they also are better programs, +because the new methodology encourages me to do a better job. For these +reasons I am compelled to write this paper, in hopes that my experiences +will prove to be relevant to others. + +I must confess that there may also be a bit of malice in my choice of +a title. During the 1970s I was coerced like everybody else into adopting +the ideas of structured programming, because I couldn't bear to be found +guilty of writing {\it unstructured\/} programs. Now I have a chance +to get even. By coining the phrase ``literate programming,'' I am imposing +a moral commitment on everyone who hears the term; surely nobody wants +to admit writing an {\it il{}literate\/} program. + +\beginsection B. THE \WEB\ SYSTEM + +I hope, however, to demonstrate in this paper that the title is not merely +wordplay. The ideas of literate programming have been embodied in a language +and a suite of computer programs that have been developed at Stanford +University during the past few years as part of my research on algorithms +and on digital typography. This language and its associated programs +have come to be known as the \WEB\ system. My goal in what follows is +to describe the philosophy that underlies \WEB, to present examples +of programs in the \WEB\ language, and to discuss what may be the future +implications of this work. + +I chose the name \WEB\ partly because it was one of the few three-letter +words of English that hadn't already been applied to computers. But as time +went on, I've become extremely pleased with the name, because I~think +that a complex piece of software is, indeed, best regarded as a {\it web\/} +that has been delicately pieced together from simple materials. We +understand a complicated system by understanding its simple parts, and by +understanding the simple relations between those parts and their immediate +neighbors. If we express a program as a web of ideas, we can emphasize +its structural properties in a natural and satisfying way. + +\WEB\ itself is chiefly a combination of two other languages: +(1)~a document formatting language and (2)~a programming language. +My prototype \WEB\ system uses \TeX\ as the document formatting +language and \PASCAL\ as the programming language, but the same +principles would apply equally well if other languages were +substituted. Instead of \TeX, one could use a language like Scribe +or Troff; instead of \PASCAL, one could use {\mc ADA}, {\mc ALGOL}, +{\mc LISP}, {\mc COBOL}, {\mc FORTRAN}, {\mc APL}, {\mc C}, etc., or +even assembly language. The main point is that \WEB\ is inherently +bilingual, and that such a combination of languages proves to be much +more powerful than either single language by itself. \WEB\ does not +make the other languages obsolete; on the contrary, it enhances them. + +I naturally chose \TeX\ to be the document formatting language, in the +first \WEB\ system, because \TeX\ is my own creation;\ref\TeXbook{D. E. Knuth, +{\sl The \TeX book}. Addison-Wesley, Reading, Mass., U.S.A. (1983).} +I wanted to acquire a lot of experience in harnessing \TeX\ to a variety +of different tasks. I~chose \PASCAL\ as the programming language because +it has received such widespread support from educational institutions all +over the world; it is not my favorite language for system programming, but +it has become a ``second language'' for so many programmers that it +provides an exceptionally effective medium of communication. Furthermore +\WEB\ itself has a macro-processing ability that makes \PASCAL's +limitations largely irrelevant. + +Document formatting languages are newcomers to the computing scene, but +their use is spreading rapidly. Therefore I'm confident that we will be +able to expect each member of the next generation of programmers to be +familiar with a document language as well as a programming language, +as part of their basic education. Once a person knows both of the +underlying languages, there's no trick at all to learning \WEB, because +the \WEB\ user's manual is fewer than ten pages long. + +A \WEB\ user writes a program that serves +as the source language for two different system routines. (See Figure~1.) +One line of processing is called {\it weaving\/} the web; it produces +a document that describes the program clearly and that facilitates program +maintenance. The other line of processing is called {\it tangling\/} the +web; it produces a machine-executable program. The program and its +documentation are both generated from the same source, so they are +consistent with each other. + +\bigskip +\centerline{\vbox{ + \halign{&\hss#\hss\cr + &&&\TeX\cr + \noalign{\vskip-4pt} + &&\encircle{TEX}&\enspace\rightarrowfill\enspace&\encircle{DVI}\cr + \multispan2\hfil\smash{\raise4pt\hbox{\tt WEAVE}\kern-1pt}$\nearrow$ \cr + \noalign{\vskip6pt} + \encircle{WEB}\cr + \noalign{\vskip6pt} + \multispan2\hfil\smash{\lower6pt\hbox{\tt TANGLE}\kern-1pt}$\searrow$ \cr + &&\encircle{PAS}&\enspace\rightarrowfill\enspace&\encircle{REL}\cr + \noalign{\vskip-2pt} + &&&\mc\ PASCAL\ \cr} + }} +\nobreak\medskip +\caption 1. Dual usage of a {\tt WEB} file. +\bigbreak + +Let's look at this process in slightly more detail. Suppose you have +written a \WEB\ program and put it into a computer text file called +{\tt COB.WEB} (say). To generate hardcopy documentation for your program, +you can run the {\tt WEAVE} processor; this is a system program that takes +the file {\tt COB.WEB} as input and produces another file {\tt COB.TEX} +as output. Then you run the \TeX\ processor, which takes {\tt COB.TEX} +as input and produces {\tt COB.DVI} as output. The latter file, +{\tt COB.DVI}, is a ``device-in\-de\-pen\-dent'' binary description of how +to typeset the documentation, so you can get printed output by applying +one more system routine to this file. + +You can also follow the other branch of Figure~1, by running the +{\tt TANGLE} processor; this is a system program that takes the file +{\tt COB.WEB} as input and produces a new file {\tt COB.PAS} as output. +Then you run the \PASCAL\ compiler, which converts {\tt COB.PAS} to +a binary file {\tt COB.REL} (say). Finally, you can run your program +by loading and executing {\tt COB.REL}. The process of ``compile, load, +and go'' has been slightly lengthened to ``tangle, compile, load, and go.'' + +\beginsection C. A COMPLETE EXAMPLE + +Now it's time for me to stop presenting general platitudes and to move on +to something tangible. Let us look at a real program that has been written +in \WEB. The numbered paragraphs that follow are the actual output of a +\WEB\ file that has been ``woven'' into a document; a computer has also +generated the indexes that appear at the program's end. If my claims for +the advantages of literate programming have any merit, you should be able +to understand the following description more easily than you could +have understood the same program when presented in a more conventional +way. However, I am trying here to explain the format of \WEB\ documentation +at the same time as I am discussing the details of a nontrivial algorithm, +so the description below is slightly longer than it would be if it were +written for people who already have been introduced to \WEB. + +\silentref\Dijk{O.-J.~Dahl, E.~W. Dijkstra, and C.~A.~R. Hoare, +{\sl Structured Programming}. Academic Press, London and New York (1972).} +\silentref\goto{D. E. Knuth, Structured programming with {\bf go to} +statements. {\sl Computing Surveys\/ \bf6}, 261--301 (1974).} + +Here, then, is the computer-generated output: + +\bigskip +\CJrule +\medskip +\begingroup +\def\prune\input webmac{\input primes.contents} +\def\Z#1#2#3{\line{\ignorespaces#1\ \dotfill\ {\tensy x}#2}} +\def\M#1.{\MN#1.\iftrue\medbreak\startsection\ignorespaces} +\def\firstmod{1} +\def\N#1.#2.{\MN#1.\iftrue\nobreak + \ifx\modno\firstmod\medskip\else\bigskip\fi + \CJrule\medbreak\startsection + {\bf\ignorespaces#2.\quad}\ignorespaces} +\def\inx{\par\medbreak + \def\:##1, {\par\hangindent2em\noindent##1:\kern1em} + \def\[##1]{$\underline{##1}$} + \rm \rightskip0pt plus2.5em \tolerance10000 \let\*=\lapstar + \hyphenpenalty10000 \parindent0pt} +\def\fin{\par\bigskip\CJrule\medbreak + \parfillskip0pt plus1fil + \def\note##1##2.{\hfil\penalty-1\hfilneg\quad{\eightrm##1 ##2.}} + \def\U{\note{Used in}} + \def\:{\par\hangindent 2em}\let\*=*} +\let\con=\par +\parskip=0pt +\expandafter\prune\input primes +\endgroup + +\beginsection D. HOW THE EXAMPLE WAS SPECIFIED + +Everything reproduced above, from the table of contents preceding the +program to the indexes of identifiers and section names at the end, +was generated by applying the program {\tt WEAVE} to a source file +{\tt PRIMES.WEB} written in the \WEB\ language. Let us now look at that +file {\tt PRIMES.WEB}, in order to get an idea of what a \WEB\ user +actually types. + +There's no need to show very much of {\tt PRIMES.WEB}, however, because +that file is reflected quite faithfully by the formatted output. Figure~2 +contains enough of the \WEB\ source to indicate the general flavor; +a reader who is familiar with the rudiments of \TeX\ will be able to +reconstruct all of {\tt PRIMES.WEB} by looking only at the formatted +output and Figure~2. + +\leftfloat +\ttverbatim +/hrule +/medskip +\font\ninerm=cmr9 +\let\mc=\ninerm % medium caps +\def\WEB{{\tt WEB}} +\def\PASCAL{{\mc PASCAL}} +\def\[{\ifhmode\ \fi$[\mkern-2mu[$} +\def\]{$]\mkern-2mu]$\ } +/cvdots +\hyphenation{Dijk-stra} +/medskip +@* Printing primes: An example of \WEB. +The following program is essentially the same +as Edsger Dijkstra's @^Dijkstra, Edsger@> +``first example of step-wise program +composition,'' found on pages 26--39 +of his {\sl Notes on Structured +Programming},$^\Dijk$ but it has been +translated into the \WEB\ language. @.WEB@> +/medskip +\[Double brackets will be used in what +follows to enclose comments relating to \WEB\ +/cvdots +an informal top-level description.\] +/medskip +@p @<Program to print the first thousand +prime numbers@> +/endverbatim +\medskip +\caption 2a. The beginning of {\tt PRIMES.WEB}. +\medskip +\hrule +\endfloat + +Figure 2a starts with \TeX\ commands (not shown in full) that make it +convenient to typeset double brackets $[\mkern-2mu[\ldots]\mkern-2mu]$ +and to give special typographic treatment to names like `\WEB' and `\PASCAL'. +A \WEB\ user generally begins by declaring such special aspects of the +document format; for example, if nonstandard fonts of type are needed, +they are usually stated first. It may also be necessary to specify the +correct hyphenation of non-English words that appear in the document. + +Then comes `{\tt@*}', which starts the program proper. \WEB\ uses the +symbol `{\tt@}' as an escape character for special instructions to the +{\tt WEAVE} and {\tt TANGLE} processors. Everything between such special +commands is either expressed in \TeX\ language or in \PASCAL\ language, +depending on the context. + +Each section of the program begins either with `{\tt@ }' (i.e., at-sign +and space) or `{\tt@*}' (i.e., at-sign and asterisk); \WEB\ supplies the +section numbers automatically. The latter case, `{\tt@*}', denotes a +{\it major section\/} of the program, for which a special title is given. +This title will appear in boldface type, and it will also appear in the +table of contents, and as a running headline on all pages of the +woven documentation until another major section begins. Each major section +starts at the top of a page. (Such page beginnings have been indicated +by horizontal lines in our example, because \WEB's normal output format +has been adapted to the format of this journal. The output of {\tt WEAVE} +usually has a lot more white space, and the individual lines of text +are usually quite a bit wider.) + +The lines that follow in Figure~2a show a few more \WEB\ instructions: +`{\tt@\char`^}' marks the beginning of an index entry to be set in roman +type; `{\tt@>}' marks the end of an argument to a \WEB\ command; +`{\tt@.}'\ marks the beginning of an index entry to be set in typewriter +type; `{\tt@p}' marks the beginning of the \PASCAL\ program; and `{\tt@<}' +marks the beginning of a top-level description, i.e., of a section +name in the \WEB\ program. + +\rightfloat +\ttverbatim +/hrule +/medskip +@ This program has no input, because we want +to keep it rather simple. The result of the +program will be to produce a list of the +first thousand prime numbers, and this list +will appear on the |output| file. +/medskip +Since there is no input, we declare the value +|m=1000| as a compile-time constant. The +program itself is capable of generating the +first |m| prime numbers for any positive |m|, +as long as the computer's finite limitations +are not exceeded. +/medskip +\[The program text below specifies the +``expanded meaning'' of `\X2:Program to print +$\ldots$ numbers\X'; notice that it involves +the top-level descriptions of three other +sections. When those top-level descriptions +are replaced by their expanded meanings, a +syntactically correct \PASCAL\ program will +be obtained.\] +/medskip +@<Program to print...@>= +program print_primes(output); +const @!m=1000; +@<Other constants of the program@>@; +var @<Variables of the program@>@; +begin @<Print the first |m| prime numbers@>; +end. +/endverbatim +\medskip +\caption 2b. The \WEB\ code that generated \sec2. +\ttverbatim +/bigskip +/hrule +/medskip +@ In order to keep this program reasonably +free of notations that are uniquely +\PASCAL esque, \[and in order to illustrate +/cvdots +The first three macro definitions here are +parametric; the other two are simple.\] +/medskip +@d print_string(#)==write(#) + {put a given string into the |output| file} +@d print_integer(#)==write(#:1) + {put a given integer into the |output| + file, in decimal notation, using only as + many digit positions as necessary} +@d print_entry(#)==write(#:ww) + {like |print_integer|, but + |ww| character positions are filled, + inserting blanks at the left} +@d new_line==write_ln + {advance to a new line in the |output| file} +@d new_page==page + {advance to a new page in the |output| file} +/endverbatim +\medskip +\caption 2c. The \WEB\ code that generated \sec6. +\medskip +\hrule +\endfloat + +Figure 2b immediately follows Figure~2a in the \WEB\ file. This material +is what generated \sec2 of the documentation, and it illustrates the +bilingual nature of \WEB: The commentary at the beginning of each section +is typed in \TeX\ language, and the program text at the end is typed +in \PASCAL\ language. + +Language-switching between \TeX\ and \PASCAL\ is occasionally desirable. +For example, when you refer to technical details about the program, you +usually want to describe them in \PASCAL, hence you want {\tt WEAVE} to format +them with the typographic conventions it uses for \PASCAL\ programs. +Conversely, when you put comments in a \PASCAL\ program, you want +the text of those comments to be formatted by \TeX\ in the normal way. +\WEB\ files use vertical bars to introduce \PASCAL\ formatting in the +midst of \TeX\ formatting; for example, Figure~2b says `{\tt the +|output| file}' in order to typeset `the \\{output} file'. + +The program text in Figure~2b begins with `{\tt@<}' instead of with the +`{\tt@p}' command used in Figure~2a, because the program text in~\sec2 +is the expansion of a specific top-level description. Notice that the +top-level description has been abbreviated to `{\tt@<Program to print...@>}'. +Since the names of sections tend to be rather long, it is a nuisance to +type them in full each time; \WEB\ allows you to type `{\tt...}'\ after you +have given enough text to identify the remainder uniquely. + +The `{\tt@!}'\ operation in the program text of Figure~2b governs the +underlining of index entries. The `{\tt@;}'\ specifies an invisible symbol +that has the effect of a semicolon in \PASCAL\ syntax. Commands such as these +are comparatively unimportant, but they are available for polishing up +the final documentation when you want to maintain fine control. + +Figure 2c shows key portions of the \WEB\ text that generated \sec6. +Notice that the command `{\tt@d}' introduces a macro definition. +All features of \WEB\ that appear in our example program are illustrated +in Figures 2a, 2b, and~2c; the remainder of {\tt PRIMES.WEB} simply +uses the same conventions again and again. In fact, most of the \WEB\ +file is much simpler than the examples shown here; Figure~2 has +illustrated only the difficult parts. + +\beginsection E. THE TANGLED OUTPUT + +Figure 3 shows the \PASCAL\ program {\tt PRIMES.PAS} that results when +{\tt TANGLE} is applied to {\tt PRIMES.WEB}. This program is not intended +for human consumption---it's only supposed to be readable by a \PASCAL\ +compiler---so {\tt TANGLE} does not go to great pains to produce a +beautiful format. Notice that underlines have been removed from the +identifier names, and that all of the letters have been converted to +uppercase (except in strings); {\tt TANGLE} tries to produce a format +that will be acceptable to a standard \PASCAL\ compiler. + +{\tt TANGLE} removes all of the commentary in the \WEB\ file, but it +inserts new comments of its own. If for some reason you need +to correlate the tangled \PASCAL\ code with the woven documentation, +you can find the program text for, say, \sec8 by looking between +the comments `{\tt\char`\{8:\char`\}}' and `{\tt\char`\{:8\char`\}}'. + +A comparison of Figure~3 to Figure~2 should make it clear why the +{\tt TANGLE} processor has acquired its name. + +\rightfloat +\ttverbatim +/hrule +/medskip +{1:}{2:}PROGRAM PRINTPRIMES(OUTPUT); +CONST M=1000;{5:}RR=50;CC=4;WW=10;{:5}{19:} +ORDMAX=30;{:19}VAR{4:} +P:ARRAY[1..M]OF INTEGER;{:4}{7:} +PAGENUMBER:INTEGER;PAGEOFFSET:INTEGER; +ROWOFFSET:INTEGER;C:0..CC;{:7}{12:}J:INTEGER; +K:0..M;{:12}{15:}JPRIME:BOOLEAN;{:15}{17:} +ORD:2..ORDMAX;SQUARE:INTEGER;{:17}{23:} +N:2..ORDMAX;{:23}{24:} +MULT:ARRAY[2..ORDMAX]OF INTEGER;{:24} +BEGIN{3:}{11:}{16:}J:=1;K:=1;P[1]:=2;{:16} +{18:}ORD:=2;SQUARE:=9;{:18}; +WHILE K<M DO BEGIN{14:}REPEAT J:=J+2;{20:} +IF J=SQUARE THEN BEGIN ORD:=ORD+1;{21:} +SQUARE:=P[ORD]*P[ORD];{:21}{25:} +MULT[ORD-1]:=J;{:25};END{:20};{22:}N:=2; +JPRIME:=TRUE; +WHILE(N<ORD)AND JPRIME DO BEGIN{26:} +WHILE MULT[N]<J DO MULT[N]:=MULT[N]+P[N]+P[N] +;IF MULT[N]=J THEN JPRIME:=FALSE{:26};N:=N+1; +END{:22};UNTIL JPRIME{:14};K:=K+1;P[K]:=J; +END{:11};{8:}BEGIN PAGENUMBER:=1; +PAGEOFFSET:=1; +WHILE PAGEOFFSET<=M DO BEGIN{9:} +BEGIN WRITE('The First ');WRITE(M:1); +WRITE(' Prime Numbers --- Page '); +WRITE(PAGENUMBER:1);WRITELN;WRITELN; +FOR ROWOFFSET:=PAGEOFFSET TO PAGEOFFSET+RR-1 +DO{10:} +BEGIN FOR C:=0 TO CC-1 DO IF ROWOFFSET+C*RR<= +M THEN WRITE(P[ROWOFFSET+C*RR]:WW);WRITELN; +END{:10};PAGE;END{:9}; +PAGENUMBER:=PAGENUMBER+1; +PAGEOFFSET:=PAGEOFFSET+RR*CC;END;END{:8}{:3}; +END.{:2}{:1} +/endverbatim +\medskip +\caption 3. PASCAL program generated from the \WEB\ file. +\medskip +\hrule +\endfloat + +\beginsection F. THE WOVEN OUTPUT + +I mentioned earlier that {\tt WEAVE} is a program that converts a file +like {\tt PRIMES.WEB} into a file {\tt PRIMES.TEX} that is a syntactically +correct source file for \TeX. Figure~4 gives a sampling of {\tt PRIMES.TEX}, +which is even more unreadable than {\tt PRIMES.PAS}. The instructions that +cause \TeX\ to produce formatted \PASCAL\ programs, with appropriate +typefaces and indentation, etc., are somewhat complex because they are +supposed to give decent results regardless of the page size. + +There is no need to discuss Figure~4 further in the present paper, because +the details of ``pretty printing'' are not relevant to my main theme. +I have shown this much of {\tt PRIMES.TEX} only to make the point that +it is nice to have a program like {\tt WEAVE} to do all the formatting; +computer programs are not easy to typeset. + +\leftfloat +\ttverbatim +/hrule +/medskip +\input webmac +\font\ninerm=amr9 +/cvdots +syntactically correct \PASCAL\ program will +be obtained.\] +/medskip +\Y\P$\4\X2:Program to print the first +thousand prime numbers\X\S$\6 +\4\&{program}\1\ \37$\\{print\_primes}(% +\\{output})$;\6 +\4\&{const} \37$\|m=1000$;\5 +\X5:Other constants of the program\X\6 +\4\&{var} \37\X4:Variables of the program\X\6 +\&{begin} \37\X3:Print the first \|m prime +numbers\X;\6 +\&{end}.\par +\U section~1.\fi +/cvdots +The first three macro definitions here are +parametric; the other two are simple.\] +/medskip +\Y\P\D \37$\\{print\_string}(\#)\S\\{write}(% +\#)$\C{put a given string into the % +\\{output} file}\par +/cvdots +\inx +\:{Bertrand, Joseph, postulate}, 21. +\:\\{boolean}, 15. +/cvdots +\:\.{WEB}, 1. +\:\\{write}, 6. +\:\\{write\_ln}, 6. +\:\\{ww}, \[5], 6. +\fin +/cvdots +\:\X4, 7, 12, 15, 17, 23, 24:Variables of +the program\X +\U section~2. +\con +/endverbatim +\medskip +\caption 4. \TeX\ program generated from the \WEB\ file. +\medskip +\hrule +\endfloat + +\beginsection G. ADDITIONAL BELLS AND WHISTLES + +A system like \WEB\ can be successful only if it is capable of handling +large programs as well as small ones, and only if it is complete enough +to take care of all the practical requirements that arise when many +different kinds of programs are considered. A small example like +{\tt PRIMES.WEB} is a satisfactory vehicle for illustrating the general +ideas, but it cannot be convincing as a demonstration of \WEB's ability +to produce quality software in the ``real world.'' My original design +of \WEB\ in September, 1981, was followed by a year of extensive +experiments, so that by the time Version~1 was released in +September, 1982, I could be fairly confident that the language was +reasonably complete. Since then only one or two small extensions +have proved to be necessary; and although numerous enhancements can +easily be imagined, I believe that a useful stopping point for a +working system called {\tt WEB83} has been reached. + +A full description of {\tt WEB83} appears in a Stanford report,\ref\WEBman% +{D. E. Knuth, {\sl The \WEB\kern-2pt\ System of Structured Documentation}. +Stanford Computer Science Report CS980 (September 1983).} +which also contains the complete \WEB\ programs for {\tt WEAVE} and +{\tt TANGLE}. The full language contains only a few features that +do not show up in the {\tt PRIMES} example considered above: + +\def\nindent#1{\noindent\hbox to\parindent{#1)\hfil}\ignorespaces} +\smallskip +\nindent1 There are facilities to override {\tt WEAVE}'s +automatic formatting of \PASCAL\ programs. For example, it is +possible to force a statement to begin on a new line, or to force +several statements to appear on the same line, or to suggest +a desirable breakpoint in the middle of a long expression. In +unusual cases, {\tt WEAVE} must parse program fragments that +are not syntactically complete---for example, there may be a +{\bf begin} without a matching {\bf end}---so a \WEB\ user must +be given a chance to control the results. Furthermore there is a +facility for changing {\tt WEAVE}'s formatting rules by declaring +that a certain identifier should be treated as a certain \PASCAL\ +reserved word, or by declaring that a certain reserved word +should be treated as an ordinary identifier. + +\smallskip +\nindent2 There is a way to force {\tt TANGLE} to omit a +space between two adjacent pieces of text, so that a name +like `\\{x3}' can be manufactured from `\|x' and `\\3'. Similarly, +there is a way to pass an arbitrary sequence of characters through +{\tt TANGLE} so that the same sequence will appear ``verbatim'' in +the \PASCAL\ file; and there is a way to force beginning-of-line +in that file. The latter extensions have proved to be necessary +to deal with various nonstandard conventions of different \PASCAL\ compilers. +When a comment in braces is sent to the \PASCAL\ file, {\tt TANGLE} +is careful not to introduce further braces inside the comment. + +\smallskip +\nindent3 There are facilities for octal and hexadecimal constants in \WEB\ +thees. {\tt TANGLE} converts such constants to decimal form; {\tt WEAVE} gives +them an appropriate typographic treatment. + +\smallskip +\nindent4 There is a facility for dealing with alphabetic constants. +When a program contains a double-quoted character like {\tt"A"}, +{\tt TANGLE} converts this to an integer between 0 and~127 that +equals the corresponding {\mc ASCII} code (in this case 65). +The use of {\mc ASCII} code facilitates the construction of software +that is readily portable from one machine to another, independent of +the actual character set in use. + +\smallskip +\nindent5 Furthermore, if a double-quoted constant is a string +of several characters, like {\tt"cat"}, {\tt TANGLE} converts it +into a unique integer that is 128 or more. A special {\it string pool +file\/} is written, containing all of the strings that have been +specially encoded in this way. I have used this general mechanism only +in large programs, but experience has shown that it makes quite a +nice substitute for the string-processing capabilities that \PASCAL\ +lacks. (Incidentally, I noticed after several months that a program +needs to have some indication that the string-pool file it is reading +contains the same strings that {\tt TANGLE} generated when the program +itself was tangled. Therefore a ``check sum'' is included in the +string pool file; each program is able to refer to its own check sum +and to compare it with the value in the file. This check-sum extension +was one of the last features to be added to \WEB.) + +\smallskip +\nindent6 The {\tt PRIMES} example illustrates macros with +parameters and macros without parameters. \WEB\ also allows ``numeric'' +macros, which are small integer constants; {\tt TANGLE} is capable of +doing simple arithmetic on such constants. This feature of \WEB\ was +introduced specifically to overcome \PASCAL's unfortunate inability to +do compile-time arithmetic. For example, it is impossible to have a +\PASCAL\ array whose bounds are `$0\to n-1$', or to write +`$20+3:$' as the label of one of the cases in `{\bf case} $x+y$'; +\WEB's numeric macros make it possible for {\tt TANGLE} to +preprocess such constants. + +\beginsection H. OCCAM'S RAZOR + +I would also like to mention several things that were intentionally left +out of \WEB, since I have tried to keep the language as simple as I could. + +There are no ``conditional macros,'' nor does {\tt TANGLE} +evaluate Boolean expressions that might influence the +output. I~found that everything I needed could be done +satisfactorily by commenting out the optional code. + +For example, a system program is often designed to gather statistics about +its own operation, but such statistics-gathering is pointless unless someone +is actually going to use the results. In order to make the instrumentation +code optional, I include the word `{\bf stat}' just before any special +code for statistics, and `{\bf tats}' just after such code; and I tell +{\tt WEAVE} to regard {\bf stat} and {\bf tats} as if they were {\bf begin} +and {\bf end}. But {\bf stat} and {\bf tats} are actually simple macros. +When I do want to gather the statistics, I define {\bf stat} and {\bf tats} to +be null; but in a production version of the software, I make {\bf stat} +expand to~`{\tt@\char`\{}' and {\bf tats} expand to~`{\tt@\char`\}}', +where {\tt@\char`\{} and {\tt@\char`\}} are special braces that {\tt TANGLE} +does not remove. Thus the optional code appears as a harmless comment in +the \PASCAL\ program. + +\WEB's macros are allowed to have at most one parameter. Again, I did this +in the interests of simplicity, because I noticed that most applications +of multiple parameters could in fact be reduced to the one-parameter case. +For example, suppose that you want to define something like +$$\hbox{\tt mac(\#1,\#2) == m[\#1*r+\#2]}$$ +which \WEB\ doesn't permit. You can get essentially the same result +with two one-parameter macros +$$\vbox{\halign{\tt#\hfil\cr +mac\char`\_tail(\#) == \#]\cr +mac(\#) == m[\#*r+mac\char`\_tail\cr}}$$ +since, e.g., `{\tt mac(a)(b)}' will expand into `{\tt m[a*r+b]}'. + +Here is another example that indicates some of the surprising generality +of one-parameter macros: Consider the two definitions +$$\vbox{\halign{\tt#\hfil\cr +define two\char`\_cases(\#)==case j of\cr +\ \ \ \ \ \ \ \ \ \ \ \ \ 1:\#(1); 2:\#(2); end\cr +define reset\char`\_file(\#)==reset(file@\&\#)\cr}}$$ +where `{\tt@\char`\&}' in the second definition is the concatenation operation +that pastes two texts together. You can now say +$$\hbox{\tt two\char`\_cases(reset\char`\_file)}$$ +and the resulting \PASCAL\ output will be +$$\vbox{\halign{\tt#\hfil\cr +case j of\cr +1:reset(file1);\cr +2:reset(file2);\cr +end\cr}}$$ +In other words, the name of one macro can usefully be a parameter to +another macro. This particular trick makes it possible to live with +\PASCAL\ compilers that do not allow arrays of files. + +\beginsection I. PORTABILITY + +One of the goals of my \TeX\ research has been to produce portable +software, and the {\tt WEB} system has been extremely helpful in this +respect. Although my own work is done on a DEC-10 computer with +Stanford's one-of-a-kind operating system, the software developed +with \WEB\ has already been transported successfully to a wide +variety of computers made by other manufacturers (including IBM, +Control Data, XEROX, Hewlett-Packard), and to a variety +of different operating systems for those machines. To my knowledge, +no other software of such complexity has ever been transported to +so many different machines. It seems likely that \TeX\ will soon be +operating on all but the smallest of the world's computer systems. + +To my surprise, the main bottleneck to portability of the \TeX ware +has been the lack of suitable \PASCAL\ compilers, because \PASCAL\ has +often been implemented without system programming in mind. Anybody +who has a decent \PASCAL\ compiler can install \WEB\ (and all programs +written in \WEB) without great difficulty, essentially as follows: + +\smallskip +\item{1)} Start with the three files {\tt WEAVE.WEB}, {\tt TANGLE.WEB}, and +{\tt TANGLE.PAS}. (The programs have not been copyrighted, so +these files are not difficult to obtain.) + +\item{2)} Run {\tt TANGLE.PAS} through your \PASCAL\ compiler to +get a working {\tt TANGLE} program. + +\item{3)} Check your {\tt TANGLE} by applying it to {\tt TANGLE.WEB}; +your output file should match {\tt TANGLE.PAS}. + +\item{4)} Apply your {\tt TANGLE} to the file {\tt WEAVE.WEB}, obtaining +{\tt WEAVE.PAS}; then apply \PASCAL\ to {\tt WEAVE.PAS} and you'll +have a working {\tt WEAVE} system. + +\item{5)} The same process applies to any software written in \WEB, +notably to \TeX\ itself. (However, you need fonts and suitable output +equipment in order to make proper use of \TeX; that may be another +bottleneck.) Once you have \TeX\ working, you can apply {\tt WEAVE} +and \TeX\ to your \WEB\ files, thereby getting program documents +as illustrated above. + +\smallskip\noindent +Notice that a {\tt TANGLE.PAS} file is needed in order to get this +``bootstrapping'' process started. If you have just {\tt WEAVE.WEB} +and {\tt TANGLE.WEB}, you can't do the first step. + +However, anybody who has looked seriously into the question of software +portability will realize that my comments in the preceding paragraphs +have been oversimplified. I have glossed over some serious +problems that arise: Character sets are different; file naming conventions +are different; special conventions are needed to interact with a user's +terminal; data is packed differently on different machines; floating-point +arithmetic is always nonstandard and sometimes nonexistent; users want +``friendly'' interaction with existing programs for editing and spooling; +etc., etc. Furthermore, many of the world's \PASCAL\ compilers are incredibly +bizarre. Therefore it is quite na\"\i ve to believe that a single program +{\tt TANGLE.PAS} could actually work on very many different machines, or +even that one single source file {\tt TANGLE.WEB} could be adequate; some +system-dependent\kern-.5pt\kern.5pt\ changes are inevitable. + +The \WEB\ system caters to system-dependent changes in a simple but surprisingly +effective way that I neglected to mention when I listed its other features. +Both {\tt TANGLE} and {\tt WEAVE} are designed to work with +{\it two\/} input files, not just one: In addition to a \WEB\ source file +like {\tt TEX.WEB}, there is also a ``change file'' {\tt TEX.CH} that +contains whatever changes are needed to customize \TeX\ for a particular +system. (Similarly, the source files {\tt WEAVE.WEB} and {\tt TANGLE.WEB} +are accompanied by {\tt WEAVE.CH} and {\tt TANGLE.CH}.) + +Here's how change files work: Each change has the form ``replace +$x_1\ldots x_m$ by $y_1\ldots y_n$,'' for some $m\ge 1$ and $n\ge0$; +here $x_i$ and~$y_j$ represent lines in the change file. +The {\tt WEAVE} and {\tt TANGLE} programs +read data from the \WEB\ input file until finding a line that matches +$x_1$; this line, and the $m-1$ following lines, are replaced by +$y_1\ldots y_n$. An error message is given if the $m$ lines replaced did +not match $x_1\ldots x_m$ perfectly. + +For example, the program {\tt PRIMES.WEB} invokes a \\{page} procedure to +begin a new page; but \\{page} was not pres\-ent in Wirth's original \PASCAL\ +and it is defined rather vaguely in the \PASCAL\ standard. Therefore +a system-dependent change may be needed here. A change file {\tt +PRIMES.CH} could be made by copying the line +$$\hbox{\tt @d new\char`\_page==page}$$ from +Figure~2c and specifying one or more appropriate replacement lines. + +The program {\tt TANGLE} itself contains about 190 sections, and a +typical installation will have to change about 15 of these. If you +want to transport {\tt TANGLE} to a new environment, you therefore +need to create a suitable file {\tt TANGLE.CH} that modifies 15~or~so parts +of {\tt TANGLE.WEB}. (Examples of {\tt TANGLE.CH} are provided to +all people who receive {\tt TANGLE.WEB}, so that each implementor has +a model of what to do.) You need to insert your changes by hand into +{\tt TANGLE.PAS}, until you have a {\tt TANGLE} program that works +sufficiently well to support further bootstrapping. But you never +actually change the master file {\tt TANGLE.WEB}. + +This approach has two important advantages. First, the same +master file {\tt TANGLE.WEB} is used by everybody, and it +contains the basic logic of {\tt TANGLE} that really defines the +essence of tangling. The system-dependent changes do not affect +any of the subtle parts of {\tt TANGLE}'s control structures or +data structures. Second, when the official {\tt TANGLE} has been +upgraded to a newer version, a brand new {\tt TANGLE.WEB} will +almost always work with the old {\tt TANGLE.CH}, since changes +are rarely made to the system-dependent parts. In other words, +this dual-input-file scheme works when the \WEB\ file is constant +and the {\tt CH} file is modified, and it also works when the +{\tt CH} file is constant but the \WEB\ file is modified. + +Change files were added to \WEB\ about three months after the system was +initially designed, based on our initial experiences with people who had +volunteered to participate in portability experiments. We realized +about a year later that {\tt WEAVE} could be modified so that +only the changed parts of a program would (optionally) be printed; thus, +it's now possible to document the changes by listing only the sections +that are actually affected by the {\tt CH} file that {\tt WEAVE} has +processed. We also generalized the original format of {\tt CH} files, +which permitted only changes that extended to the end of a section. These +two important ideas were among the final enhancements incorporated into +{\tt WEB83}. + +\beginsection J. PROGRAMS AS WEBS + +When I first began to work with the ideas that eventually became the +\WEB\ system, I thought that I would be designing a language for ``top-down'' +programming, where a top-level description is given first and successively +refined. On the other hand I knew that I often created major parts +of programs in a ``bottom-up'' fashion, starting with the definitions of +basic procedures and data structures and gradually building more and +more powerful subroutines. I had the feeling that top-down and bottom-up +were opposing methodologies: one more suitable for program exposition +and the other more suitable for program creation. + +But after gaining experience with \WEB, I have come to realize that there is +no need to choose once and for all between top-down and bottom-up, because +a program is best thought of as a web instead of a tree. A hierarchical +structure is present, but the most important thing about a program is +its structural relationships. A complex piece of software consists of +simple parts and simple relations between those parts; the programmer's +task is to state those parts and those relationships, in whatever order +is best for human comprehension---not in some rigidly determined +order like top-down or bottom-up. + +When I'm writing a longish program like {\tt TANGLE.WEB} or {\tt WEAVE.WEB} +or {\tt TEX.WEB}, I invariably have strong feelings about what part of the +whole should be tackled next. For example, I'll come to a point where I need +to define a major data structure and its conventions, before I'll feel +happy about going further. My experiences have led me to believe that a +person reading a program is, likewise, ready to comprehend it by +learning its various parts in approximately the order in which it +was written. The {\tt PRIMES.WEB} example illustrates this principle on +a small scale; the decisions that Dijkstra made as he composed the original +program$^\Dijk$ appear in the \WEB\ documentation in the same order. + +Top-down programming gives you a strong idea of where you are going, but +it forces you to keep a lot of plans in your head; suspense builds up +because nothing is really nailed down until the end. Bottom-up programming +has the advantage that you continually wield a more and more powerful +pencil, as more and more subroutines have been constructed; but it forces +you to postpone the overall program organization until the last minute, +so you might flounder aimlessly. + +When I tear up the first draft of a program and start over, my second draft +usually considers things in almost the same order as the first one did. +Sometimes the ``correct'' order is top-down, sometimes it is bottom-up, +and sometimes it's a mixture; but always it's an order that makes sense on +expository grounds. + +Thus the \WEB\ language allows a person to express programs +in a ``stream of consciousness'' order. {\tt TANGLE} is able to scramble +everything up into the arrangement that a \PASCAL\ compiler demands. This +feature of \WEB\ is perhaps its greatest asset; it makes a \WEB-written +program much more readable than the same program written purely in +\PASCAL, even if the latter program is well commented. And the fact that there's +no need to be hung up on the question of top-down versus bottom-up---since +a programmer can now view a large program as a web, to be explored in +a psychologically correct order---is perhaps the greatest lesson I have +learned from my recent experiences. + +Another surprising thing that I learned while using \WEB\ was that +traditional programming languages had been causing me to write inferior +programs, although I hadn't realized what I was doing. My original idea was that +\WEB\ would be merely a tool for documentation, but I actually found that +my \WEB\ programs were better than the programs I had been writing +in other languages. How could this be? + +Well, imagine that you are writing a small subroutine that updates part +of a data structure, and suppose that the updating takes only +one or two lines of code. In practical programs, there's often something +that can go wrong, if the user's input is incorrect, so the subroutine +has to check that the input is correct before doing the update. +Thus, the subroutine has the general form +$$\vbox{\halign{#\hfil\cr +\&{procedure} \\{update};\cr +\&{begin if} \<input data is invalid\> \&{then}\cr +\quad \<Issue an error message and try to recover\>;\cr +\<Update the data structure\>;\cr +\&{end}.\cr}}$$ +A subtle phenomenon occurs in traditional programming languages: While +writing the program for `\<Issue an error message and try to recover\>', +a programmer subconsciously tries to get by with the fewest possible +lines of code, since the program for `\<Update the data structure\>' is +quite short. If an extensive error recovery is actually programmed, the +subroutine will appear to have error-message printing as its main purpose. +But the programmer knows that the error is really an exceptional case that +arises only rarely; therefore a lengthy error recovery doesn't look right, +and most programmers will minimize it (without realizing that they are doing +so) in order to make the subroutine's appearance match its intended behavior. +On the other hand when the same task is programmed with \WEB, the purpose +of \\{update} can be shown quite clearly, and the possibility of error +recovery can be reduced to a mere mention when \\{update} is defined. +When another section entitled `\<Issue an error message and try to +recover\>' is subsequently written, the whole point of that section is to do +the best error recovery, and it becomes quite natural to write a better program +as a result. + +\begingroup +\tolerance=3000 + +This fact---that \WEB\ allows you to let each part of the program have +its appropriate size, without distorting the readability of other parts---means +that good programmers find their \WEB\ programs better than their \PASCAL\ +programs, even though their \PASCAL\ programs once looked like the work +of an expert. + +\endgroup + +\beginsection K. STYLISTIC ISSUES + +I found that my style of using \WEB\ evolved quite a bit during the first +year. The general format, in which each section beings with commentary and +ends with a formal program fragment, is extremely versatile; you have the +freedom to say anything you want, yet you must make a decision about how +you'll do it. I imagine that different programmers will converge to +quite different styles, but I would like to note down some of the things +that have seemed to work best for me. + +Consider first the question of macros versus section names. A named section, +like `\<Issue an error message and try to recover\>', is essentially the +same as a parameterless macro; \WEB\ provides both. I prefer to use +parameterless macros for ``small'' things that can be embodied in a word +or two, but named sections for longer portions of the program that +merit a fuller description. + +I usually start the name of a section with an imperative verb, but I give +a declarative commentary at the beginning of a section. Thus, +{\tt PRIMES.WEB} says `{\bf 8.}~Now that appropriate $\ldots$ +\X8:Print table $p$\X$\;\S\;$\dots\thinspace'; I wouldn't do the opposite +and say `{\bf8.}~Print the table. \X8:Code for printing\X$\;\S\;$\dots'. + +The name of a section (enclosed in angle brackets) should be long enough +to encapsulate the essential characteristics of the code in that section, +but it should not be too verbose. I found very early that it would be a +mistake to include all of the assumptions about local and global variables +in the name of each section, even though such information would strictly +be necessary to isolate that section as an independent module. The trick is +to find a balance between formal and informal exposition so that a reader +can grasp what is happening without being overwhelmed with detail.\ref\Naur% +{P. Naur, Formalization in program development. {\sl BIT\/ \bf22}, +437--453 (1982).} + +Another lesson I learned early in the game was that the name of a section +should explicitly mention any nonstandard control structures, even though +its data structures can often be left implied. Furthermore, if the control +flow is properly explained, you can avoid the usual errors associated +with \&{goto} statements; such statements can safely be introduced in +a restrained but natural manner. + +For example, \sec14 of the prime-printing example could be reprogrammed as +follows, using `\&{loop}' as a macro abbreviation for `\&{while} \\{true} +\&{do}': +$$\vbox{\halign{\hbox to\hsize{#\hfil}\cr +\X14:Increase $j$ until it is the next prime number\X$\;\S$\cr +\quad\&{loop begin} $j\K j+2$;\cr +\qquad\X20:Update variables that depend on $j$\X;\cr +\qquad\X22:If $j$ is prime, \&{goto} \\{found}\X;\cr +\qquad\&{end};\cr +\\{found}:\cr}}$$ +With this change, \sec22 could become +$$\vbox{\halign{\hbox to\hsize{#\hfil}\cr +\X22:If $j$ is prime, \&{goto} \\{found}\X$\;\S$\cr +\quad$n\K2$;\cr +\quad\&{while} $n<\\{ord}$ \&{do}\cr +\qquad\&{begin} \X26:If $p[n]$ is a factor of $j$, \&{goto} \\{not\_found}\X;\cr +\qquad$n\K n+1$;\cr +\qquad\&{end};\cr +\quad\&{goto} \\{found};\cr +\\{not\_found}:\cr}}$$ +if \sec26 changes in the obvious way. The resulting program will be more +efficient on most machines; and I believe that it is actually easier to +read and to write, in spite of the fact that two \&{goto} statements +appear, because the labels have been used with appropriate interpretations +of their abstract significance. + +Of course, \PASCAL\ makes it difficult to use \&{goto} statements, +because Wirth decided that labels should be numeric, and that they +should be declared in advance. If I were to introduce the \&{goto} +statements as suggested, I would have to define numeric macros +\\{found} and \\{not\_found}, and I would have to insert +`\&{label} \\{found}, \\{not\_found}' into the program at the right place. +Such extra work is a bit of a nuisance, but it can be done in \WEB\ without +spoiling the exposition. + +\PASCAL\ has a few other misfeatures that prove to be inconvenient with +respect to \WEB\ exposition. The worst of these is the inability to +declare local variables in the midst of a program or procedure. For +example, a programmer often finds it most natural to define an integer +variable when a \&{for} loop is introduced, but the rules of \PASCAL\ +insist that such a variable be declared rather far away from +that \&{for} loop. My \WEB\ programs overcome this problem by having +sections like `\<Local variables for \\{xyzzy}\>' whenever there's a +rather lengthy procedure `\\{xyzzy}' whose local variables should not +be declared all at once. But when a procedure is short, say only half +a dozen sections long, there's usually no harm in declaring its local +variables in \PASCAL\ style, because the entire text of the procedure will +tend to appear on one or two adjacent pages of the documentation. + +Another slightly awkward aspect of \PASCAL\ is its treatment of semicolons. +If you look closely at the prime-number example, you'll see that I had to +be a bit careful about where I put semicolons; sometimes they occur at the +end of the expanded text of a section, but usually they don't. With +a little self discipline, a person can learn to do this quite satisfactorily, +but it is a nuisance until you get used to it. + +\beginsection L. ECONOMIC ISSUES + +What does it cost to use \WEB? Let's look first at the lowest level, where +computer costs are considered, because it is easy to make quantitative +statements at this level. The running time to {\tt TANGLE} a \WEB\ file is +approximately the same as the time needed to compile the resulting +\PASCAL\ program; hence the extra preprocessing does not cost much. +Similarly, {\tt WEAVE} doesn't take long to produce a file for \TeX. +However, \TeX\ needs a comparatively large amount of time to typeset the +final document. For example, if we assume that each page requires four +seconds, it will take four minutes to produce a 60-page document. The +running time for {\tt WEAVE}-plus-\TeX\ is quite reasonable when you +consider that your program is effectively being +converted into a fairly substantial booklet; but the costs are sufficiently +large to discourage remaking and reprinting such a booklet more than once or +twice a day. When a new program is being developed, it is therefore customary +to work with hardcopy documentation that is slightly obsolete, and to read +the \WEB\ source file itself when up-to-date information is required; +the source file is sufficiently easy to read for such purposes. + +The costs of \WEB\ are more difficult to estimate at higher levels, but I have +found to my surprise that the total time of writing and debugging a \WEB\ +program is no greater than the total time of writing and debugging an +{\mc ALGOL} or {\mc PASCAL} program, even though my \WEB\ programs are +much better, and even though I am putting substantially more documentation +into the programs. Therefore I have lately been using \WEB\ for all of my +programming, even for one-off jobs that I write ``for my eyes only'' just +to explore occasional problems. The extra time I spend in preparing additional +commentary is regained because the debugging time is reduced. + +In retrospect, the fact that a ``literate'' program takes much less time to +debug is not surprising, because the \WEB\ language encourages a discipline +that I was previously unwilling to impose on myself. I had known for a long +time that the programs I construct for publication in a book, or the programs +that I construct in front of a class, have tended to be comparatively free +of errors, because I am forced to clarify my thoughts as I do the programming. +By contrast, when writing for myself alone, I have often taken shortcuts that +proved later to be dreadful mistakes. It's harder for me to fool myself in +such ways when I'm writing a \WEB\ program, because I'm in ``expository +mode'' (analogous to classroom lecturing) whenever a \WEB\ is being spun. +Ergo, less debugging time. + +Now that I am writing all my programs in \WEB, an unforeseen problem has, +however, arisen: I suddenly have a collection of programs that seem quite +beautiful in my own eyes, and I have a compelling urge to publish all of +them so that everybody can admire these works of art. A nice little 10-page +program can easily be written and debugged in an afternoon and evening; +if I keep accumulating such gems, I'll soon run out of storage space, +and my office will be encrusted with webs of my own making. There is no +telling what will happen if lots of other people catch \WEB\ fever and +start foisting their creations on each other. I can already envision the +appearance of a new journal, to be entitled {\sl Webs}, for the publication +of literate programs; I imagine that it will have a large backlog and +a large group of dedicated editors and referees. + +\beginsection M. RELATED WORK + +Nothing about \WEB\ is really new; I have simply combined a bunch of +ideas that have been in the air for a long time. I would like to +summarize in the next few paragraphs the things that had the greatest +influence on my thinking as I put those pieces together. + +George Forsythe wrote in 1966 that ``A useful algorithm is a substantial +contribution to knowledge. Its publication constitutes an important +piece of schol\-ar\-ship.''\ref\GEF{G. E. Forsythe, Algorithms for +scientific computation. {\sl Communications of the ACM\/ \bf9}, 255--256 +(1966).} His comments have always inspired me to strive for excellence +in programming, and they have played a major r\^^Dole in shaping my present +view that it is worthwhile to consider {\it every\/} program as a +work of literature. + +The design of \WEB\ was influenced primarily by the pioneering work +of Pierre-Arnoul de Marneffe,\ref\deM{P. A. de Marneffe, {\sl Holon +Programming}. Univ.~de Liege, Service D'Informatique (December, 1973).}$^,$% +\ref\deMR{P. A. de Marneffe and D. Ribbens, Holon Programming, in +A. G\"unther et al.\ (eds.), {\sl International Computing Symposium 1973\/}, +Amsterdam, North-Holland (1974).} whose research on what he called +``Holon Programming'' has not received the attention it deserves. His +work was, in turn, inspired by Arthur Koestler's excellent treatise +on the structure of complex systems and organisms;\ref\Koest{A. +Koestler, {\sl The Ghost in the Machine}. New York, Macmillan (1968).} +thus we have another connection between programming and literature. +A somewhat similar system was independently created by Edwin Towster.\ref\Tow% +{E. Towster, A convention for explicit declaration of environments +and top-down refinement of data. {\sl IEEE Transactions on Software +Engineering\/ \bf SE--5}, 374--386 (1979).} + +I owe a great debt to Edsger Dijkstra, Tony Hoare, Ole-Johan Dahl, and +Niklaus Wirth for opening my eyes to the importance of abstraction in the +reading and writing of programs, and to Peter Naur for stressing the +importance of a balance between formal and informal methods. + +Tony Hoare provided a special impetus for \WEB\ when he suggested in 1978 +that I should publish my program for \TeX. Since very few large-scale +software systems were available in the literature, he had been trying to +promote the publication of well-written programs. Hoare's suggestion was +actually rather terrifying to me, and I'm sure he knew that he was posing +quite a challenge. As a professor of computer science, I was quite +comfortable publishing papers about toy problems that could be polished up +nicely and presented in an elegant manner; but I had no idea how to take a +piece of real software, with all the compromises necessary to make it +useful to a large class of people on a wide variety of systems, and to open +it up to public scrutiny. How could a supposedly respectable academic, like +me, reveal the way he actually writes large programs? And could a large +program be made intelligible? My previous attempts along these +lines\ref\CF{D. E. Knuth, Computer-drawn flow charts. {\sl +Communications of the ACM\/ \bf 6}, 555--563 (1963).} were by now +hopelessly out of date. I decided that this would be a good time to try +out de Marneffe's ideas; furthermore, the \TeX\ system itself provided me +with new tools for printing and format control, so I suspected that it +would be possible to obtain state-of-the-art documentation by making +proper use of typography. + +It is interesting to reread some of the comments that Tony made ten years +ago in his keynote address to the first ACM symposium on Principles +of Programming Languages:\ref\Hoare{C. A. R. Hoare, {\sl Hints on +Programming Language Design}. Stanford Computer Science Report CS403 +(October 1973).} +\smallskip +{\narrower\noindent +Documentation must be regarded as an integral part of the process of +design and coding. A good programming language will encourage and +assist the programmer to write clear, self-documenting code, and even +perhaps to develop and display a pleasant style of writing. +\smallskip} +\noindent He foresaw many future trends, but not the impending improvements in +typesetting quality: +\smallskip +{\narrower\noindent +It is of course possible for a compiler or service program to expand the +abbreviations, fill in the defaults, and make explicit the assumptions. +But in practice, experience shows that it is very unlikely that the +output of a computer will ever be more readable than its input, except +in such trivial but important aspects as improved indentation. +\smallskip} + +Typographic formatting of computer programs has a long tradition, originating +with {\mc ALGOL} and its immediate precursors. I'm not sure who made the +first experiments, but I believe that the lion's share of the credit +for developing excellent programming-language typography belongs to two +people: Peter Naur, who edited the {\mc ALGOL~60} report\ref\Alg{P. Naur +(ed.)~et al., Report on the algorithmic language ALGOL 60. +{\sl Communications of the ACM\/ \bf3}, 299--314.} and gave special +care to its presentation; and Myrtle Kellington, who served for many years +as executive editor of ACM publications and set the standards that have +been adopted by other journals. The computing profession owes much to +these people, who made published programs so much more readable than they +would otherwise have been; the magnitude of their contribution can only +be appreciated by people who submit computer programs to journals +like {\sl Acta Arithmetica\/} whose editors are unfamiliar with computer +science. Bill McKeeman called attention to formatting issues when he +published Algorithm~268, ``{\mc ALGOL~60} reference language editor,'' +in 1965.\ref\McK{W. M. McKeeman, Algorithm 268. {\sl Communications +of the ACM\/ \bf8}, 667--668 (1965).} There has been a flowering of +such algorithms in recent years; the papers by Oppen\ref\DO{D. Oppen, +Prettyprinting. {\sl ACM Transactions on Programming Languages and +Systems\/ \bf2}, 465--483 (1980).} and by Rose and Welsh\ref\RW{G. A. +Rose and J. Welsh, Formatted programming languages. {\sl Software---% +Practice \char'46\ Experience\/ \bf11}, 651--669 (1981).} are particularly +noteworthy. + +I began to design \WEB\ in the spring of 1979, when I constructed a prototype +system that was called {\tt DOC}. Luis Trabb~Pardo helped me to develop +a suitable style of exposition at that time; then Ignacio Zabala~Salelles +gave a {\tt DOC} a thorough test when he prepared a full implementation +of \TeX\ in \PASCAL. Zabala's implementation was successfully transported +to many different computers,\ref\Z{I. Zabala and L. Trabb Pardo, +The status of the PASCAL implementation of \TeX. {\sl TUGboat\/ \bf1}, +16--17 (1980).}\silentref\ZZ{I. Zabala, \TeX-PASCAL and PASCAL compilers. +{\sl TUGboat\/ \bf2} (1), 11--12 (1981).}\silentref\ZZZ{I. Zabala, +Some feedback from PTEX installations. {\sl TUGboat\/ \bf2} (2), 16--19 +(1981).}$^-$\ref\ZZZZ{I. A. Zabala, How +portable is PASCAL? Draft of paper in preparation (1982).} +and this experience was of immense value to me when I cast \WEB\ into its +present form in 1981. Since then many significant improvements have been +suggested by my colleague David R. Fuchs, and I have also benefited from +the experiences of a large number of outstanding people who volunteered to +be guinea pigs for pre-released versions of \TeX. It's impossible for me +to name everyone who has helped, but I would like to give special thanks +to Arthur Samuel, Howard Trickey, Joe Weening, and Pierre MacKay for +important contributions. I'm fortunate indeed to share a working +environment with such stimulating people. + +When I originally designed the \WEB\ system, I spent about six +weeks preparing the files {\tt TANGLE.WEB} and {\tt WEAVE.WEB}, +during which time I was continually changing the language and +trying different styles of exposition. (The programs were neither +long nor complicated, but this was rather intensive work, so I +didn't get much else done during those six weeks. The first two +weeks were actually spent drafting the first ten per cent of what +is now {\tt TEX.WEB}.) Then I spent about six tedious hours with +a text editor, hand-simulating the behavior of {\tt TANGLE} on +{\tt TANGLE.WEB}, so that I had a program {\tt TANGLE.PAS} that +was ripe for debugging. At first I had to correct errors both in +{\tt TANGLE.WEB} and {\tt TANGLE.PAS}, but soon {\tt TANGLE} was +working well enough that I needed only {\tt TANGLE.WEB} as a +source file. Then {\tt WEAVE.WEB} could be tangled and debugged +too. The total time to create ``Version~0'' of the \WEB\ system, +including the language design and the time to debug the programs +and write a brief manual for users, was about eight weeks; then +enhancements were added at the rate of about one per month for +the next 18 months. As a result of this experience I think it's +reasonable to state that a {\tt WEB}-like system can be created +from scratch in a fairly short time, for some other pair of +languages besides \TeX\ and \PASCAL, by an expert system +programmer who is conversant with both languages. Indeed, I spoke +about \WEB\ on a recent visit to London and one of the people in +the audience decided to test this hypothesis; shortly afterwards I +received an elegant report from Harold Thimbleby, who had just constructed +an excellent system called {\tt Cweb}, based on Troff/Nroff and {\mc +C} instead of \TeX\ and \PASCAL.\ref\Thim{H. Thimbleby, {\sl Cweb}. +Preprint, University of York (August 1983).} + +\beginsection N. RETROSPECT AND PROSPECTS + +Enthusiastic reports about new computer languages, by the authors of those +languages, are commonplace. Hence I'm well aware of the fact that my own +experiences cannot be extrapolated too far. I also realize that, whenever I have +encountered a problem with \WEB, I've simply changed +the system; other users of \WEB\ cannot operate under the same ground rules. + +However, I believe that I have stumbled on a way of programming that produces +better programs that are more port\-able and more easily understood and +maintained; furthermore, the system seems to work with large programs as +well as with small ones. I'm pleased that my work on typography, which +began as an application of computers to another field, has come full circle +and become an application of typography to the heart of +computer science; I like to think of \WEB\ as a neat ``spinoff'' of my +research on \TeX. However, all of my experiences with this system have +been highly colored by my own tastes, and only time will tell if a large +number of other people will find \WEB\ to be equally attractive and useful. + +I made a conscious decision not to design a language that would be +suitable for everybody. My goal was to provide a tool for system +programmers, not for high school students or for hobbyists. I don't have +anything against high school students and hobbyists, but I don't believe +every computer language should attempt to offer all things to all people. +A user of \WEB\ needs to be good enough at computer science that he or she +is comfortable dealing with several languages simultaneously. Since +\WEB\ combines \TeX\ and \PASCAL\ with a few rules of its own, \WEB\ programs +can contain \WEB\ syntax errors, \TeX\ syntax errors, \PASCAL\ syntax errors, +and algorithmic errors; in practice, all four types of errors occur, and +a bit of sophistication is needed to sort out which is which. Computer +scientists tend to be better at such things than other people. I have found +that \WEB\ programs can be debugged rapidly in spite of the profusion +of languages, but I'm sure that many other intelligent people will find +such a task difficult. + +In other words, \WEB\ seems to be specifically for the peculiar breed of +people who are called computer scientists. And I'm pretty sure that there +are also a lot of computer scientists who will not enjoy using \WEB; some +of us are glad that traditional programming languages have comparatively +primitive capabilities for inserted comments, because such difficulties provide +a good excuse for not documenting programs well. Thus, \WEB\ may be only for the +subset of computer scientists who like to write and to explain what they +are doing. My hope is that the ability to make explanations more natural will +cause more programmers to discover the joys of literate programming, +because I believe it's quite a pleasure to combine verbal and mathematical +skills; but perhaps I'm hoping for too much. The fact that at least one +paper has been written that is a syntactically correct {\mc ALGOL 68} +program\ref\ft{C. H. Lindsey, ALGOL 68 with fewer tears. {\sl The +Computer Journal\/ \bf15}, 176--188 (1972).} encourages me to persevere +in my hopes for the future. Perhaps we will even one day find Pulitzer +prizes awarded to computer programs. + +And what about the future of \WEB? If the next year or so of trial use +shows that a lot of other people besides myself become ``hooked'' on this +method of programming, there will be many ways to incorporate the \WEB\ +philosophy into a really effective programming environment. For example, +it will be worthwhile to produce a unified system that does both +tangling and compiling, instead of using separate programs as in Figure~1; +and it will also be worthwhile to carry the unification one step further, +so that run-time debugging as well as syntactic debugging can be done +entirely in terms of the \WEB\ source language. Furthermore, a \WEB-like +system could be designed to incorporate additional modularization, +so that it would be easier to compile different parts of a program +independently. The new generation of graphic workstations makes it +desirable to display selected program sections on demand, by using \TeX\ +only on the sections that are of current interest, instead of producing +hardcopy for an entire document. And so on; a considerable amount of +additional research and development will be appropriate if the idea +of literate programming catches on. + +\bigskip\leftline{\bf Acknowledgements} +\smallskip +{\eightrm\baselineskip9pt +\noindent The preparation of this paper was supported in part by +the National Science Foundation under grants IST-8201926 and MCS-8300984, +and by the System Development Foundation. `\TeX' is a trademark of the +American Mathematical Society.\par} + +\enddoublecolumns % prepare for the references +\bigskip\bigskip +\hbox to\pagewidth{\hss\bf REFERENCES\hss\strut} +\CJrule width\pagewidth +\bigskip +\begindoublecolumns +\let\rm=\eightss \let\sl=\eightssi \let\bf=\eightssb \rm +\baselineskip=9pt +\tolerance=1000 +\references +\bigskip +\noindent +Received September 1983 +\enddoublecolumns +\kern6mm +\CJrule width\pagewidth +\bye diff --git a/support/makeprog/docu/webmac.tex b/support/makeprog/docu/webmac.tex new file mode 100644 index 0000000000..e4a5630ce5 --- /dev/null +++ b/support/makeprog/docu/webmac.tex @@ -0,0 +1,200 @@ +% standard macros for WEB listings (in addition to PLAIN.TEX) +\xdef\fmtversion{\fmtversion+WEBMAC1.4} +\parskip 0pt % no stretch between paragraphs +\parindent 1em % for paragraphs and for the first line of Pascal text + +\font\eightrm=cmr8 +\let\sc=\eightrm \let\mainfont=\tenrm +\font\titlefont=cmr7 scaled\magstep4 % title on the contents page +\font\ttitlefont=cmtt10 scaled\magstep2 % typewriter type in title +\font\tentex=cmtex10 % TeX extended character set (used in strings) + +\def\\#1{\hbox{\it#1\/\kern.05em}} % italic type for identifiers +\def\|#1{\hbox{$#1$}} % one-letter identifiers look a bit better this way +\def\{\hbox{\bf#1\/}} % boldface type for reserved words +\def\.#1{\hbox{\tentex % typewriter type for strings + \let\\=\BS % backslash in a string + \let\'=\RQ % right quote in a string + \let\`=\LQ % left quote in a string + \let\{=\LB % left brace in a string + \let\}=\RB % right brace in a string + \let\~=\TL % tilde in a string + \let\ =\SP % space in a string + \let\_=\UL % underline in a string + \let\&=\AM % ampersand in a string + #1}} +\def\#{\hbox{\tt\char`\#}} % parameter sign +\def\${\hbox{\tt\char`\$}} % dollar sign +\def\%{\hbox{\tt\char`\%}} % percent sign +\def\^{\ifmmode\mathchar"222 \else\char`^ \fi} % pointer or hat +% circumflex accents can be obtained from \^^D instead of \^ +\def\AT!{@} % at sign for control text + +\chardef\AM=`\& % ampersand character in a string +\chardef\BS=`\\ % backslash in a string +\chardef\LB=`\{ % left brace in a string +\def\LQ{{\tt\char'22}} % left quote in a string +\chardef\RB=`\} % right brace in a string +\def\RQ{{\tt\char'23}} % right quote in a string +\def\SP{{\tt\char`\ }} % (visible) space in a string +\chardef\TL=`\~ % tilde in a string +\chardef\UL=`\_ % underline character in a string + +\newbox\bak \setbox\bak=\hbox to -1em{} % backspace one em +\newbox\bakk\setbox\bakk=\hbox to -2em{} % backspace two ems + +\newcount\ind % current indentation in ems +\def\1{\global\advance\ind by1\hangindent\ind em} % indent one more notch +\def\2{\global\advance\ind by-1} % indent one less notch +\def\3#1{\hfil\penalty#10\hfilneg} % optional break within a statement +\def\4{\copy\bak} % backspace one notch +\def\5{\hfil\penalty-1\hfilneg\kern2.5em\copy\bakk\ignorespaces}% optional break +\def\6{\ifmmode\else\par % forced break + \hangindent\ind em\noindent\kern\ind em\copy\bakk\ignorespaces\fi} +\def\7{\Y\6} % forced break and a little extra space + +\let\yskip=\smallskip +\def\to{\mathrel{.\,.}} % double dot, used only in math mode +\def\note#1#2.{\Y\noindent{\hangindent2em\baselineskip10pt\eightrm#1 #2.\par}} +\def\lapstar{\rlap{*}} +\def\startsection{\Q\noindent{\let\*=\lapstar\bf\modstar.\quad}} +\def\defin#1{\global\advance\ind by 2 \1\&{#1 }} % begin `define' or `format' +\def\A{\note{See also}} % cross-reference for multiply defined section names +\def\B{\mathopen{\.{@\{}}} % begin controlled comment +\def\C#1{\ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi % Pascal comments + \XX\hfil\penalty-1\hfilneg\quad$\{\,$#1$\,\}$\XX} +\def\D{\defin{define}} % macro definition +\def\E{\cdot10^} % exponent in floating point constant +\def\F{\defin{format}} % format definition +\let\G=\ge % greater than or equal sign +\def\H#1{\hbox{\rm\char"7D\tt#1}} % hexadecimal constant +\let\I=\ne % unequal sign +\def\J{\.{@\&}} % TANGLE's join operation +\let\K=\gets % left arrow +\let\L=\le % less than or equal sign +\outer\def\M#1.{\MN#1.\ifon\vfil\penalty-100\vfilneg % beginning of section + \vskip12ptminus3pt\startsection\ignorespaces} +\outer\def\N#1.#2.{\MN#1.\vfil\eject % beginning of starred section + \def\rhead{\uppercase{\ignorespaces#2}} % define running headline + \message{*\modno} % progress report + \edef\next{\write\cont{\Z{#2}{\modno}{\the\pageno}}}\next % to contents file + \ifon\startsection{\bf\ignorespaces#2.\quad}\ignorespaces} +\def\MN#1.{\par % common code for \M, \N + {\xdef\modstar{#1}\let\*=\empty\xdef\modno{#1}} + \ifx\modno\modstar \onmaybe \else\ontrue \fi \mark{\modno}} +\def\O#1{\hbox{\rm\char'23\kern-.2em\it#1\/\kern.05em}} % octal constant +\def\P{\rightskip=0pt plus 100pt minus 10pt % go into Pascal mode + \sfcode`;=3000 + \pretolerance 10000 + \hyphenpenalty 10000 \exhyphenpenalty 10000 + \global\ind=2 \1\ \unskip} +\def\Q{\rightskip=0pt % get out of Pascal mode + \sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50 } +\let\R=\lnot % logical not +\let\S=\equiv % equivalence sign +\def\T{\mathclose{\.{@\}}}} % terminate controlled comment +\def\U{\note{This code is used in}} % cross-reference for uses of sections +\let\V=\lor % logical or +\let\W=\land % logical and +\def\X#1:#2\X{\ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi % section name + \XX$\langle\,$#2{\eightrm\kern.5em#1}$\,\rangle$\XX} +\def\Y{\par\yskip} +\let\Z=\let % now you can \send the control sequence \Z +\def\){\hbox{\.{@\$}}} % sign for string pool check sum +\def\]{\hbox{\.{@\\}}} % sign for forced line break +\def\=#1{\kern2pt\hbox{\vrule\vtop{\vbox{\hrule + \hbox{\strut\kern2pt\.{#1}\kern2pt}} + \hrule}\vrule}\kern2pt} % verbatim string +\let\~=\ignorespaces +\let\*=* + +\def\onmaybe{\let\ifon=\maybe} \let\maybe=\iftrue +\newif\ifon \newif\iftitle \newif\ifpagesaved +\def\lheader{\mainfont\the\pageno\eightrm\qquad\rhead\hfill\title\qquad + \tensy x\mainfont\topmark} % top line on left-hand pages +\def\rheader{\tensy x\mainfont\topmark\eightrm\qquad\title\hfill\rhead + \qquad\mainfont\the\pageno} % top line on right-hand pages +\def\page{\box255 } +\def\normaloutput#1#2#3{\ifodd\pageno\hoffset=\pageshift\fi + \shipout\vbox{ + \vbox to\fullpageheight{ + \iftitle\global\titlefalse + \else\hbox to\pagewidth{\vbox to10pt{}\ifodd\pageno #3\else#2\fi}\fi + \vfill#1}} % parameter #1 is the page itself + \global\advance\pageno by1} + +\def\rhead{\.{WEB} OUTPUT} % this running head is reset by starred sections +\def\title{} % an optional title can be set by the user +\def\topofcontents{\centerline{\titlefont\title} + \vfill} % this material will start the table of contents page +\def\botofcontents{\vfill} % this material will end the table of contents page +\def\contentspagenumber{0} % default page number for table of contents +\newdimen\pagewidth \pagewidth=6.5in % the width of each page +\newdimen\pageheight \pageheight=8.7in % the height of each page +\newdimen\fullpageheight \fullpageheight=9in % page height including headlines +\newdimen\pageshift \pageshift=0in % shift righthand pages wrt lefthand ones +\def\magnify#1{\mag=#1\pagewidth=6.5truein\pageheight=8.7truein + \fullpageheight=9truein\setpage} +\def\setpage{\hsize\pagewidth\vsize\pageheight} % use after changing page size +\def\contentsfile{CONTENTS} % file that gets table of contents info +\def\readcontents{\input CONTENTS} + +\newwrite\cont +\output{\setbox0=\page % the first page is garbage + \openout\cont=\contentsfile + \global\output{\normaloutput\page\lheader\rheader}} +\setpage +\vbox to \vsize{} % the first \topmark won't be null + +\def\ch{\note{The following sections were changed by the change file:} + \let\*=\relax} +\newbox\sbox % saved box preceding the index +\newbox\lbox % lefthand column in the index +\def\inx{\par\vskip6pt plus 1fil % we are beginning the index + \write\cont{} % ensure that the contents file isn't empty + \closeout\cont % the contents information has been fully gathered + \output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi + \global\setbox\sbox=\page \global\pagesavedtrue} + \pagesavedfalse \eject % eject the page-so-far and predecessors + \setbox\sbox\vbox{\unvbox\sbox} % take it out of its box + \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height + \hsize=.5\pagewidth \advance\hsize by -10pt + % column width for the index (20pt between cols) + \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines + \def\lr{L} % this tells whether the left or right column is next + \output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R} + \else\normaloutput{\vbox to\pageheight{\box\sbox\vss + \hbox to\pagewidth{\box\lbox\hfil\page}}}\lheader\rheader + \global\vsize\pageheight\gdef\lr{L}\global\pagesavedfalse\fi} + \message{Index:} + \parskip 0pt plus .5pt + \outer\def\:##1, {\par\hangindent2em\noindent##1:\kern1em} % index entry + \let\ttentry=\. \def\.##1{\ttentry{##1\kern.2em}} % give \tt a little room + \def\[##1]{$\underline{##1}$} % underlined index item + \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar + \hyphenpenalty 10000 \parindent0pt} +\def\fin{\par\vfill\eject % this is done when we are ending the index + \ifpagesaved\null\vfill\eject\fi % output a null index column + \if L\lr\else\null\vfill\eject\fi % finish the current page + \parfillskip 0pt plus 1fil + \def\rhead{NAMES OF THE SECTIONS} + \message{Section names:} + \output{\normaloutput\page\lheader\rheader} + \setpage + \def\note##1##2.{\hfil\penalty-1\hfilneg\quad{\eightrm##1 ##2.}} + \linepenalty=10 % try to conserve lines + \def\U{\note{Used in}} % cross-reference for uses of sections + \def\:{\par\hangindent 2em}\let\*=*\let\.=\ttentry} +\def\con{\par\vfill\eject % finish the section names + \rightskip 0pt \hyphenpenalty 50 \tolerance 200 + \setpage + \output{\normaloutput\page\lheader\rheader} + \titletrue % prepare to output the table of contents + \pageno=\contentspagenumber \def\rhead{TABLE OF CONTENTS} + \message{Table of contents:} + \topofcontents + \line{\hfil Section\hbox to3em{\hss Page}} + \def\Z##1##2##3{\line{\ignorespaces##1 + \leaders\hbox to .5em{.\hfil}\hfil\ ##2\hbox to3em{\hss##3}}} + \readcontents\relax % read the contents info + \botofcontents \end} % print the contents page(s) and terminate diff --git a/support/makeprog/dos/makeprog.cf1 b/support/makeprog/dos/makeprog.cf1 new file mode 100644 index 0000000000..b8345ef3d7 --- /dev/null +++ b/support/makeprog/dos/makeprog.cf1 @@ -0,0 +1,58 @@ +% This is MAKEPROG.CF.STDOUT as of 02 May 89 +%-------------------------------------------------------- +% (c) 1988,1989 by J.Schrod. + +% +% Changefile to map terminal output to standard output +% assumes terminal output to be unbuffered +% + + +@x S 2 +program MAKEPROG(@!doc_file,@!change_file,@!prog_file); +@y +program MAKEPROG(@!output,@!doc_file,@!change_file,@!prog_file); +@z + + +@x S 20 +is assumed to consist of characters of type |text_char|: +@^system dependencies@> + +@d print(#)==write(term_out,#) {`|print|' means write on the terminal} +@d print_ln(#)==write_ln(term_out,#) {`|print|' and then start new line} +@d new_line==write_ln(term_out) {start new line} +@d print_nl(#)== {print information starting on a new line} + begin new_line; print(#); + end + +@<Globals...@>= +@!term_out:text_file; {the terminal as an output file} +@y +is assumed to consist of characters of type |text_char|: +@^system dependencies@> + +@d term_out == output {the output file is normaly the terminal} +@# +@d print(#)==write(term_out,#) {`|print|' means write on the terminal} +@d print_ln(#)==write_ln(term_out,#) {`|print|' and then start new line} +@d new_line==write_ln(term_out) {start new line} +@d print_nl(#)== {print information starting on a new line} + begin new_line; print(#); + end +@z + + +@x S 21 +rewrite(term_out,'TTY:'); {send |term_out| output to the terminal} +@y +do_nothing; {|output| is opened automatically} +@z + + +@x S 22 +@d update_terminal == break(term_out) {empty the terminal output buffer} +@y +@d update_terminal == do_nothing {output to |output| is unbuffered} +@z + diff --git a/support/makeprog/dos/makeprog.cf2 b/support/makeprog/dos/makeprog.cf2 new file mode 100644 index 0000000000..88cc2e3cdc --- /dev/null +++ b/support/makeprog/dos/makeprog.cf2 @@ -0,0 +1,140 @@ +% This is MAKEPROG.CF.SYSTEM.TURBO as of 02 May 89 +%-------------------------------------------------------- +% (c) 1988,1989 by J.Schrod. + +% Revision 1 was released in November, 1988. +% Revision 2 switched to Turbo Pascal. (89-05-02) + +% +% Changefile for system dependencies of Turbo-Pascal (V5.0) +% documentation updates (too less...) +% no non-local goto's (=> no global label end_of_MAKEPROG) +% units CRT and DOS are used (???) +% compiler directives +% othercases +% new_line is appended at end (for Compaq DOS 3.3!) +% +% I/O handling is done later +% + + +@x Limbo +\def\version{1.0} +@y + \let\maybe=\iffalse +\def\version{1.0, Rev.~2} +@z + + +@x Limbo + \centerline{\titlefont The MAKEPROG processor} +@y + \centerline{\titlefont DOS Changes to the MAKEPROG processor} +@z + + +@x S 1 +@d banner=='This is MAKEPROG, Version 1.0.' +@y +@d banner=='This is MAKEPROG, Version 1.0 (DOS Changes, Rev. 2).' +@z + + +@x +@d end_of_MAKEPROG = 9999 {go here to wrap it up} +@y +@f uses == const +@z + + +@x S 2 +label end_of_MAKEPROG; {go here to finish} +@y + uses CRT, DOS ; +@z + + + the following change should be ignored after TIE + +@x S 3 +@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging} +@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging} +@y +@d debug== {we are debugging \.{MAKEPROG}} +@d gubed== +@z + + +@x S 4 +@<Compiler directives@>= +@{@&@=$D-@> @} {no debug overhead} +@!debug @{@&@=$D+@> @}@+ gubed @; {but turn everything on when debugging} +@y +@<Compiler directives@>= +@{@=$R-,B-,D-,E-,L-,S+,V-@>@} + +{ R- no range checking } +{ B- boolean evaluation ... short circuit } +{ D- debug information off } +{ E- emulation off } +{ L- local symbols ... off } +{ S+ stack checking ... on } +{ V- var string checking ... relaxed } +@z + + +@x S 7 +@d othercases == others: {default for cases not listed explicitly} +@y +@d othercases == else {default for cases not listed explicitly} +@z + + +@x S 31 +procedure jump_out; +begin goto end_of_MAKEPROG; +end; +@y +procedure jump_out; + begin + {here files should be closed if the operating system requires it} + @;@# + @<Print the job |history|@>; + halt; + end; +@z + + +@x +end_of_MAKEPROG: + @# + {here files should be closed if the operating system requires it} + @;@# + @<Print the job |history|@>; +@y + jump_out; +@z + + +@x S 187 +@<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 +@<Print the job |history|@>= +begin +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; +end +@z + diff --git a/support/makeprog/dos/makeprog.cf3 b/support/makeprog/dos/makeprog.cf3 new file mode 100644 index 0000000000..7ed17748c3 --- /dev/null +++ b/support/makeprog/dos/makeprog.cf3 @@ -0,0 +1,230 @@ +% This is MAKEPROG.CF.IO.TURBO as of 02 May 89 +%-------------------------------------------------------- +% (c) 1989 by J.Schrod. + +% +% Changefile for I/O handling with Turbo-Pascal (V5.0) +% text_char is extended ASCII (up to chr(255)) +% text_file must be named text +% tab marks should be recognized +% binding of external files to internal file variables is not done by the +% run time system, therefor reset and rewrite must be done with own +% procedures +% file name is taken from command line +% files are buffered for higher performance +% file access is done with a read/write in a string (no get/put available) +% files must be closed (???) +% + + +@x S 2 +@t\4@>@<Error handling procedures@>@; +@y +@t\4@>@<Turbo Pascal specific procedures@>@; +@t\4@>@<Error handling procedures@>@; +@z + + +@x S 12 +@d last_text_char=127 {ordinal number of the largest element of |text_char|} +@y +@d last_text_char=255 {ordinal number of the largest element of |text_char|} +@z + + +@x S 12 +@!text_file=packed file of text_char; +@y +@!text_file=text; +@z + + +@x S 17 +for i:=1 to " "-1 do xchr[i]:=' '; +@y +for i:=1 to " "-1 do xchr[i]:=' '; +xchr[tab_mark] := chr(tab_mark); +@z + + +@x S 24 +reset(doc_file); reset(change_file); +@y +@< Initialize the Turbo Pascal specific input/output related variables @>; +tp_reset(doc_file, 'DOC', doc_buffer); +tp_reset(change_file, 'CHF', chf_buffer); +@z + + +@x S 26 +rewrite(prog_file); +@y +tp_rewrite(prog_file, prog_ext, prog_buffer); +@z + + +@x S 28 [ERROR] +function input_ln(var f:text_file):boolean; {inputs a line or returns |false|} + var final_limit:0..buf_size; {|limit| without trailing blanks} + begin limit:=0; final_limit:=0; + if eof(f) then input_ln:=false + else begin + while not eoln(f) do + begin buffer[limit]:=xord[f^]; get(f); + incr(limit); + if (buffer[limit-1]<>" ") and (buffer[limit-1]<>tab_mark) then + final_limit:=limit; + if limit=buf_size then + begin while not eoln(f) do get(f); + decr(limit); {keep |buffer[buf_size]| empty} + print_nl('! Input line too long'); error; mark_error; +@.Input line too long@> + end; + end; + read_ln(f); limit:=final_limit; input_ln:=true; + end; + end; +@y +function input_ln(var f:text_file):boolean; {inputs a line or returns |false|} + var s: string; {temporary line storage} + final_limit: 0..buf_size+1; {index into |s|} + i: 0..buf_size; {index into |buffer|} + begin + if eof(f) then + begin limit := 0; input_ln := false; + end + else begin read_ln(f,s); + final_limit := ord(s[0]); + while (final_limit > 0) and + ((s[final_limit] = ' ') or (s[final_limit] = xchr[tab_mark])) do + decr(final_limit); + if final_limit > buf_size+1 then final_limit := buf_size + 1; + for i:=0 to final_limit-1 do buffer[i] := xord[s[i+1]]; + if final_limit >= buf_size then + begin final_limit := buf_size - 1; {keep |buffer[buf_size]| empty} + print_nl('! Input line too long'); error; mark_error; +@.Input line too long@> + end; + limit := final_limit; input_ln := true ; + end; + end; +@z + + +@x S 31 + {here files should be closed if the operating system requires it} +@y + close(prog_file); close(doc_file); close(change_file); +@z + + +@x +procedure put_line; + var i: 0..buf_size; + begin + for i:=0 to limit-1 do write(prog_file, xchr[buffer[i]]); + write_ln(prog_file); + end; +@y +procedure put_line; + var i: 0..buf_size; + s: string; {temporary line storage} + begin + for i:=0 to limit-1 do s[i+1] := xchr[buffer[i]]; + s[0] := chr(limit); write_ln(prog_file, s); + end; +@z + + +@x S 188 +\noindent This module should be replaced, if necessary, by +changes to the program that are necessary to make +\MAKEPROG{} work at a particular installation. It is +usually best to design your change file so that all changes +to previous modules preserve the module numbering; then +everybody's version will be consistent with the printed +program. More extensive changes, which introduce new +modules, can be inserted here; then only the index itself +will get a new module number. +@^system dependencies@> +@y +\noindent This module should be replaced, if necessary, by +changes to the program that are necessary to make +\MAKEPROG{} work at a particular installation. It is +usually best to design your change file so that all changes +to previous modules preserve the module numbering; then +everybody's version will be consistent with the printed +program. More extensive changes, which introduce new +modules, can be inserted here; then only the index itself +will get a new module number. +@^system dependencies@> + + + +@* Resetting and rewriting files. + +\noindent Turbo Pascal allows the usage of command line +parameters but does not connect external files with the +internal file variables automatically. Therefore we demand +the file name (without extension) in the command line---with +this name we can open resp.\ create the files. The real +opening of input files is done via |tp_reset| and the +creating of output files is done via |tp_rewrite|. Both +procedures have two parameters, the file variable and the +extension which should be used together with the file name. +The extension is passed without a dot. + +@< Glob... @>= +@!file_name: string; +@!prog_ext: string; + + +@ But first we have to look at the command line. If there is +no parameter present we print a usage message and finish the +program afterwards. Here |jump_out| cannot be used because +files would be closed that would be not open. + +@< Initialize the Turbo... @>= +if param_count < 1 then + begin print('! usage: makeprog [-ext] file_name'); halt; + end +else begin + file_name := param_str(1); + if (param_count = 2) and (file_name[1] = '-') then + begin prog_ext := copy(file_name, 2, 3); file_name := param_str(2); + end + else prog_ext := 'PRG'; + end; + + +@ @< Type... @>= +@!file_buffer=packed array[1..4096] of byte; + + +@ @< Glob... @>= +@!doc_buffer:file_buffer; {file buffer for primary input} +@!chf_buffer:file_buffer; {file buffer for updates} +@!prog_buffer:file_buffer; {file buffer for output} + + +@ If an input file can't be opened we handle it just like a +file that is not there. I.e., we use \.{NUL:} as the input +file---|eof| will then always return true as presumed by the +program. + +@< Turbo Pascal specific procedures @>= +procedure tp_reset( var f: text_file; ext: string; var buf: file_buffer ); + begin assign(f, file_name+'.'+ext); @/ + @{@=$I-@>@} reset(f); @{@=$I+@>@} + if io_result = 0 then settextbuf(f, buf) + else begin assign(f, 'NUL'); reset(f); { |eof(f) = true| } + end; + end; + +@# + +procedure tp_rewrite( var f: text_file; ext: string; var buf: file_buffer ); + begin assign(f, file_name+'.'+ext); rewrite(f); settextbuf(f, buf); + end; +@z + diff --git a/support/makeprog/dos/makeprog.chf b/support/makeprog/dos/makeprog.chf new file mode 100644 index 0000000000..e2ddd3d7ce --- /dev/null +++ b/support/makeprog/dos/makeprog.chf @@ -0,0 +1,349 @@ +@x +\def\version{1.0.1} +@y + \let\maybe=\iffalse +\def\version{1.0.1, Rev.~2} +@z + +@x + \centerline{\titlefont The MAKEPROG processor} +@y + \centerline{\titlefont DOS Changes to the MAKEPROG processor} +@z + +@x +@d banner=='This is MAKEPROG, Version 1.0.1.' +@y +@d banner=='This is MAKEPROG, Version 1.0.1 (DOS Changes, Rev. 2).' +@z + +@x +@d end_of_MAKEPROG = 9999 {go here to wrap it up} +@y +@f uses == const +@z + +@x +program MAKEPROG(@!doc_file,@!change_file,@!prog_file); +@y +program MAKEPROG(@!output,@!doc_file,@!change_file,@!prog_file); +@z + +@x +label end_of_MAKEPROG; {go here to finish} +@y + uses CRT, DOS ; +@z + +@x +@t\4@>@<Error handling procedures@>@; +@y +@t\4@>@<Turbo Pascal specific procedures@>@; +@t\4@>@<Error handling procedures@>@; +@z + + x +@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging} +@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging} + y +@d debug== {we are debugging \.{MAKEPROG}} +@d gubed== + z + +@x +@<Compiler directives@>= +@{@&@=$D-@> @} {no debug overhead} +@!debug @{@&@=$D+@> @}@+ gubed @; {but turn everything on when debugging} +@y +@<Compiler directives@>= +@{@=$R-,B-,D-,E-,L-,S+,V-@>@} + +{ R- no range checking } +{ B- boolean evaluation ... short circuit } +{ D- debug information off } +{ E- emulation off } +{ L- local symbols ... off } +{ S+ stack checking ... on } +{ V- var string checking ... relaxed } +@z + +@x +@d othercases == others: {default for cases not listed explicitly} +@y +@d othercases == else {default for cases not listed explicitly} +@z + +@x +@d last_text_char=127 {ordinal number of the largest element of |text_char|} +@y +@d last_text_char=255 {ordinal number of the largest element of |text_char|} +@z + +@x +@!text_file=packed file of text_char; +@y +@!text_file=text; +@z + +@x +for i:=1 to " "-1 do xchr[i]:=' '; +@y +for i:=1 to " "-1 do xchr[i]:=' '; +xchr[tab_mark] := chr(tab_mark); +@z + +@x +is assumed to consist of characters of type |text_char|: +@^system dependencies@> + +@d print(#)==write(term_out,#) {`|print|' means write on the terminal} +@d print_ln(#)==write_ln(term_out,#) {`|print|' and then start new line} +@d new_line==write_ln(term_out) {start new line} +@d print_nl(#)== {print information starting on a new line} + begin new_line; print(#); + end + +@<Globals...@>= +@!term_out:text_file; {the terminal as an output file} +@y +is assumed to consist of characters of type |text_char|: +@^system dependencies@> + +@d term_out == output {the output file is normaly the terminal} +@# +@d print(#)==write(term_out,#) {`|print|' means write on the terminal} +@d print_ln(#)==write_ln(term_out,#) {`|print|' and then start new line} +@d new_line==write_ln(term_out) {start new line} +@d print_nl(#)== {print information starting on a new line} + begin new_line; print(#); + end +@z + +@x +rewrite(term_out,'TTY:'); {send |term_out| output to the terminal} +@y +do_nothing; {|output| is opened automatically} +@z + +@x +@d update_terminal == break(term_out) {empty the terminal output buffer} +@y +@d update_terminal == do_nothing {output to |output| is unbuffered} +@z + +@x +reset(doc_file); reset(change_file); +@y +@< Initialize the Turbo Pascal specific input/output related variables @>; +tp_reset(doc_file, 'DOC', doc_buffer); +tp_reset(change_file, 'CHF', chf_buffer); +@z + +@x +rewrite(prog_file); +@y +tp_rewrite(prog_file, prog_ext, prog_buffer); +@z + +@x +function input_ln(var f:text_file):boolean; {inputs a line or returns |false|} + var final_limit:0..buf_size; {|limit| without trailing blanks} + begin limit:=0; final_limit:=0; + if eof(f) then input_ln:=false + else begin + while not eoln(f) do + begin buffer[limit]:=xord[f^]; get(f); + incr(limit); + if (buffer[limit-1]<>" ") and (buffer[limit-1]<>tab_mark) then + final_limit:=limit; + if limit=buf_size then + begin while not eoln(f) do get(f); + decr(limit); {keep |buffer[buf_size]| empty} + print_nl('! Input line too long'); error; mark_error; +@.Input line too long@> + end; + end; + read_ln(f); limit:=final_limit; input_ln:=true; + end; + end; +@y +function input_ln(var f:text_file):boolean; {inputs a line or returns |false|} + var s: string; {temporary line storage} + final_limit: 0..buf_size+1; {index into |s|} + i: 0..buf_size; {index into |buffer|} + begin + if eof(f) then + begin limit := 0; input_ln := false; + end + else begin read_ln(f,s); + final_limit := ord(s[0]); + while (final_limit > 0) and + ((s[final_limit] = ' ') or (s[final_limit] = xchr[tab_mark])) do + decr(final_limit); + if final_limit > buf_size+1 then final_limit := buf_size + 1; + for i:=0 to final_limit-1 do buffer[i] := xord[s[i+1]]; + if final_limit >= buf_size then + begin final_limit := buf_size - 1; {keep |buffer[buf_size]| empty} + print_nl('! Input line too long'); error; mark_error; +@.Input line too long@> + end; + limit := final_limit; input_ln := true ; + end; + end; +@z + +@x +procedure jump_out; +begin goto end_of_MAKEPROG; +end; +@y +procedure jump_out; + begin + close(prog_file); close(doc_file); close(change_file); + @;@# + @<Print the job |history|@>; + halt; + end; +@z + +@x +procedure put_line; + var i: 0..buf_size; + begin + for i:=0 to limit-1 do write(prog_file, xchr[buffer[i]]); + write_ln(prog_file); + end; +@y +procedure put_line; + var i: 0..buf_size; + s: string; {temporary line storage} + begin + for i:=0 to limit-1 do s[i+1] := xchr[buffer[i]]; + s[0] := chr(limit); write_ln(prog_file, s); + end; +@z + +@x +end_of_MAKEPROG: + @# + {here files should be closed if the operating system requires it} + @;@# + @<Print the job |history|@>; +@y + jump_out; +@z + +@x +@<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 +@<Print the job |history|@>= +begin +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; +end +@z + +@x +\noindent This module should be replaced, if necessary, by +changes to the program that are necessary to make +\MAKEPROG{} work at a particular installation. It is +usually best to design your change file so that all changes +to previous modules preserve the module numbering; then +everybody's version will be consistent with the printed +program. More extensive changes, which introduce new +modules, can be inserted here; then only the index itself +will get a new module number. +@^system dependencies@> +@y +\noindent This module should be replaced, if necessary, by +changes to the program that are necessary to make +\MAKEPROG{} work at a particular installation. It is +usually best to design your change file so that all changes +to previous modules preserve the module numbering; then +everybody's version will be consistent with the printed +program. More extensive changes, which introduce new +modules, can be inserted here; then only the index itself +will get a new module number. +@^system dependencies@> + + + +@* Resetting and rewriting files. + +\noindent Turbo Pascal allows the usage of command line +parameters but does not connect external files with the +internal file variables automatically. Therefore we demand +the file name (without extension) in the command line---with +this name we can open resp.\ create the files. The real +opening of input files is done via |tp_reset| and the +creating of output files is done via |tp_rewrite|. Both +procedures have two parameters, the file variable and the +extension which should be used together with the file name. +The extension is passed without a dot. + +@< Glob... @>= +@!file_name: string; +@!prog_ext: string; + + +@ But first we have to look at the command line. If there is +no parameter present we print a usage message and finish the +program afterwards. Here |jump_out| cannot be used because +files would be closed that would be not open. + +@< Initialize the Turbo... @>= +if param_count < 1 then + begin print('! usage: makeprog [-ext] file_name'); halt; + end +else begin + file_name := param_str(1); + if (param_count = 2) and (file_name[1] = '-') then + begin prog_ext := copy(file_name, 2, 3); file_name := param_str(2); + end + else prog_ext := 'PRG'; + end; + + +@ @< Type... @>= +@!file_buffer=packed array[1..4096] of byte; + + +@ @< Glob... @>= +@!doc_buffer:file_buffer; {file buffer for primary input} +@!chf_buffer:file_buffer; {file buffer for updates} +@!prog_buffer:file_buffer; {file buffer for output} + + +@ If an input file can't be opened we handle it just like a +file that is not there. I.e., we use \.{NUL:} as the input +file---|eof| will then always return true as presumed by the +program. + +@< Turbo Pascal specific procedures @>= +procedure tp_reset( var f: text_file; ext: string; var buf: file_buffer ); + begin assign(f, file_name+'.'+ext); @/ + @{@=$I-@>@} reset(f); @{@=$I+@>@} + if io_result = 0 then settextbuf(f, buf) + else begin assign(f, 'NUL'); reset(f); { |eof(f) = true| } + end; + end; + +@# + +procedure tp_rewrite( var f: text_file; ext: string; var buf: file_buffer ); + begin assign(f, file_name+'.'+ext); rewrite(f); settextbuf(f, buf); + end; +@z + diff --git a/support/makeprog/filelist b/support/makeprog/filelist new file mode 100644 index 0000000000..d29ce7b3eb --- /dev/null +++ b/support/makeprog/filelist @@ -0,0 +1,29 @@ +./makeprog/COPYING +./makeprog/Filelist +./makeprog/History +./makeprog/Readme +./makeprog/docu/mkprgdoc.bbl +./makeprog/docu/mkprgdoc.tex +./makeprog/docu/primes.web +./makeprog/docu/web.tex +./makeprog/docu/webmac.tex +./makeprog/src/doc2tex.l +./makeprog/src/makeprog.web +./makeprog/src/progdoc.doc +./makeprog/src/progdoc.tex +./makeprog/src/progltx.doc +./makeprog/src/progltx.sty +./makeprog/src/tex2doc.l +./makeprog/WEBtoC/convert +./makeprog/WEBtoC/makefile +./makeprog/WEBtoC/makepext.c +./makeprog/WEBtoC/makeprog.c +./makeprog/WEBtoC/makeprog.ch +./makeprog/WEBtoC/makeprog.h +./makeprog/WEBtoC/web2c.def +./makeprog/WEBtoC/web2c.h +./makeprog/dos/makeprog.cf1 +./makeprog/dos/makeprog.cf2 +./makeprog/dos/makeprog.cf3 +./makeprog/dos/makeprog.chf +./makeprog/dos/makeprog.exe diff --git a/support/makeprog/history b/support/makeprog/history new file mode 100644 index 0000000000..f852fb8dc5 --- /dev/null +++ b/support/makeprog/history @@ -0,0 +1,14 @@ +% History for MAKEPROG 06 Oct 90 +%------------------------------------------------------------ + +% !! MSCF -- most significant change first !! + +DATE WHO REMARK +90-10-06 js added doc2tex and tex2doc +90-10-05 js begun history + + +AUTHOR: + js: Joachim Schrod <xitijsch@ddathd21.bitnet> +CONTRIBUTORS: + (-: place enough for your name :-) diff --git a/support/makeprog/readme b/support/makeprog/readme new file mode 100644 index 0000000000..0cd59c7cf9 --- /dev/null +++ b/support/makeprog/readme @@ -0,0 +1,44 @@ +% Readme for MAKEPROG 25 Sep 90 +%--------------------------------------------------------- +% (c) 1989,1990 by J.Schrod (TeXsys). + +This is the MAKEPROG system of structured documentation. +It has the version number 1.0.1. + +These programs are given away to you under the conditions of the GNU +General Public License. This license is described in the file COPYING. + +It is delivered either as a compressed tar or a ZOO archive. If you +dearchive it with ZOO you should use "x//" as the extraction command +to create all directories. + The following directory structure is used: + + ./makeprog General files, like Readme and COPYING. + ./makeprog/src WEB source and TeX macro files + ./makeprog/docu documentation + ./makeprog/dos DOS executable and Turbo Pascal changefiles + ./makeprog/webtoc changefiles and resulting C source from WEBtoC + +I have included the article ``Literate Programming'' of Prof. Knuth +in the DOCU subdirectory, it consists of the two files web.tex and +primes.web. It should be read first because it focuses on the +principles standing behind every WEB-like system. Afterwards a look +should be taken on mkprgdoc.tex -- the user documentation of the +MAKEPROG system. The installation procedure is described (very short) +in a section of this report. + Please note that the DOS changefile makeprog.chf is produced by +merging the changefiles makeprog.cf? with TIE and discarding the debug +switches afterwards. + + +Please send bug reports or enhancements to: + + Detig Schrod \TeX{}sys + Joachim Schrod + Kranichweg 1 + + D-6074 R\"odermark-Urberach + FR Germany + + Bitnet: XITIJSCH@DDATHD21 +---- diff --git a/support/makeprog/src/doc2tex.l b/support/makeprog/src/doc2tex.l new file mode 100644 index 0000000000..fbe22e600a --- /dev/null +++ b/support/makeprog/src/doc2tex.l @@ -0,0 +1,122 @@ +%{ +/* doc2tex.l 06 Oct 90 + *--------------------------------------------------------- + * (c) 1990 by J.Schrod. + * Copyright conditions: GNU General Public License + */ + +/* + * VERSION HISTORY + * + * DATE REMARK + * 90-10-06 mentioned filelist WEBWARE for LISTSERV in Heidelberg, + * added tuglib + * 90-01-14 june.cs.washington.edu instead of cs.washington.edu + * 90-01-20 first version + */ + +%} + +%S PREAMBLE COMMENT COPY + + +%{ +#include <ctype.h> +void hint_to_makeprog(); +%} + + +%% + +%{ +#ifdef MSDOS + check_yycrank(); /* this should go into ncform... */ +#endif + BEGIN PREAMBLE; +%} + + +^\ *\n putc('\n', yyout); + + +<PREAMBLE>^%.*\n ECHO; +<PREAMBLE>^. { + hint_to_makeprog(); + unput(yytext[0]); NLSTATE; + BEGIN COMMENT; + } + +<COMMENT>^\\beginprog.*\n { + fputs("%%% ", yyout); ECHO; + BEGIN COPY; + } +<COMMENT>^.*\n { + fputs("%%% ", yyout); ECHO; + } + +<COPY>^\\endprog.*\n { + fputs("%%% ", yyout); ECHO; + BEGIN COMMENT; + } +<COPY>^%%%.*\n { + fputs("%%% ", yyout); ECHO; + fprintf(stderr, "Macro code changed at line %d\n", yylineno-1); + } +<COPY>^.*\n ECHO; + + +.|\n { + register char c = yytext[0]; + fprintf(stderr, + "! Illegal state at char %c (\"%02x) in line %d\n", + ( isprint(c) ? c : ' ' ), + (unsigned char)c, + ( c == '\n' ? yylineno-1 : yylineno ) + ); + } + + +%% + +void hint_to_makeprog() +{ + fputs("%%%%\n",yyout); + fputs("%%%%\n",yyout); + fputs( + "%%%% These TeX macros were documented with the documentation system\n", + yyout); + fputs( + "%%%% MAKEPROG and automatically converted to the current form.\n", + yyout); + fputs( + "%%%% If you have MAKEPROG available you may transform it back to\n", + yyout); + fputs( + "%%%% the original input: Remove every occurence of three percents\n", + yyout); + fputs( + "%%%% and one optional blank from the beginning of a line and remove\n", + yyout); + fputs( + "%%%% every line which starts with four percents. The following lex\n", + yyout); + fputs( + "%%%% program will do this:\n", + yyout); + fputs("%%%%\n",yyout); + fputs("%%%% %%\n",yyout); + fputs("%%%%\n",yyout); + fputs("%%%% ^%%%\\ ? ;\n",yyout); + fputs("%%%% ^%%%%.*\\n ;\n",yyout); + fputs("%%%%\n",yyout); + fputs( + "%%%% MAKEPROG may be obtained over the net from the Bitnet-Listserver\n", + yyout); + fputs( + "%%%% LISTSERV@DHDURZ1 (filelist WEBWARE), from tuglib@science.utah.edu,\n", + yyout); + fputs("%%%% or via ftp from june.cs.washington.edu.\n", yyout); + fputs("%%%%\n",yyout); + fputs("%%%%\n",yyout); + return; +} diff --git a/support/makeprog/src/makeprog.web b/support/makeprog/src/makeprog.web new file mode 100644 index 0000000000..0ef2f4989d --- /dev/null +++ b/support/makeprog/src/makeprog.web @@ -0,0 +1,1171 @@ +% This is MAKEPROG.WEB as of 25 Sep 90 +%--------------------------------------------------------- +% (c) 1988 by J.Schrod. +% Major parts of this program have been taken from TANGLE.WEB +% by D.E.Knuth, which is not copyrighted. + +% +% VERSION HISTORY (MSCF -- most significant change first) +% +% DATE VER WHO REMARK +% 90-09-25 1.0.1 js updated copyright notice +% 88-11 1.0 js first release + +% bug reports to: +% +% Detig$\,\cdot\,$Schrod \TeX{}sys +% Joachim Schrod +% Kranichweg 1 +% +% D-6074 R\"odermark-Urberach +% FR Germany +% +% Tel. (+6074) 1617 +% Bitnet: XITIJSCH@DDATHD21 + +% author: +% js: Joachim Schrod <xitijsch@ddathd21.bitnet> +% contributors: +% (-: place enough for your name! :-) + + +\def\title{MAKEPROG (V\version)} +\def\version{1.0.1} +\def\years{1988,1990} + +\font\ninerm=cmr9 +\let\mc=\ninerm % medium caps for names like EBCDIC + +\def\hang{\hangindent 3em\indent\ignorespaces} +\def\item#1{ % fixed item of 30pt width + {\parskip=0pt\endgraf\noindent}% % new line + \hangindent 30pt % + \hbox to 30pt{\hfil#1\kern 1em}% + \ignorespaces % ignore following blanks + } + +\def\PASCAL{Pascal} +\def\MAKEPROG{\leavevmode\hbox{\mc MAKEPROG\/}} +\def\ASCII{\leavevmode\hbox{\mc ASCII\/}} +\mathchardef\BA="3224 % double arrow as relation + +\def\topofcontents{\null\vfill + \centerline{\titlefont The MAKEPROG processor} + \vskip 15pt + \centerline{(Version \version)} + \vskip 2cm} +\def\botofcontents{\vfill + \hangindent 3em + \noindent $\copyright$ \years{} by Joachim Schrod. + All rights reserved. + \par \vskip 1ex + \noindent \MAKEPROG{} comes with {\bf absolutely no warranty}. + This is free software, and you are welcome to redistribute it + under certain conditions; see section~2 for details. + \par} + + + + + +@* Introduction. % sect. 1 + +\noindent This program converts a documentation file to a +program file, i.e.\ all files between \.{\\beginprog} and +\.{\\endprog} are copied verbatim. It was written by +J.~Schrod in September, 1987. This program is written in +\.{WEB}. + +The program uses a few features of the local \PASCAL\ +compiler that may need to be changed in other installations: + +\item{1)} Case statements have a default. + +\item{2)} Input-output routines may need to be adapted for +use with a particular character set and/or for printing +messages on the user's terminal. + +\noindent System-dependent portions of \MAKEPROG{} can be +identified by looking at the entries for `system +dependencies' in the index below. +@!@^system dependencies@> + + +@ % sect. 2 +This program 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~1, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +{\bf without any warranty\/}; without even the implied warranty of +{\bf merchantability\/} or {\bf 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 this program; if not, write to the Free Software Foundation, +Inc., 675~Mass Ave, Cambridge, MA~02139, USA. + +The ``banner line'' defined here should be changed whenever +\MAKEPROG{} is modified. The copyright notice must not be deleted. + +@d banner=='This is MAKEPROG, Version 1.0.1.' +@d copy_right==' (c) 1988,1990 by J.Schrod.' +@d rights_res==' All rights reserved.' + + +@ % sect. 3 +The program begins with a fairly normal header, made up of +pieces that will mostly be filled in later. The input comes +from files |doc_file| and |change_file|, the output goes to +file |prog_file|. Messages from \MAKEPROG{} are written to +|term_out|, which is supposed to be the terminal. +@^system dependencies@> + +If it is necessary to abort the job because of a fatal +error, the program calls the `|jump_out|' procedure, which +goes to the label |end_of_MAKEPROG|. + +@d end_of_MAKEPROG = 9999 {go here to wrap it up} + +@p @t\4@>@<Compiler directives@>@/ +program MAKEPROG(@!doc_file,@!change_file,@!prog_file); +label end_of_MAKEPROG; {go here to finish} +const @<Constants in the outer block@>@; +type @<Types in the outer block@>@; +var @<Globals in the outer block@>@; +@t\4@>@<Error handling procedures@>@; +procedure initialize; + var @<Local variables for initialization@>@; + begin @<Set initial values@> + end; + + +@ % sect. 4 +Some of this code is optional for use when debugging only; +such material is enclosed between the delimiters \&{debug} +and \&{gubed}. + +@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging} +@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging} +@f debug==repeat +@f gubed==until + + +@ % sect. 5 +The \PASCAL\ compiler used to develop this system has +``compiler directives'' that can appear in comments whose +first character is a dollar sign. In production versions of +\MAKEPROG{} these directives tell the compiler that it is +safe to avoid range checks and to leave out the extra code +it inserts for the \PASCAL\ debugger's benefit. +@^system dependencies@> + +@<Compiler directives@>= +@{@&@=$D-@> @} {no debug overhead} +@!debug @{@&@=$D+@> @}@+ gubed @; {but turn everything on when debugging} + + +@ % sect. 6 +Labels are given symbolic names by the following +definitions. We insert the label `|exit|' just before the +`\&{end}' of a procedure in which we have used the +`|return|' statement defined below; the label `|restart|' is +occasionally used at the very beginning of a procedure; and +the label `|reswitch|' is occasionally used just prior to a +\&{case} statement in which some cases change the conditions +and we wish to branch to the newly applicable case. Loops +that are set up with the \&{loop} construction defined below +are commonly exited by going to `|done|' or to `|found|' or +to `|not_found|', and they are sometimes repeated by going +to `|continue|'. + +@d exit=10 {go here to leave a procedure} +@d restart=20 {go here to start a procedure again} +@d reswitch=21 {go here to start a case statement again} +@d continue=22 {go here to resume a loop} +@d done=30 {go here to exit a loop} +@d found=31 {go here when you've found it} +@d not_found=32 {go here when you've found something else} + + +@ % sect. 7 +Here are some macros for common programming idioms. + +@d incr(#) == #:=#+1 {increase a variable by unity} +@d decr(#) == #:=#-1 {decrease a variable by unity} +@d loop == @+ while true do@+ {repeat over and over until a |goto| happens} +@d do_nothing == {empty statement} +@d return == goto exit {terminate a procedure call} +@f return == nil +@f loop == xclause + + +@ % sect. 8 +We assume that |case| statements may include a default case +that applies if no matching label is found. Thus, we shall +use constructions like +@^system dependencies@> +$$ +\vbox{\halign{#\hfil\cr +|case x of|\cr + \quad 1: $\langle\,$code for $x=1\,\rangle$;\cr + \quad 3: $\langle\,$code for $x=3\,\rangle$;\cr + \quad |othercases| $\langle\,$code for |x<>1| and |x<>3|$\,\rangle$\cr +|endcases|\cr +}} +$$ +since most \PASCAL\ compilers have plugged this hole in the +language by incorporating some sort of default mechanism. +For example, the compiler used to develop \.{WEB} and \TeX\ +allows `|others|:' as a default label, and other \PASCAL s +allow syntaxes like `\&{else}' or `\&{otherwise}' or +`\\{otherwise}:', etc. The definitions of |othercases| and +|endcases| should be changed to agree with local +conventions. (Of course, if no default mechanism is +available, the |case| statements of this program must be +extended by listing all remaining cases.) + +@d othercases == others: {default for cases not listed explicitly} +@d endcases == @+end {follows the default case in an extended |case| statement} +@f othercases == else +@f endcases == end + + +@ % sect. 9 +The following parameter is set big enough to be sufficient +for most applications of \MAKEPROG{}. + +@<Constants...@>= +@!buf_size=500; {maximum length of input line} + + +@ % sect. 10 +A global variable called |history| will contain one of four values +at the end of every run: |spotless| means that no unusual messages were +printed; |harmless_message| means that a message of possible interest +was printed but no serious errors were detected; |error_message| means that +at least one error was found; |fatal_message| means that the program +terminated abnormally. The value of |history| does not influence the +behavior of the program; it is simply computed for the convenience +of systems that might want to use such information. + +@d spotless=0 {|history| value for normal jobs} +@d harmless_message=1 {|history| value when non-serious info was printed} +@d error_message=2 {|history| value when an error was noted} +@d fatal_message=3 {|history| value when we had to stop prematurely} +@# +@d mark_harmless==@t@>@+if history=spotless then history:=harmless_message +@d mark_error==history:=error_message +@d mark_fatal==history:=fatal_message + +@<Glob...@>=@!history:spotless..fatal_message; {how bad was this run?} + +@ % sect. 11 +@<Set init...@>=history:=spotless; + + + + + +@* The character set. % sect. 12 + +\noindent One of the main goals in the design of \MAKEPROG{} +has been to make it readily portable between a wide variety +of computers. Yet \MAKEPROG{} by its very nature must use a +greater variety of characters than most computer programs +deal with, and character encoding is one of the areas in +which existing machines differ most widely from each other. + +To resolve this problem, all input to \MAKEPROG{} is +converted to an internal seven-bit code that is essentially +standard \ASCII{}, the ``American Standard Code for +Information Interchange.'' The conversion is done +immediately when each character is read in. Conversely, +characters are converted from \ASCII{} to the user's +external representation just before they are output. Such +an internal code is never relevant to users of \MAKEPROG{}. + +\noindent Here is a table of the standard visible \ASCII{} codes: +$$\def\:{\char\count255\global\advance\count255 by 1} +\count255='40 +\vbox{ +\hbox{\hbox to 40pt{\it\hfill0\/\hfill}% +\hbox to 40pt{\it\hfill1\/\hfill}% +\hbox to 40pt{\it\hfill2\/\hfill}% +\hbox to 40pt{\it\hfill3\/\hfill}% +\hbox to 40pt{\it\hfill4\/\hfill}% +\hbox to 40pt{\it\hfill5\/\hfill}% +\hbox to 40pt{\it\hfill6\/\hfill}% +\hbox to 40pt{\it\hfill7\/\hfill}} +\vskip 4pt +\hrule +\def\^{\vrule height 10.5pt depth 4.5pt} +\halign{\hbox to 0pt{\hskip -24pt\O{#0}\hfill}&\^ +\hbox to 40pt{\tt\hfill#\hfill\^}& +&\hbox to 40pt{\tt\hfill#\hfill\^}\cr +04&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} +05&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} +06&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} +07&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} +10&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} +11&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} +12&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} +13&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} +14&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} +15&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} +16&\:&\:&\:&\:&\:&\:&\:&\:\cr\noalign{\hrule} +17&\:&\:&\:&\:&\:&\:&\:\cr} +\hrule width 280pt}$$ +(Actually, of course, code @"20 is an invisible blank +space.) Code @"3E was once an upward arrow (\.{\char'13}), +and code @"3F was once a left arrow (\.^^X), in olden times +when the first draft of \ASCII{} code was prepared; but +\MAKEPROG{} works with today's standard \ASCII{} in which +those codes represent circumflex and underline as shown. + +@<Types...@>= +@!ASCII_code=0..@"7F; {seven-bit numbers, a subrange of the integers} + + +@ % sect. 13 +The original \PASCAL\ compiler was designed in the late 60s, +when six-bit character sets were common, so it did not make +provision for lowercase letters. Nowadays, of course, we +need to deal with both capital and small letters in a +convenient way, so \MAKEPROG{} assumes that it is being used +with a \PASCAL\ whose character set contains at least the +characters of standard \ASCII{} as listed above. Some +\PASCAL\ compilers use the original name |char| for the data +type associated with the characters in text files, while +other \PASCAL s consider |char| to be a 64-element subrange +of a larger data type that has some other name. + +In order to accommodate this difference, we shall use the +name |text_char| to stand for the data type of the +characters in the input and output files. We shall also +assume that |text_char| consists of the elements +|chr(first_text_char)| through |chr(last_text_char)|, +inclusive. The following definitions should be adjusted if +necessary. +@^system dependencies@> + +@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=127 {ordinal number of the largest element of |text_char|} + +@<Types...@>= +@!text_file=packed file of text_char; + + +@ % sect. 14 +The \MAKEPROG{} processor convert between \ASCII{} code and +the user's external character set by means of arrays |xord| +and |xchr| that are analogous to \PASCAL's |ord| and |chr| +functions. + +@<Globals...@>= +@!xord: array [text_char] of ASCII_code; + {specifies conversion of input characters} +@!xchr: array [ASCII_code] of text_char; + {specifies conversion of output characters} + + +@ % sect. 15 +If we assume that every system using \.{WEB} is able to read +and write the visible characters of standard \ASCII{} +(although not necessarily using the \ASCII{} codes to +represent them), the following assignment statements +initialize most of the |xchr| array properly, without +needing any system-dependent changes. For example, the +statement \.{xchr["A"]:=\'A\'} that appears in the present +\.{WEB} file might be encoded in, say, \hbox{\mc EBCDIC} +code on the external medium on which it resides, but +\.{TANGLE} will convert from this external code to \ASCII{} +and back again. Therefore the assignment statement +\.{XCHR[65]:=\'A\'} will appear in the corresponding +\PASCAL\ file, and \PASCAL\ will compile this statement so +that |xchr[65]| receives the character \.A in the external +(|char|) code. Note that it would be quite incorrect to say +\.{xchr["A"]:="A"}, because |"A"| is a constant of type +|integer|, not |char|, and because we have $|"A"|=65$ +regardless of the external character set. + +@<Set init...@>= +xchr[" "]:=' '; +xchr["!"]:='!'; +xchr[""""]:='"'; +xchr["#"]:='#'; +xchr["$"]:='$'; +xchr["%"]:='%'; +xchr["&"]:='&'; +xchr["'"]:='''';@/ +xchr["("]:='('; +xchr[")"]:=')'; +xchr["*"]:='*'; +xchr["+"]:='+'; +xchr[","]:=','; +xchr["-"]:='-'; +xchr["."]:='.'; +xchr["/"]:='/';@/ +xchr["0"]:='0'; +xchr["1"]:='1'; +xchr["2"]:='2'; +xchr["3"]:='3'; +xchr["4"]:='4'; +xchr["5"]:='5'; +xchr["6"]:='6'; +xchr["7"]:='7';@/ +xchr["8"]:='8'; +xchr["9"]:='9'; +xchr[":"]:=':'; +xchr[";"]:=';'; +xchr["<"]:='<'; +xchr["="]:='='; +xchr[">"]:='>'; +xchr["?"]:='?';@/ +xchr["@@"]:='@@'; +xchr["A"]:='A'; +xchr["B"]:='B'; +xchr["C"]:='C'; +xchr["D"]:='D'; +xchr["E"]:='E'; +xchr["F"]:='F'; +xchr["G"]:='G';@/ +xchr["H"]:='H'; +xchr["I"]:='I'; +xchr["J"]:='J'; +xchr["K"]:='K'; +xchr["L"]:='L'; +xchr["M"]:='M'; +xchr["N"]:='N'; +xchr["O"]:='O';@/ +xchr["P"]:='P'; +xchr["Q"]:='Q'; +xchr["R"]:='R'; +xchr["S"]:='S'; +xchr["T"]:='T'; +xchr["U"]:='U'; +xchr["V"]:='V'; +xchr["W"]:='W';@/ +xchr["X"]:='X'; +xchr["Y"]:='Y'; +xchr["Z"]:='Z'; +xchr["["]:='['; +xchr["\"]:='\'; +xchr["]"]:=']'; +xchr["^"]:='^'; +xchr["_"]:='_';@/ +xchr["`"]:='`'; +xchr["a"]:='a'; +xchr["b"]:='b'; +xchr["c"]:='c'; +xchr["d"]:='d'; +xchr["e"]:='e'; +xchr["f"]:='f'; +xchr["g"]:='g';@/ +xchr["h"]:='h'; +xchr["i"]:='i'; +xchr["j"]:='j'; +xchr["k"]:='k'; +xchr["l"]:='l'; +xchr["m"]:='m'; +xchr["n"]:='n'; +xchr["o"]:='o';@/ +xchr["p"]:='p'; +xchr["q"]:='q'; +xchr["r"]:='r'; +xchr["s"]:='s'; +xchr["t"]:='t'; +xchr["u"]:='u'; +xchr["v"]:='v'; +xchr["w"]:='w';@/ +xchr["x"]:='x'; +xchr["y"]:='y'; +xchr["z"]:='z'; +xchr["{"]:='{'; +xchr["|"]:='|'; +xchr["}"]:='}'; +xchr["~"]:='~';@/ +xchr[0]:=' '; xchr[@"7F]:=' '; {these \ASCII{} codes are not used} + + +@ % sect. 16 +Some of the nonprintable \ASCII{} codes have been given +symbolic names in \MAKEPROG{} because they are used with a +special meaning. + +@d tab_mark=@"09 {\ASCII{} code used as tab-skip} +@d line_feed=@"0A {\ASCII{} code thrown away at end of line} +@d form_feed=@"0C {\ASCII{} code used at end of page} +@d carriage_return=@"0D {\ASCII{} code used at end of line} + + +@ % sect. 17 +When we initialize the |xord| array and the remaining parts of |xchr|, +it will be convenient to make use of an index variable, |i|. + +@<Local variables for init...@>= +@!i:0..last_text_char; + + +@ % sect. 18 +Here now is the system-dependent part of the character set. +If \MAKEPROG{} is being implemented on a garden-variety +\PASCAL\ for which only standard \ASCII{} codes will appear +in the input and output files, you don't need to make any +changes here. +@^system dependencies@> + +Changes to the present module will make \MAKEPROG{} more +friendly on computers that have an extended character set, +so that one can type things like Umlaute. If you have an +extended set of characters that are easily incorporated into +text files, you can assign codes arbitrarily here, giving an +|xchr| equivalent to whatever characters the users of +\MAKEPROG{} are allowed to have in their input files, +provided that unsuitable characters do not correspond to +special codes like |carriage_return| that are listed above. + +@<Set init...@>= +for i:=1 to " "-1 do xchr[i]:=' '; + + +@ % sect. 19 +The following system-independent code makes the |xord| array +contain a suitable inverse to the information in |xchr|. + +@<Set init...@>= +for i:=first_text_char to last_text_char do xord[chr(i)]:=" "; +for i:=1 to "~" do xord[xchr[i]]:=i; + + + + + +@* Basic Input and output. % sect. 20 + +\noindent The input conventions of \MAKEPROG{} are identical +to those of \.{WEB}. Therefore people who need to make +modifications to both systems should be able to do so +without too many headaches. + + +@ % sect. 21 +Terminal output is done by writing on file |term_out|, which +is assumed to consist of characters of type |text_char|: +@^system dependencies@> + +@d print(#)==write(term_out,#) {`|print|' means write on the terminal} +@d print_ln(#)==write_ln(term_out,#) {`|print|' and then start new line} +@d new_line==write_ln(term_out) {start new line} +@d print_nl(#)== {print information starting on a new line} + begin new_line; print(#); + end + +@<Globals...@>= +@!term_out:text_file; {the terminal as an output file} + + +@ % sect. 22 +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} + + +@ % sect. 23 +The |update_terminal| procedure is called when we want to +make sure that everything we have output to the terminal so +far has actually left the computer's internal buffers and +been sent. +@^system dependencies@> + +@d update_terminal == break(term_out) {empty the terminal output buffer} + + +@ % sect. 24 +The main input comes from |doc_file|; this input may be +overridden by changes in |change_file|. (If |change_file| +is empty, there are no changes.) + +@<Globals...@>= +@!doc_file:text_file; {primary input} +@!change_file:text_file; {updates} + + +@ % sect. 25 +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@> + +@< Set init... @>= +reset(doc_file); reset(change_file); + + +@ % sect. 26 +The output goes to |prog_file|. + +@<Globals...@>= +@!prog_file: text_file; + + +@ % sect. 27 +The following code opens |prog_file|. Since this file is +listed in the program header, we assume that the \PASCAL\ +runtime system has checked that a suitable external file +name have been given. +@^system dependencies@> + +@<Set init...@>= +rewrite(prog_file); + + +@ % sect. 28 +Input goes into an array called |buffer|. + +@<Globals...@>= +@!buffer: array[0..buf_size] of ASCII_code; + + +@ % sect. 29 +The |input_ln| procedure brings the next line of input from +the specified file into the |buffer| array and returns the +value |true|, unless the file has already been entirely +read, in which case it returns |false|. The conventions of +\.{WEB} are followed; i.e., |ASCII_code| numbers +representing the next line of the file are input into +|buffer[0]|, |buffer[1]|, \dots, |buffer[limit-1]|; trailing +blanks are ignored; and the global variable |limit| is set +to the length of the line. The value of |limit| must be +strictly less than |buf_size|. +@^system dependencies@> + +We assume that none of the |ASCII_code| values of +|buffer[j]| for |0<=j<limit| is equal to 0, @"7F, +|line_feed|, |form_feed|, or |carriage_return|. + +@p +function input_ln(var f:text_file):boolean; {inputs a line or returns |false|} + var final_limit:0..buf_size; {|limit| without trailing blanks} + begin limit:=0; final_limit:=0; + if eof(f) then input_ln:=false + else begin + while not eoln(f) do + begin buffer[limit]:=xord[f^]; get(f); + incr(limit); + if (buffer[limit-1]<>" ") and (buffer[limit-1]<>tab_mark) then + final_limit:=limit; + if limit=buf_size then + begin while not eoln(f) do get(f); + decr(limit); {keep |buffer[buf_size]| empty} + print_nl('! Input line too long'); error; mark_error; +@.Input line too long@> + end; + end; + read_ln(f); limit:=final_limit; input_ln:=true; + end; + end; + + + + + +@* Reporting errors to the user. % sect. 30 + +\noindent Errors are reported to the user by saying +$$ + \hbox{`|err_print('! Error message')|'}, +$$ +followed by `|jump_out|' if no recovery from the error is +provided. This will print the error message followed by an +indication of where the error was spotted in the source +file. Note that no period follows the error message, since +the error routine will automatically supply a period. + +\noindent The actual error indications are provided by a +procedure called |error|. + +@d err_print(#)==begin print_nl(#); error; mark_error; end + +@<Error handling...@>= +procedure error; {prints '\..' and location of error message} + begin @< Print error location @>; + update_terminal; + end; + + +@ % sect. 31 +The error locations can be indicated by using the global +variables |line| and |changing|, which tell respectively the +the current line number and whether or not the current line +is from |change_file| or |doc_file|. This routine should be +modified on systems whose standard text editor has special +line-numbering conventions. +@^system dependencies@> + +@< Print error location @>= +begin +if changing then print('. (change file ') @+ else print('. ('); +print_ln('l.', line:1, ')'); +print(' '); {this space separates the message from future output} +end + + +@ % sect. 32 +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 \MAKEPROG{}. 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_MAKEPROG| should be copied into the |jump_out| +procedure, followed by a call to a system procedure that +terminates the program. + +@d fatal_error(#)==begin print_nl(#); error; mark_fatal; jump_out; + end + +@<Error handling...@>= +procedure jump_out; +begin goto end_of_MAKEPROG; +end; + + +@ % sect. 33 +Sometimes the program's behavior is far different from what +it should be, and \MAKEPROG{} prints an error message that +is really for the \MAKEPROG{} maintenance person, not the +user. In such cases the program says +|confusion('indication of where we are')|. + +@d confusion(#)==fatal_error('! This can''t happen (',#,')') +@.This can't happen@> + + + + + +@* The kernel. % sect. 34 + +\noindent Let us now consider the routine |get_line| that +takes care of merging |change_file| into |doc_file|. The +|get_line| procedure also updates the line numbers for error +messages. + +@<Globals...@>= +@!line:integer; {the number of the current line in the current file} +@!other_line:integer; {the number of the current line in the input file that + is not currently being read} +@!temp_line:integer; {used when interchanging |line| with |other_line|} +@!limit:0..buf_size; {the last character position occupied in the buffer} +@!input_has_ended: boolean; {if |true|, there is no more input} +@!changing: boolean; {if |true|, the current line is from |change_file|} + + +@ % sect. 35 +As we change |changing| from |true| to |false| and back +again, we must remember to swap the values of |line| and +|other_line| so that the |err_print| routine will be sure to +report the correct line number. + +@d change_changing== + begin changing := not changing;@/ + temp_line:=other_line; other_line:=line; line:=temp_line; + end {$|line| \BA |other_line|$} + + +@ % sect. 36 +When |changing| is |false|, the next line of |change_file| +is kept in |change_buffer[0..change_limit-1]|, for purposes +of comparison with the next line of |doc_file|. After the +change file has been completely input, we set +|change_limit:=0|, so that no further matches will be made. + +@<Globals...@>= +@!change_buffer:array[0..buf_size] of ASCII_code; +@!change_limit:0..buf_size; {the last position occupied in |change_buffer|} + + +@ % sect. 37 +Here's a simple function that checks if the two buffers are +different. + +@p function lines_dont_match:boolean; +label exit; +var k:0..buf_size; {index into the buffers} +begin lines_dont_match:=true; +if change_limit<>limit then return; +if limit>0 then + for k:=0 to limit-1 do if change_buffer[k]<>buffer[k] then return; +lines_dont_match:=false; +exit: end; + + +@ % sect. 38 +Procedure |prime_the_change_buffer| sets |change_buffer| in +preparation for the next matching operation. Since blank +lines in the change file are not used for matching, we have +|(change_limit=0)and not changing| if and only if the change +file is exhausted. This procedure is called only when +|changing| is true; hence error messages will be reported +correctly. + +@p procedure prime_the_change_buffer; +label continue, done, exit; +var k:0..buf_size; {index into the buffers} +begin change_limit:=0; {this value will be used if the change file ends} +@<Skip over comment lines in the change file; |return| if end of file@>; +@<Skip to the next nonblank line; |return| if end of file@>; +@<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>; +exit: end; + + +@ % sect. 39 +While looking for a line that begins with \.{@@x} in the +change file, we allow lines that begin with \.{@@}, as long +as they don't begin with \.{@@y} or \.{@@z} (which would +probably indicate that the change file is fouled up). + +@<Skip over comment lines in the change file...@>= +loop@+ begin incr(line); + if not input_ln(change_file) then return; + if limit<2 then goto continue; + if buffer[0]<>"@@" then goto continue; + if (buffer[1]>="X")and(buffer[1]<="Z") then + buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify} + if buffer[1]="x" then goto done; + if (buffer[1]="y")or(buffer[1]="z") then + err_print('! Where is the matching @@x?'); +@.Where is the match...@> +continue: end; +done: + + +@ % sect. 40 +Here we are looking at lines following the \.{@@x}. + +@<Skip to the next nonblank line...@>= +repeat incr(line); + if not input_ln(change_file) then + begin err_print('! Change file ended after @@x'); +@.Change file ended...@> + return; + end; +until limit>0; + + +@ % sect. 41 +@<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>= +begin change_limit:=limit; +for k:=0 to limit-1 do change_buffer[k]:=buffer[k]; +end + + +@ % sect. 42 +The following procedure is used to see if the next change +entry should go into effect; it is called only when +|changing| is false. The idea is to test whether or not the +current contents of |buffer| matches the current contents of +|change_buffer|. If not, there's nothing more to do; but if +so, a change is called for: All of the text down to the +\.{@@y} is supposed to match. An error message is issued if +any discrepancy is found. Then the procedure prepares to +read the next line from |change_file|. + +@p +procedure check_change; {switches to |change_file| if the buffers match} +label exit; +var n:integer; {the number of discrepancies found} +@!k:0..buf_size; {index into the buffers} +begin if lines_dont_match then return; +n:=0; +loop@+ begin change_changing; {now it's |true|} + incr(line); + if not input_ln(change_file) then + begin err_print('! Change file ended before @@y');@/ +@.Change file ended...@> + change_limit:=0; change_changing; {|false| again} + return; + end; + @<If the current line starts with \.{@@y}, + report any discrepancies and |return|@>; + @<Move |buffer| and |limit|...@>; + change_changing; {now it's |false|} + incr(line); + if not input_ln(doc_file) then + begin err_print('! CWEB file ended during a change'); +@.CWEB file ended...@> + input_has_ended:=true; return; + end; + if lines_dont_match then incr(n); + end; +exit: end; + + +@ % sect. 43 +@<If the current line starts with \.{@@y}...@>= +if limit>1 then if buffer[0]="@@" then + begin if (buffer[1]>="X")and(buffer[1]<="Z") then + buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify} + if (buffer[1]="x")or(buffer[1]="z") then + err_print('! Where is the matching @@y?') +@.Where is the match...@> + else if buffer[1]="y" then + begin if n>0 then + err_print('! Hmm... ',n:1,' of the preceding lines failed to match'); +@.Hmm... n of the preceding...@> + return; + end; + end + + +@ % sect. 44 +@< Initialize the input system @>= +begin line:=0; other_line:=0;@/ +changing:=true; prime_the_change_buffer; change_changing;@/ +limit:=0; buffer[0]:=" "; input_has_ended:=false; +end + + +@ % sect. 45 +The |get_line| procedure puts the next line of merged input +into the buffer and updates the other variables +appropriately. A space is placed at the right end of the +line. We output points to show the user the progress in +reading. + +@p +procedure get_line; {inputs the next line} + label restart; + begin +restart: if changing then + @<Read from |change_file| and maybe turn off |changing|@>; + if not changing then + begin @<Read from |doc_file| and maybe turn on |changing|@>; + if changing then goto restart; + end; + buffer[limit]:=" "; + if (line mod 500) = 0 then + begin print(line:1); update_terminal; + end + else if (line mod 100) = 0 then + begin print('.'); update_terminal; + end + @!debug else begin print('.'); update_terminal; @+ end @+ gubed @; + ;@/ + end; + + +@ % sect. 46 +@<Read from |doc_file|...@>= +begin incr(line); +if not input_ln(doc_file) then input_has_ended:=true +else if change_limit>0 then check_change; +end + + +@ % sect. 47 +@<Read from |change_file|...@>= +begin incr(line); +if not input_ln(change_file) then + begin err_print('! Change file ended without @@z'); +@.Change file ended...@> + buffer[0]:="@@"; buffer[1]:="z"; limit:=2; + end; +if limit>1 then {check if the change has ended} + if buffer[0]="@@" then + begin if (buffer[1]>="X")and(buffer[1]<="Z") then + buffer[1]:=buffer[1]+"z"-"Z"; {lowercasify} + if (buffer[1]="x")or(buffer[1]="y") then + err_print('! Where is the matching @@z?') +@.Where is the match...@> + else if buffer[1]="z" then + begin prime_the_change_buffer; change_changing; + end; + end; +end + + +@ % sect. 48 +At the end of the program, we will tell the user if the +change file had a line that didn't match any relevant line +in |doc_file|. + +@<Check that all changes have been read@>= +begin +if change_limit<>0 then {|changing| is false} + begin + for limit:=0 to change_limit do buffer[limit] := change_buffer[limit]; + limit := change_limit; changing := true; line := other_line; + err_print('! Change file entry did not match'); +@.Change file entry did not match@> + end; +end + + +@ % sect. 49 +The |put_line| procedure outputs the next line from +|buffer| to |prog_file|. Perhaps we should give here a +progress report, too (with asterisks?) + +@p +procedure put_line; + var i: 0..buf_size; + begin + for i:=0 to limit-1 do write(prog_file, xchr[buffer[i]]); + write_ln(prog_file); + end; + + + + + +@* The main program. % sect. 50 + +\noindent We have defined some procedures, and it is time to +use them---here is where \MAKEPROG{} starts, and where it +ends. +@^system dependencies@> + +@p + begin initialize; + print_ln(banner); {print a ``banner line''} + print_ln(copy_right); print_ln(rights_res); {print a copyright notice} + @< Initialize the input system @>; + debug print_ln('begin copy'); + gubed @; + @< Copy all program parts to the output @>; + debug print_ln('end copy'); + gubed @; + @< Check that all changes... @>; +end_of_MAKEPROG: + @# + {here files should be closed if the operating system requires it} + @;@# + @<Print the job |history|@>; + end. + + +@ % sect. 51 +A program part begins after a line that begins with +\.{\\beginprog} and ends before the next line with +\.{\\endprog} starting it. If we find the starting line we +set |state| to |begin_prog|, between to lines |state| has +the value |inner_prog| and with the ending line |state| is +set to |out_of_prog|. + +@d begin_prog = 0 +@d inner_prog = 1 +@d out_of_prog = 2 + +@< Glob... @>= +@!state: begin_prog..out_of_prog; + +@ % sect. 52 +@< Set init... @>= +state := out_of_prog; + + +@ % sect. 53 +After we have read the introducing line for a program part +which is signaled with |state=begin_prog| we change the +state to |inner_prog| to start copy the next line; + +@< Copy all program... @>= +begin get_line; +while not input_has_ended do + begin @< Look at the input line and store in |state| the result @>; + if state = inner_prog then put_line + else if state = begin_prog then state := inner_prog; + get_line; + end; +if state = inner_prog then err_print('! Input has ended prematurely'); +@.Input has ended...@> +end + + +@ % sect. 54 +We first define a few macros to improve the readability of +the program part behind. After \.{\\beginprog} no letters +may appear and after \.{\\endprog} either the line is ended +or white space is to be there. The comparison is facilitated +by the fact that |buffer[limit]=" "|, i.e.\ the last +character of a line is always a space. + +@d cmp_prog(#) == (buffer[#]="p") and (buffer[#+1]="r") and + (buffer[#+2]="o") and (buffer[#+3]="g") +@d cmp_begin == (buffer[1]="b") and (buffer[2]="e") and (buffer[3]="g") and@| + (buffer[4]="i") and (buffer[5]="n") and cmp_prog(6) and@| + ((buffer[10]<"A") or (buffer[10]>"Z")) and@| + ((buffer[10]<"a") or (buffer[10]>"z")) +@d cmp_end == (buffer[1]="e") and (buffer[2]="n") and (buffer[3]="d") and + cmp_prog(4) and @| + ((buffer[8]=" ") or (buffer[8]=tab_mark)) + +@< Look at the input... @>= +begin +if buffer[0] = "\" then + if limit >= 10 then + begin @+ if cmp_begin then state := begin_prog; @+ end + else if limit >= 8 then + if cmp_end then state := out_of_prog; +end + + +@ % sect. 55 +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} + + + + + +@* System-dependent changes. % sect. 56 + +\noindent This module should be replaced, if necessary, by +changes to the program that are necessary to make +\MAKEPROG{} work at a particular installation. It is +usually best to design your change file so that all changes +to previous modules preserve the module numbering; then +everybody's version will be consistent with the printed +program. More extensive changes, which introduce new +modules, can be inserted here; then only the index itself +will get a new module number. +@^system dependencies@> + + + + + +@* Index. % sect. 57 + +\noindent Here is a cross-reference table for the +\MAKEPROG{} processor. All modules in which an identifier +is used are listed with that identifier, except that +reserved words are indexed only when they appear in format +definitions, and the appearances of identifiers in module +names are not indexed. Underlined entries correspond to +where the identifier was declared. Error messages and a few +other things like ``system dependencies'' are indexed here +too. + diff --git a/support/makeprog/src/progdoc.doc b/support/makeprog/src/progdoc.doc new file mode 100644 index 0000000000..d85bd0ca9c --- /dev/null +++ b/support/makeprog/src/progdoc.doc @@ -0,0 +1,668 @@ +% progdoc.doc 21 Sep 90 +%------------------------------------------------------------ +% (c) 1988-1990 by J.Schrod. Copyright conditions see below. + +% +% Macro package for the documentation of programs (and TeX macros) +% MAKEPROG is needed +% + +% +% VERSION HISTORY (MSCF -- most significant change first) +% +% DATE WHO REMARK +% 90-09-21 js table of contents and headline added. +% 89-09-08 js repaired initialization of \char_count. +% 89-06-30 js Definition of \beginverbatim/\endverbatim. +% 88-12-05 js first version (for ftp/Bitnet). + +% bug reports to: +% +% Detig$\,\cdot\,$Schrod \TeX{}sys +% Joachim Schrod +% Kranichweg 1 +% +% D-6074 R\"odermark-Urberach +% FR Germany +% +% Tel. (+6074) 1617 +% Bitnet: XITIJSCH@DDATHD21 + +% author: +% js: Joachim Schrod <xitijsch@ddathd21.bitnet> +% contributors: +% (-: place enough for your name! :-) + + +% documented with itself... +\input progdoc + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% local macros +% + +\font\mc=cmr9 % for names like ASCII +\font\sc=cmcsc10 % caps and small caps 10pt + +\def\WEB{{\tt WEB\/}} +\def\LaTeX{{\rm L\kern-.36em\raise.3ex\hbox{\sc a}\kern-.15em\TeX}} +\def\GNU{{\mc GNU}} + +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + + + +\title{{\tt progdoc} --- A {\tt WEB\/}-like Markup} + + + + + +\chap Introduction. + +\WEB{} systems allow the documentation of programs by supporting the +separation in program fragments which can be collected and rearranged +afterwards. This allows {\it top-down\/} programming as well as the +{\it bottom-up\/} design of programs. Each program fragment can be +documented, usually with \TeX{}. The creation of a new \WEB{} system +has become easy with the introduction of Spider \WEB{} but even within +Spider \WEB{} the lexical analysis is bound to languages of the Algol +family. Building up \WEB{} systems for languages which are line +oriented or even for ``exotic'' programming languages like \TeX{} +(with a dynamic lexical, syntactical and semantical structure) remains +to be difficult. + +This macro package was built to allow good documentation for programs +in languages for which \WEB{} does not exist. It separates a program +text in sections that can be documented. All sections, collected +sequentially, will result in the complete program. In each section +begin and end of the program part are marked with |\beginprog| and +|\endprog|, this program part will be formatted as is input +(``verbatim''). + +The separation of the sections is done in this version (which is +based on Plain~\TeX{}) by the macros |\chap| and |\sect|. In the +documentation part of a section text pieces, e.g.\ names of +variables, can be inserted verbatim to demonstrate the connection to +the program text. These parts are included in vertical bars +(`{\tt\vbar}') while the vertical bar itself can be obtained by +|\origvert|. Longer text parts can be printed verbatim by enclosing +them in |\beginverbatim| and |\endverbatim| which behaves like +|\beginprog| and |\endprog| except that the text between is not +copied to the program file. + +This macro package does not offer the creation of an index because it +is not known which syntactical tokens the language has that should be +mentioned in an index. As well the creation of a table of contents +and of headings would be preferable to allow better orientation in the +document. Some remarks about future enhancements which should be done +urgently are set in slanted type. + + +\sect This program 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~1, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +{\bf without any warranty\/}; without even the implied warranty of +{\bf merchantability\/} or {\bf 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 this program; if not, write to the Free Software Foundation, +Inc., 675~Mass Ave, Cambridge, MA~02139, USA. + + +\sect We have to realize three parts: (1)~the formatting of rather +small verbatim texts in a line, (2)~the formatting of larger parts of +program and (3)~the document structuring elements for the separation +of the sections. + +But before we start we declare some shorthands for category codes. By +declaring the at sign~(`|@|') as well as the underscore~`(|_|)' as +letters we can use them in our macros. (I agree with D.~Knuth that +|\identifier_several_words_long| is more readable than +|\IdentifierSeveralWordsLong| and in every case better than |\p@@@s|.) +With the at sign we can use the ``private'' Plain macros and with the +underscore we can make our own macros more readable. But as we have +to restore these category codes at the end of this macro file we store +their former values in the control sequences |\atcode| and |\uscode|. +This method is better than to use a group because not all macros have +to be defined global this way. + +\beginprog +\chardef\escape=0 +\chardef\subscript=8 +\chardef\letter=11 +\chardef\other=12 +%\chardef\active=13 % is defined in Plain already + +\chardef\atcode=\catcode`\@ +\chardef\uscode=\catcode`\_ + +\catcode`\@=\letter +\catcode`\_=\letter +\endprog + + + + +\chap Local Verbatim Formatting. + +The main point of every verbatim formatting is the switching of the +character codes of all characters that have a special meaning for +\TeX{}. This can be done with the control sequence |\dospecials| that +applies the control sequence |\do| to all special characters. +Additionally, every line is regarded as a paragraph without +indentation. Between two paragraphs, i.e.\ between two lines, no +extra space is set. Finally all blanks and tab characters shall be +obeyed and the inter word space after sentence terminators shall not +be enlarged. The activation of the tab character expansion with +|\obeytabs| is analog to |\obeyspaces| in {\tt plain.tex\/}. + +Verbatim texts are set in monospace, we use (like in \WEB{}) the +character set with the extended {\mc ASCII}. + +\beginprog +\font\tentex=cmtex10 % typewriter extended ASCII 10pt +\let\ttex=\tentex % only for PLAIN with base size 10pt + +\def\setup_verbatim{% + \def\do##1{\catcode`##1\other}\dospecials + \parskip\z@skip \parindent\z@ + \obeylines \obeyspaces \obeytabs \frenchspacing + \ttex + } + +\let\tab=\space +\begingroup + \catcode`\^^I=\active% % Attention: no tabs! + \gdef\obeytabs{\catcode`\^^I=\active\def^^I{\tab}} + \global\let^^I=\tab% % if an active tab appears in a \write +\endgroup +\endprog + +\sect After having saved the old meaning of `{\tt\vbar}' in +|\origvert| and after declaring |\vbar| as a synonym for the character +that has the code of a vertical bar in the current font, the vertical +bar can be made active. Then we call |\setup_verbatim|. But the +newline characters shall not be processed, they shall be treated as +blank space. This can be done by defining |\par| as |\space|. + +The next vertical bar in the input closes the group which becomes an +(unbreakable) |\hbox| then. The old meanings of the special +characters and of the vertical bar are restored and \TeX{} is in +normal (horizontal) mode again. + +\beginprog +\let\origvert=| +\chardef\vbar=`\| + +\catcode`\|=\active + +\def|{% + \leavevmode + \hbox\bgroup + \let\par\space \setup_verbatim + \let|\egroup + } +\endprog + + + + +\chap Program Fragments in Verbatim. + +We need macros to format the program fragments without any +linebreaking. Such a text area shall start with the macro +|\beginprog| resp.\ |\beginverbatim| and end with |\endprog| +resp.\ |\endverbatim|. The end macros must stand +at the very beginning of a line and must be followed by white space +(blank, tab or newline character). After all these macros +the rest of the line is ignored. + +Two demands must be regarded: There should be no length restrictions +for the processed text, and the tab characters should be expanded +so that this macro works on PC's and on VAXes, too. + + +\sect The implementation method is quite simple: We read the next +line, test, wether the end is reached (by comparing with the end line) +and otherwise set the line actually read. Every character is +inspected and tabular characters are expanded. + +The verbatim text is started with |\begin_verbatim| which will be +either called by |\beginprog| or by |\beginverbatim|. These macros +will also define the contents of the end line. + +Whether a line is set or whether the end of the processed area is +reached is indicated by the switch |\if@print|. At the beginning of +the |\begin_verbatim| macro most settings are done with +|\setup_verbatim| (the vertical bar must be handled separately) +and the rest of the line is ignored. As everything is done within +a group, the end of the verbatim text can be processed by simply +closing this group. + +For the user it looks as if |\endprog| or |\endverbatim| +terminates the processing, but +it just serves for the identification of the end, the true processing +is done with the internal macro |\end_verbatim|. + +\beginprog +\newif\if@print + +\def\begin_verbatim{% + \endgraf + \bigbreak + \begingroup + \setup_verbatim \catcode`\|\other + \@printtrue + \ignore_rest_line + } +\let\end_verbatim=\endgroup % internal command ! +\endprog + + +\sect The first line is ignored, all the other lines are identified +with |\set_next_line| and processed with |\do_set|. This separation +in identification and processing allows that the line end character is +active in the definition only for a short time. + +When a line is to be formatted, we first check with |\check_print| +whether it contains |\endprog|, otherwise it is printed with +|\print_char|. The printing must be done for every character +individually because we want to check for tab characters; the +exact algorithm is described below. Here we just have to note that +|\print_char| is used with two parameters of which the second one is +terminated with the token |\end_line|. The first parameter is the first +character of the line, the second parameter is the rest of the line. +If the line is empty, the argument of |\do_set| is empty, too; so the +activation of |\print_char| must be finished with two |\end_line|. +Then the first |\end_line| is the first argument for |\print_char| and +the second argument is empty. But if the line did contain something, +the second |\end_line| is evaluated, for this case it is defined as +|\relax|. + +Finally we call |\set_next_line| again to format the next line. If +the end is reached, i.e.\ if a line with |\endprog| was found, +|\set_next_line| is redefined as |\relax|. This can be done because +the original meaning is restored when the group is closed by +|\end_verbatim|. + +\beginprog +\begingroup + \obeylines% % ^^M is active! ==> every line must end with % + \gdef\ignore_rest_line#1^^M{\set_next_line}% + \gdef\set_next_line#1^^M{\do_set{#1}}% +\endgroup + +\def\do_set#1{% + \endgraf + \check_print{#1}% + \if@print \indent \print_char#1\end_line\end_line + \else \let\set_next_line\end_verbatim + \fi + \set_next_line + } +\let\end_line=\relax +\endprog + + +\sect Before we attack the problem of formatting a line, we declare +|\check_print| that checks the end of the verbatim mode. We have to +do two things: we must split everything in front of the first blank +or tab character and compare it with |\endprog|. The splitting is easy +because the line which is our first argument contains blanks and +tabulators as active characters. First we call |\cut_at_tab| that +demands a tab character as separator for its two parameters so that +everything in the line in front of the first tabulator is part of the +first parameter. If there is no tab character in the line, we append +one so that the second parameter is empty. The same trick is used to +separate the part in front of the first blank character from the +resulting first part. + +The check is done with |\do_check|. We use a separate macro here so +that we can indent it (in the following definition blanks are active!) + +\beginprog +\begingroup +\obeyspaces\obeytabs +\gdef\check_print#1{\cut_at_tab#1^^I\end_line} +\gdef\cut_at_tab#1^^I#2\end_line{\check_first_part#1 \end_line}% blank ! +\gdef\check_first_part#1 #2\end_line{\do_check{#1}} +\endgroup +\endprog + + +\sect |\do_check| compares the line with a sample line that is +available in |\end_verbatim_line|. This macro will be defined later. + +\beginprog +\def\do_check#1{% + \def\@line{#1}% + \ifx \@line\end_verbatim_line \@printfalse + \fi + } +\endprog + + +\sect Now we can set a line: we start with the first character, +followed by the rest of the line. Each character is counted in +|\char_count|. At the beginning of a line |\char_count| is~0, it +is reset at the end of the line. + +\beginprog +\newcount\char_count \char_count\z@ + +\def\print_char#1#2\end_line{% + \print_first_char{#1}% + \print_rest_of_line{#2}% + } +\endprog + + +\sect For each character that is set |\char_count| is incremented. +If a character is a tabulator, we set with |\print_tab| the required +amount of blank characters, otherwise the character itself. We must +compare the character that is stored in |\@char| with a macro whose +``first-level'' expansion is an active tabulator. For this case we +declare |\@tab|. + +\beginprog +{\obeytabs\gdef\@tab{^^I}} + +\def\print_first_char#1{% + \def\@char{#1}% + \advance \char_count\@ne + \ifx \@char\@tab \print_tab + \else \@char + \fi + } +\endprog + + +\sect If we want to fill the line with blank spaces up to the next +column with a number that can be divided by~8, we must be able to +compute the column number modulo~8, but \TeX{} has no modulo operator. +So we define the macro |\mod_viii| that computes its argument modulo~8 +and returns the result in the counter |\count_mod_viii|. For the +computation we use the temporary counter |\count@|. + +\beginprog +\newcount\count_mod_viii +\def\mod_viii#1{% + \count@ #1\relax \count_mod_viii\count@ + \divide \count@ 8\relax + \multiply \count@ 8\relax + \advance \count_mod_viii -\count@ + } +\endprog + + +\sect Now we can declare |\print_tab|. We must remember that +|\char_count| was incremented already, if we set only one blank +character the counter keeps untouched. + +\beginprog +\def\print_tab{% + \loop \space \mod_viii\char_count + \ifnum \count_mod_viii>\z@ + \advance \char_count\@ne + \repeat + } +\endprog + + +\sect If the rest of the line is empty, we have finished the task. +|\char_count| is reset to~0 for the next line. + +Inside the |\else| part of |\ifx| |\print_char| should not be used +directly because this costs too much storage of \TeX{}. Instead we +set a control sequence |\next| that is processed afterwards, depending +on the result of the comparison. If there is still something to set, +we use |\print_char| again, otherwise a syntactically similar macro +that expands to |\relax|. + +\beginprog +\def\print_rest_of_line#1{% + \def\@line{#1}% + \ifx \@line\empty \char_count\z@ + \def\next##1\end_line{\relax}% + \else \let\next\print_char + \fi + \next#1\end_line + } +\endprog + + +\sect Now we are ready to define the two ``user accessible'' macros +|\beginprog| and |\beginverbatim|. They must define the prototyp end +line |\end_verbatim_line| which will be compared against every line +in the verbatim text. During the definition of +|\end_verbatim_line| it must be cared for that the escape character~`|\|' +is a printable character: A comparison with |\ifx| demands identical +category codes. As a temporary escape character we use the slash. + +\beginprog +{\catcode`\/=\escape \catcode`\\=\other % / is temporary escape char + /gdef/beginprog{% + /gdef/end_verbatim_line{\endprog}% + /begin_verbatim + } + /gdef/beginverbatim{% + /gdef/end_verbatim_line{\endverbatim}% + /begin_verbatim + } +} % here \endgroup can't be used +\endprog + + + + +\chap Document Structuring. + +The layout of the document shall be like in \WEB{}; this can be done +easily in Plain. A document -- which may have a title -- consists of +sections which are numbered. We distinguish between main sections +which start a group of sections and between normal sections within a +group. Main sections have a title, a table of contents with all +titles is output at the end. + + +\sect The number of the current section is stored in the counter +|\sectno|. + +\beginprog +\newcount\sectno \sectno=\@ne + +\newtoks\title \title{} +\endprog + + +\sect As usual, the name |\toc_file_name| of the table of contents +file |\toc_file| is built from the jobname and the extension |toc|. +We open the file and output a |\relax| so we are save that something +is in the file and it can be read in later without any difficulties. + +In this file lines are written with the main section numbers, their +titles, and their page numbers, respectively. Every line is marked +with |\tocline| which is assumed to have the given three parameters. +Now we define |\tocline| to be |\relax| so that |\write| does not +expand it. + +\beginprog +\def\toc_file_name{\jobname.toc } % <--- Space!! +\newwrite\toc_file + +\immediate\openout\toc_file=\toc_file_name +\immediate\write\toc_file{\relax} + +\let\tocline=\relax +\endprog + + +\sect We want a headline with the current section number, the +document title, the last main section title, and the page number. The +footline is empty. + +To realize this headline every section must |\mark| it's section +number {\it in front of\/} a potentiel page break (so the number may +be accessed by |\topmark|) and every main section must define +|\main_title| to be it's title. + +\beginprog +\headline{% + \tenrm + \S\topmark \qquad \the\title + \hfil + \main_title \qquad \tenrm \folio + } +\footline{\hfil} +\endprog + + +\sect But this does not work the way it should: On the first page +|\topmark| will be always empty! Here we borrow a trick from +|webmac.tex|, we insert a first garbage page which will be discarded +by the output routine. + +\beginprog +\vbox to \vsize{} + +\output={% + \setbox\z@ \vbox{\unvbox\@cclv}% % throw away the first page + \global\output{\plainoutput}% % restore Plain output routine + } +\endprog + + +\sect The main sections are started with the macro |\chap|. It has +one parameter, the title of the section group. This parameter must +be terminated by a dot. We start a new page, typeset the title in +bold face and separate it from the section text with a |\medskip|. +This text, the first paragraph of the documentation part of the +section, is formatted without paragraph indentation. + +At the beginning of the new page we output the section number, the +title, and the page number to the contents file. We must take care to +expand the section number right now because it will be incremented +and would not be the correct value during the output routine. + +The deletion of the paragraph indentation is done with |\everypar|. +We do not care if |\everypar| is used for something else and delete it +after usage. + +\beginprog +\def\chap#1.{% + \endgraf + \mark{\number\sectno}% % gather information for headline + \vfill\supereject + \begingroup + \def\protect##1{\string ##1 }% % <--- Space!! + \edef\write_toc_sectno{\write\toc_file{\tocline{\number\sectno}}}% + \write_toc_sectno + \write\toc_file{{#1}{\folio}}% + \endgroup + \gdef\main_title{#1}% % more information for headline + \noindent {\bf \number\sectno.\quad #1.}% + \global\advance \sectno\@ne + \endgraf + \medskip \nobreak + \everypar{% % remove next parindent + \setbox0\lastbox + \global\everypar{}% + }% + } +\endprog + + +\sect Normal sections are started with |\sect|. This macro has no +parameter. Between two paragraphs we set 2~pica space (if possible) +and prefer the page breaking. Between the section number and the text +one quad of space is set. + +\begingroup \sl +The page break manipulation should be more sophisticated. +\endgroup + +\beginprog +\def\sect{% + \endgraf + \mark{\number\sectno}% % gather information for headline + \vskip 2pc plus 1pc minus 6dd \goodbreak + \noindent {\bf \number\sectno.}\quad \ignorespaces + \global\advance \sectno\@ne + } +\endprog + + +\sect At the end we add a page with the table of contents. This is +triggered by |\bye|. If the user does not want this table he may use +|\byebye|. + +\beginprog +\def\byebye{\par\vfil\supereject\end} % original definition of \bye +\def\bye{% + \endgraf + \vfill \supereject + \immediate\closeout\toc_file + \message{Table of contents:}% + \leftskip\z@skip \rightskip\z@skip + \parskip\z@skip \parindent\z@ \parfillskip\z@skip + \normalbaselines + \headline{\hfil}% + \footline{\hfil \folio \hfil}% + \def\HeaderToc{\rightline{Section\hbox to\thr@@ em{\hss Page}}}% + \def\tocline##1##2##3{% + \line{% + \ignorespaces ##2% + \leaders\hbox to .5em{.\hfil}\hfil + \ ##1% + \hbox to \thr@@ em{\hss ##3}% + }% + } + \pageno\m@ne % table of contents gets roman page numbers + \topofcontents % may redefine \HeaderToc and \tocline! + \HeaderToc + \catcode`\@\letter % @ and _ are letters (for reading private macros) + \catcode`\_\letter + \input \toc_file_name + \catcode`\@\atcode % @ and _ are no more letters + \catcode`\_\uscode + \botofcontents + \byebye % print the contents page(s) and terminate + } + +\font\titlefont=cmr7 scaled\magstep4 % title on the contents page +\font\ttitlefont=cmtt10 scaled\magstep2 % typewriter type in title +\def\topofcontents{ + \null \vfill + \centerline{\let\tt\ttitlefont \titlefont \the\title} + \vskip 1cm + } +\def\botofcontents{ + \vfill\vfill + } +\endprog + + +\sect We are finished and just have to restore the category codes. + +\beginprog +\catcode`\@=\atcode +\catcode`\_=\uscode + +\endinput +\endprog + + + + + +\bye + diff --git a/support/makeprog/src/progdoc.tex b/support/makeprog/src/progdoc.tex new file mode 100644 index 0000000000..da52668952 --- /dev/null +++ b/support/makeprog/src/progdoc.tex @@ -0,0 +1,217 @@ +\chardef\escape=0 +\chardef\subscript=8 +\chardef\letter=11 +\chardef\other=12 +%\chardef\active=13 % is defined in Plain already + +\chardef\atcode=\catcode`\@ +\chardef\uscode=\catcode`\_ + +\catcode`\@=\letter +\catcode`\_=\letter +\font\tentex=cmtex10 % typewriter extended ASCII 10pt +\let\ttex=\tentex % only for PLAIN with base size 10pt + +\def\setup_verbatim{% + \def\do##1{\catcode`##1\other}\dospecials + \parskip\z@skip \parindent\z@ + \obeylines \obeyspaces \obeytabs \frenchspacing + \ttex + } + +\let\tab=\space +\begingroup + \catcode`\^^I=\active% % Attention: no tabs! + \gdef\obeytabs{\catcode`\^^I=\active\def^^I{\tab}} + \global\let^^I=\tab% % if an active tab appears in a \write +\endgroup +\let\origvert=| +\chardef\vbar=`\| + +\catcode`\|=\active + +\def|{% + \leavevmode + \hbox\bgroup + \let\par\space \setup_verbatim + \let|\egroup + } +\newif\if@print + +\def\begin_verbatim{% + \endgraf + \bigbreak + \begingroup + \setup_verbatim \catcode`\|\other + \@printtrue + \ignore_rest_line + } +\let\end_verbatim=\endgroup % internal command ! +\begingroup + \obeylines% % ^^M is active! ==> every line must end with % + \gdef\ignore_rest_line#1^^M{\set_next_line}% + \gdef\set_next_line#1^^M{\do_set{#1}}% +\endgroup + +\def\do_set#1{% + \endgraf + \check_print{#1}% + \if@print \indent \print_char#1\end_line\end_line + \else \let\set_next_line\end_verbatim + \fi + \set_next_line + } +\let\end_line=\relax +\begingroup +\obeyspaces\obeytabs +\gdef\check_print#1{\cut_at_tab#1^^I\end_line} +\gdef\cut_at_tab#1^^I#2\end_line{\check_first_part#1 \end_line}% blank ! +\gdef\check_first_part#1 #2\end_line{\do_check{#1}} +\endgroup +\def\do_check#1{% + \def\@line{#1}% + \ifx \@line\end_verbatim_line \@printfalse + \fi + } +\newcount\char_count \char_count\z@ + +\def\print_char#1#2\end_line{% + \print_first_char{#1}% + \print_rest_of_line{#2}% + } +{\obeytabs\gdef\@tab{^^I}} + +\def\print_first_char#1{% + \def\@char{#1}% + \advance \char_count\@ne + \ifx \@char\@tab \print_tab + \else \@char + \fi + } +\newcount\count_mod_viii +\def\mod_viii#1{% + \count@ #1\relax \count_mod_viii\count@ + \divide \count@ 8\relax + \multiply \count@ 8\relax + \advance \count_mod_viii -\count@ + } +\def\print_tab{% + \loop \space \mod_viii\char_count + \ifnum \count_mod_viii>\z@ + \advance \char_count\@ne + \repeat + } +\def\print_rest_of_line#1{% + \def\@line{#1}% + \ifx \@line\empty \char_count\z@ + \def\next##1\end_line{\relax}% + \else \let\next\print_char + \fi + \next#1\end_line + } +{\catcode`\/=\escape \catcode`\\=\other % / is temporary escape char + /gdef/beginprog{% + /gdef/end_verbatim_line{\endprog}% + /begin_verbatim + } + /gdef/beginverbatim{% + /gdef/end_verbatim_line{\endverbatim}% + /begin_verbatim + } +} % here \endgroup can't be used +\newcount\sectno \sectno=\@ne + +\newtoks\title \title{} +\def\toc_file_name{\jobname.toc } % <--- Space!! +\newwrite\toc_file + +\immediate\openout\toc_file=\toc_file_name +\immediate\write\toc_file{\relax} + +\let\tocline=\relax +\headline{% + \tenrm + \S\topmark \qquad \the\title + \hfil + \main_title \qquad \tenrm \folio + } +\footline{\hfil} +\vbox to \vsize{} + +\output={% + \setbox\z@ \vbox{\unvbox\@cclv}% % throw away the first page + \global\output{\plainoutput}% % restore Plain output routine + } +\def\chap#1.{% + \endgraf + \mark{\number\sectno}% % gather information for headline + \vfill\supereject + \begingroup + \def\protect##1{\string ##1 }% % <--- Space!! + \edef\write_toc_sectno{\write\toc_file{\tocline{\number\sectno}}}% + \write_toc_sectno + \write\toc_file{{#1}{\folio}}% + \endgroup + \gdef\main_title{#1}% % more information for headline + \noindent {\bf \number\sectno.\quad #1.}% + \global\advance \sectno\@ne + \endgraf + \medskip \nobreak + \everypar{% % remove next parindent + \setbox0\lastbox + \global\everypar{}% + }% + } +\def\sect{% + \endgraf + \mark{\number\sectno}% % gather information for headline + \vskip 2pc plus 1pc minus 6dd \goodbreak + \noindent {\bf \number\sectno.}\quad \ignorespaces + \global\advance \sectno\@ne + } +\def\byebye{\par\vfil\supereject\end} % original definition of \bye +\def\bye{% + \endgraf + \vfill \supereject + \immediate\closeout\toc_file + \message{Table of contents:}% + \leftskip\z@skip \rightskip\z@skip + \parskip\z@skip \parindent\z@ \parfillskip\z@skip + \normalbaselines + \headline{\hfil}% + \footline{\hfil \folio \hfil}% + \def\HeaderToc{\rightline{Section\hbox to\thr@@ em{\hss Page}}}% + \def\tocline##1##2##3{% + \line{% + \ignorespaces ##2% + \leaders\hbox to .5em{.\hfil}\hfil + \ ##1% + \hbox to \thr@@ em{\hss ##3}% + }% + } + \pageno\m@ne % table of contents gets roman page numbers + \topofcontents % may redefine \HeaderToc and \tocline! + \HeaderToc + \catcode`\@\letter % @ and _ are letters (for reading private macros) + \catcode`\_\letter + \input \toc_file_name + \catcode`\@\atcode % @ and _ are no more letters + \catcode`\_\uscode + \botofcontents + \byebye % print the contents page(s) and terminate + } + +\font\titlefont=cmr7 scaled\magstep4 % title on the contents page +\font\ttitlefont=cmtt10 scaled\magstep2 % typewriter type in title +\def\topofcontents{ + \null \vfill + \centerline{\let\tt\ttitlefont \titlefont \the\title} + \vskip 1cm + } +\def\botofcontents{ + \vfill\vfill + } +\catcode`\@=\atcode +\catcode`\_=\uscode + +\endinput diff --git a/support/makeprog/src/progltx.doc b/support/makeprog/src/progltx.doc new file mode 100644 index 0000000000..fdc6047699 --- /dev/null +++ b/support/makeprog/src/progltx.doc @@ -0,0 +1,739 @@ +% This is PROGLTX.DOC as of 25 May 90 +%--------------------------------------------------------- +% (c) 1989,1990 by J.Schrod. copy conditions see below + +% +% Macro package for the documentation of programs +% LaTeX style option +% MAKEPROG is needed +% + +% +% DATE PERSON REMARK +% 89-11-18 js repaired the handling of `|' (verbatim and \index). +% 89-10-05 js first version (for TeX89) +% + +% author's current address: +% +% Detig$\,\cdot\,$Schrod \TeX{}sys +% Joachim Schrod +% Kranichweg 1 +% +% D-6074 R\"odermark-Urberach +% FR Germany +% +% Tel. (+6074) 1617 +% Bitnet: XITIJSCH@DDATHD21 + + + +% documented with itself... +\documentstyle[progltx]{article} + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% +% local macros +% + +\let\mc=\small % for names like GNU + +\def\WEB{{\tt WEB\/}} +\def\GNU{{\mc GNU}} + +% +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +\begin{document} + + +\title{ + Documenting programs in a \WEB{} style\\ + The {\tt progltx\/} style option + } +\author{\sc Joachim Schrod} + +\maketitle + + +\chap Introduction. + +\WEB{} systems allow the documentation of programs by supporting the +separation in program fragments which can be collected and rearranged +afterwards. This allows {\it top-down\/} programming as well as the +{\it bottom-up\/} design of programs. Each program fragment can be +documented, usually with \TeX{}\@. A disadvantadge is that \WEB{} +actually exists only for a few programming languages (Pascal, C, +Modula-2). Besides, building up \WEB{} systems for ``exotic'' +programming languages like \TeX{} is very difficult. + +This macro package was built to allow good documentation for programs +in languages for which \WEB{} doesn't exist. It separates a program +text in sections that can be documented. All sections, collected +sequentially, will result in the complete program. In every section +begin and end of the program part are marked with |\beginprog| and +|\endprog|, this program part will be formatted as it is input +(``verbatim''). + +Originally these macros were written for the usage with Plain \TeX{} +resp.\ on top of the \WEB{} macro package |webmac.tex|. But often the +requirement has been told to me that a \LaTeX{} version would be +useful, too---well, here it is. But even with \LaTeX{} I have decided +that still |\beginprog| and |\endprog| must be used for the markup of +the program parts, not |\begin{prog}| or |\end{prog}| which would be +more ``\LaTeX{}-like.'' The reason behind this is that I didn't want +to maintain two versions of the {\mc MAKEPROG\/} processor---but {\mc +MAKEPROG\/} wants to see the non-\LaTeX{}-like macros. But everybody +is encouraged to change it by themselves (it is {\it very\/} easy). + +The user of these macros may use the usual sectioning macros of +\LaTeX{} for structuring his documentation. Additionally we provide a +|progdoc|-compatible (i.e.\ \WEB{}-like) markup with the two macros +|\chap| and |\sect|. + +In the documentation part of a section text pieces, e.g.\ names of +variables, can be inserted verbatim to demonstrate the connection to +the program text. These parts are included in vertical bars +(`{\tt\vbar}') This makes this style option extremely useful if your +identifiers (or your file names) include characters which would be +special characters for \TeX{} otherwise. (One example of these +program languages is \TeX{} itself, but just enclose the macro names +in vertical bars and {\tt \vbar|\relax|\vbar}.) + +The macros |\makevertother| and |\makevertactive| are available to +activate and deactivate the special behaviour of the vertical bar but +these macros should be needed seldom because the usual cases as the +usage within |verbatim| and |tabular| environments or |\index| macros +etc.\ are handled. + +If a vertical bar must be used in the original fashion you can use two +command sequences: |\origvert| holds the meaning of the vertical bar +at the time this macro file was read in and |\vbar| is the character +with the {\mc ASCII\/} code of the vertical bar (i.e.~|"EC|) in the +current font. + +The new notion of |\verb| must not be used within parameters of +macros, e.g.\ in the argument of |\section| etc.---it will result in +an error message by \TeX{}\@. Furthermore within the preamble of a +|tabular|, an |array| environment, or the |\multicolumn| command the +vertical bar has the same meaning as before. (That means within +|@{|\ldots|}| it is an ordinary character and otherwise it specifies a +vertical rule between the columns. + +This macro package does not offer the automatic creation of an index +because it is not known which syntactical tokens the language has that +should be mentioned in an index. But of course all \LaTeX{} stuff like +|\index|, |\tableofcontents|, etc.\ may be used. + + +\sect This program 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~1, or (at your +option) any later version. + +This program is distributed in the hope that it will be useful, but +{\bf without any warranty\/}; without even the implied warranty of +{\bf merchantability\/} or {\bf 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 this program; if not, write to the Free Software Foundation, +Inc., 675~Mass Ave, Cambridge, MA~02139, USA. + + +\sect We have to realize three parts: (1)~the formatting of rather +small verbatim texts in a line, (2)~the formatting of larger parts of +program and (3)~the document structuring elements for the separation +of the sections. + +\begin{sloppypar} +Before we start we declare some shorthands for category codes. By +declaring the underscore~`(|_|)' as a letter we can use it in our +macros names. (I agree with D.~Knuth that +|\identifier_several_words_long| is more readable than +|\IdentifierSeveralWordsLong| and in every case better than +|\p@@@s|.) But as we have to restore the category codes at the end +of this macro file we store its former value in the control +sequence |\uscode|. This method is better than the usage of a +group because not all macros have to be defined global this way. +\end{sloppypar} + +\beginprog +\chardef\escape=0 +\chardef\open=1 +\chardef\close=2 +\chardef\letter=11 +\chardef\other=12 +%\chardef\active=13 % is defined in Plain already + +\chardef\uscode=\catcode`\_ + +\catcode`\_=\letter +\endprog + + + + +\chap Local Verbatim Formatting. + +The main point of every verbatim formatting is the switching of the +character codes of all characters that have a special \TeX{} meaning. +This can be done with the control sequence |\dospecials| that applies +the control sequence |\do| to all special characters. Additionally, +every line is regarded as a paragraph without indentation. Between +two paragraphs, i.e.\ between two lines, no extra space is set. +Finally all blanks and tabular characters shall be obeyed and the +inter word space after sentence terminators shall not be enlarged. +The activation of the tabular characters with |\obeytabs| is +equivalent to |\obeyspaces| in {\tt plain.tex}. + +\begin{sl} +As a matter of fact, I would like to use the character set with the +extended {\mc ASCII} (like in\/ \WEB{}) for setting the verbatim texts +in monospace. But then I must code many\/ |\@getfont|'s\,\dots +\end{sl} + +\beginprog +%\font\tentex=cmtex10 % typewriter extended ASCII 10pt +%\let\ttex=\tentex % only with base size 10pt +\def\ttex{\tt} % as a substitute + +\def\setup_verbatim{% + \def\do##1{\catcode`##1\other}\dospecials + \parskip\z@skip \parindent\z@ + \catcode`\`\active \@noligs + \obeylines \@vobeyspaces \obeytabs \frenchspacing + \ttex + } + +\let\tab=\space +\begingroup + \catcode`\^^I=\active% % Attention: no tabs! + \gdef\obeytabs{\catcode`\^^I=\active\def^^I{\tab}} + \global\let^^I=\tab% % if an active tab appears in a \write +\endgroup +\endprog + + +\sect After having saved the old meaning of `{\tt\vbar}' in +|\origvert| and after declaring |\vbar| as a synonym for the character +that has the code of a vertical bar in the actual font, the vertical +bar can be made active. Then we call |\setup_verbatim|. But the +newline characters shall not be processed, they shall be regarded like +blank space. This can be reached by defining |\par| as |\space|. + +The next vertical bar in the input closes the group which becomes an +(unbreakable) |\hbox| then. The old meanings of the special +characters and of the vertical bar are restored and \TeX{} is in +normal (horizontal) mode again. + +\beginprog +\let\origvert=| +\chardef\vbar=`\| + +\def\makebaractive{\catcode`\|\active} +\def\makebarother{\catcode`\|\other} +\makebaractive + +\def|{% + \leavevmode + \hbox\bgroup + \let\par\space \setup_verbatim + \let|\egroup + } +\endprog + + +\sect A problem with this definition of the vertical bar is that +the bar is not a normal character any more but there exists situations +where the \LaTeX{} macros assumes this: +% +\begin{itemize} + +\item In a |verbatim| environment a vertical bar must be typeset if it +occurs in the input. + +\item In a |tabular| or an |array| environment a vertical bar is used +to denote rules between columns in the table. These environments must +be started with a parameter which is a ``preamble,'' the same preamble +construction is used to specify the format of multi-column entries. + +\item In an output of an index entry the vertical bar must not result +in an error message. Usually index entries are typeset in a seperate +\TeX{} run where |idverb| is not used as a style option and the +vertical bar is therefore useful. This is no problem in section +headings or captions etc. Because they take their argument as a +parameter in every case the vertical bar can never be used there. But +|\origvert| resp.\ |\vbar| may be used there. + +\end{itemize} +% +In the next sections we handle each of these problems. + + +\sect Before we start a |verbatim| environment we just redefine the +vertical bar as an ordinary character. Because this is within the +environment grouping the end of the environment will reestablish the +special meaning. + +\beginprog +\let\@@verbatim=\@verbatim +\def\@verbatim{% + \makebarother + \@@verbatim + } +\endprog + + +\sect Special care is needed in the |tabular| and the |array| +environment. Both environments are begun by one macro (|\@tabarray|), +we redefine it so that the bar can be used in the preamble +specification. The same problem occurs in |\multicolumn|, it is +solved the same way. After the preamble construction (with +|\@mkpream|) the special meaning of the bar can be switched on +again---this allows the usage of the new meaning in the body of a +table. + +Of course this mean that the ``verbatim identifier'' facility can +not be used within a preamble of a table. Furthermore it can not +be used within the |\multicolumn| statement, neither in the +preamble part (the second parameter) nor within the text part (the +third parameter). + +\beginprog +\let\@@tabarray=\@tabarray +\def\@tabarray{% + \makebarother + \@@tabarray + } + +\def\multicolumn#1{% + \multispan{#1}% + \begingroup + \makebarother + \restof_multicolumn + } +\def\restof_multicolumn#1#2{% + \@mkpream{#1}% + \def\@sharp{#2}% + \let\protect\relax + \let\@startpbox\@@startpbox \let\@endpbox\@@endpbox + \@arstrut \@preamble + \endgroup + \ignorespaces + } + +\let\@@mkpream=\@mkpream +\def\@mkpream#1{% + \@@mkpream{#1}% + \makebaractive + } +\endprog + + +\sect Before an index entry is scanned almost all special characters +are transformed into ordinary characters. The only exceptions are the +opening and the closing brace because they are needed to delimit the +argument. This transformation is done with |\@sanitize|, this macro +must be called within a group. We just append the transformation of +the vertical bar. + +\beginprog +\begingroup + \def\@makeother{\noexpand\@makeother\noexpand} + \xdef\@sanitize{\@sanitize\@makeother\|} +\endgroup +\endprog + + + + +\chap Fragments in Verbatim. + +We need macros to format the program fragments without any +linebreaking. Such a text area shall start with the macro +|\beginprog| and end with |\endprog|, i.e.\ as a kind of a +|prog|-environment. The macro |\endprog| must stand at the very +beginning of a line and must be followed by white space (blank, tab or +newline character). After |\beginprog| as well as after +|\endprog| the rest of the line is ignored. + +Two demands must be regarded: There should be no length restrictions +for the processed text, and the tabular characters should be expanded +so that this macro works on PC's and on VAXes etc., too. + + +\sect The implementation method is quite simple: We read the next +line, test, wether the end is reached (by comparing with the end line) +and otherwise set the line actually read. Every character is +inspected and tabular characters are expanded. + +The verbatim text is started with |\begin_verbatim| which will be +either called by |\beginprog| or by |\beginverbatim|. These macros +will also define the contents of the end line. + +Whether a line is set or whether the end of the processed area is +reached is indicated by the switch |\if@print|. At the beginning of +the |\begin_verbatim| macro most settings are done with +|\setup_verbatim| (the vertical bar must be handled separately) +and the rest of the line is ignored. As everything is done within +a group, the end of the verbatim text can be processed by simply +closing this group. + +For the user it looks as if |\endprog| or |\endverbatim| +terminates the processing, but it just serves for the +identification of the end, the true processing is done with the +internal macro |\end_verbatim|. + +\beginprog +\newif\if@print + +\def\begin_verbatim{% + \endgraf + \bigbreak + \begingroup + \setup_verbatim \makebarother + \@printtrue + \ignore_rest_line + } +\let\end_verbatim=\endgroup % internal command ! +\endprog + + +\sect {\sloppy + The first line is ignored, all the other lines are identified with +|\set_next_line| and processed with |\do_set|. This separation in +identification and processing allows that the line end character is +active in the definition only for a short time. + \par} + +When a line is to be formatted, we first check with |\check_print| +wether it contains the end line, otherwise it is printed with +|\print_char|. The printing must be done for every character +individually because we want to check for tabular characters; the +exact algorithm is described below. Here we just have to note that +|\print_char| is used with two parameters of which the second one is +finished with the token |\end_line|. The first parameter is the first +character of the line, the second parameter is the rest of the line. +If the line is empty, the argument of |\do_set| is empty, too; so the +activation of |\print_char| must be finished with two |\end_line|. +Then the first |\end_line| is the first argument for |\print_char| and +the second argument is empty. But if the line did contain something, +the second |\end_line| is evaluated, for this case it is defined as +|\relax|. + +At last we call |\set_next_line| again to format the next line. If +the end is reached, i.e.\ if the sample line was found, +|\set_next_line| will be redefined as |\relax|. This can be done +because the original meaning is restored while closing the group +with |\end_verbatim|. + +\beginprog +\begingroup + \obeylines% % ^^M is active! ==> every line must end with % + \gdef\ignore_rest_line#1^^M{\set_next_line}% + \gdef\set_next_line#1^^M{\do_set{#1}}% +\endgroup + +\def\do_set#1{% + \endgraf + \check_print{#1}% + \if@print \indent \print_char#1\end_line\end_line + \else \let\set_next_line\end_verbatim + \fi + \set_next_line + } +\let\end_line=\relax +\endprog + + +\sect {\sloppy + Before we look at the problem of formatting a line, we declare +|\check_print| that checks the end of the verbatim mode. We have to +do two things: we must split everything in front of the first blank +or tabular character and compare for identity with |\endprog|. The +splitting is easy because the line which is our first argument +contains blanks and tabulators as active characters. First we call +|\cut_at_tab| that demands a tabular character as separator for its +two pramenters so that everything in the line in front of the first +tabulator is part of the first parameter. If there is no tabular +character in the line, we append one so that the second parameter is +empty. The same trick is used to separate the part in front of the +first blank character from the resulting first part. + \par} + +The check is done with |\do_check|. We use a separate macro here so +that we can indent it (in the following definition blanks are active!) + +\beginprog +\begingroup +\obeyspaces\obeytabs +\gdef\check_print#1{\cut_at_tab#1^^I\end_line} +\gdef\cut_at_tab#1^^I#2\end_line{\check_first_part#1 \end_line}% blank ! +\gdef\check_first_part#1 #2\end_line{\do_check{#1}} +\endgroup +\endprog + + +\sect +\begin{sloppypar} +|\do_check| compares the line with a sample line that is +available in |\end_verbatim_line|. This macro will be defined later. +\end{sloppypar} + +\beginprog +\def\do_check#1{% + \def\@line{#1}% + \ifx \@line\end_verbatim_line \@printfalse + \fi + } +\endprog + + +\sect Now we can set a line: we start with the first character, +followed by the rest of the line. Each character is counted in +|\char_count|. At the beginning of a line |\char_count| is~0, this +is reset at the end of the line. + +\beginprog +\newcount\char_count \char_count\z@ + +\def\print_char#1#2\end_line{% + \print_first_char{#1}% + \print_rest_of_line{#2}% + } +\endprog + + +\sect For each character that is set |\char_count| is incremented. +If a character is a tabulator, we set with |\print_tab| the fitting +amount of blank characters, otherwise the character itself. We must +compare the character that is stored in |\@char| with a macro of which +the ``first-level'' expansion is an active tabulator. For this case +we declare |\@tab|. + +\beginprog +{\obeytabs\gdef\@tab{^^I}} + +\def\print_first_char#1{% + \def\@char{#1}% + \advance \char_count\@ne + \ifx \@char\@tab \print_tab + \else \@char + \fi + } +\endprog + + +\sect If we want to fill the line with blank spaces up to the next +column with a number that can be divided by~8, we must be able to +compute the column number modulo~8, but \TeX{} has no modulo operator. +So we define the macro |\mod_viii| that computes its argument modulo~8 +and returns the result in the counter |\count_mod_viii|. For the +computation we need the temporary counter |\count@|. + +\beginprog +\newcount\count_mod_viii +\def\mod_viii#1{% + \count@ #1\relax \count_mod_viii\count@ + \divide \count@ 8\relax + \multiply \count@ 8\relax + \advance \count_mod_viii -\count@ + } +\endprog + + +\sect Now we can declare |\print_tab|. We must remember that +|\char_count| was incremented already, if we set only one blank +character the counter keeps untouched. + +\beginprog +\def\print_tab{% + \loop \space \mod_viii\char_count + \ifnum \count_mod_viii>\z@ + \advance \char_count\@ne + \repeat + } +\endprog + + +\sect If the rest of the line is empty, we are ready. |\char_count| +is reset to~0 for the next line. + +Inside the |\else| part of |\ifx| |\print_char| should not be used +directly because this costs too much storage of \TeX{}\@. Instead we +set a control sequence |\next| that is processed afterwards, depending +on the result of the comparison. (This tail recursion will be +discovered by \TeX{} and handled appropriately.) If there is still +something to set, we use |\print_char| again, otherwise a +syntactically similar macro that expands to |\relax|. + +\beginprog +\def\print_rest_of_line#1{% + \def\@line{#1}% + \ifx \@line\empty \char_count\z@ + \def\next##1\end_line{\relax}% + \else \let\next\print_char + \fi + \next#1\end_line + } +\endprog + + +\sect {\sloppy +Now we are ready to define the two ``user accessible'' macros +|\beginprog| and |\beginverbatim|. They must define the prototyp end +line |\end_verbatim_line| which will be compared against every line +in the verbatim text. During the definition of +|\end_verbatim_line| it must be cared for that the escape character~`|\|' +is a printable character: A comparison with |\ifx| demands identical +category codes. As a temporary escape character we use the slash. +\par} + +\beginprog +{\catcode`\/=\escape % / is temporary escape char + \catcode`\\=\other + /gdef/beginprog{% + /gdef/end_verbatim_line{\endprog}% + /begin_verbatim + } + /gdef/beginverbatim{% + /gdef/end_verbatim_line{\endverbatim}% + /begin_verbatim + } +} % here \endgroup can't be used +\endprog + + + + +\chap Document Structuring. + +In addition to the normal \LaTeX{} structuring markups we will +provide a markup for a layout of the document that is like in +\WEB{}. This can be done easily. All sections are numbered, the +number of the next section is stored in the counter |section|. We +distinguish between main sections which start a group of sections +and between normal sections within a group. + +The main sections are started with the macro |\chap|. It has one +parameter, the title of the section group. This parameter must be +terminated by a dot. We start a new page, typeset the title in +bold face and separate it from the section text with a |\medskip|. +This text, the documentation part of the section, is formatted +without paragraph indentation. + +If the \WEB{}-like macros are used every section number should be +output with a following dot. We want to establish this when we use the +|\chap| or the |\sect| macro the first time. Furthermore the new page +at the begin of a main section should not be started at the first +|\chap| because a title may precede it (and the title should not +be on a seperate page unless explicitely requested). Instead a +skip of (approximately) 2~pica should be set. To achieve +both goals a macro |\chap_intro| is defined that defines |\thesection| +appropriately, skips the 2~pica, and redefines itself to +|\newpage|---we then just have to call |\chap_intro| at the beginning +of |\chap|. The same applies to |\sect_intro|. + +\beginprog +\newskip\pre_sect_skip \pre_sect_skip=2pc plus 1pc minus 6pt + +\def\chap_intro{% + \gdef\thesection{\arabic{section}.}% + \gdef\chap_intro{\newpage}% + \addvspace{\pre_sect_skip}% + } + +\def\sect_intro{% + \gdef\thesection{\arabic{section}.}% + \global\let\sect_intro\relax + } +\endprog + + +\sect Now we will have a look at the {\it wonderful\/} internal +interface of \LaTeX{}\@. With ``wonderful'' I mean that I'm always +full of wonder why it was designed in this way. E.g., if anybody +can explain to me why the coding of the non-indentation of the first +line in the following paragraph was combined with the coding of +the skip which will come in front of the section---I will be thankful +for the rest of my life. Such different things should be kept +different, i.e., the suppression of the indentation should not be +hidden in the sign of a skip! The same comment applies to the combined +coding of the skip below the section heading resp.\ to the right +of a run-in heading. + +\begin{quote} \it +I want the following layout: no indentation in the following +paragraph\/ {\rm but no} space in front of the section heading. +How do I code a value of\/ $\sl -0 pt$? +\end{quote} +% +Well, a work-around is to use a value of $\rm -1\,sp$ because the +reader will not see this distance---but in my opinions this is no +good programming style. In this special case I have luck, too: +the skip is not set anyway, either it is discarded by the preceding +|\newpage| or it is not added by |\addvspace| because of the preceding +|\pre_sect_skip|. + +\beginprog +\def\chap#1.{% + \chap_intro + \@startsection{section}{1}% + {\z@}% + {\m@ne sp}% + {\medskipamount}% + {\normalsize\bf}% + {#1.}% + } +\endprog + + +\sect Normal sections are started with |\sect|. This macro has no +parameter. Between two paragraphs we skip |\pre_sect_skip|. +Between the section number and the text one quad of space is set. +We have to take care for empty documentation parts where the +program part (i.e.\ |\beginprog|) will follow immediately. Then +the program part should begin on a new line and not behind the +section number. So we provide |\null| as an empty documentation +part in every case. + +Now again the design of |\@startsection| comes in hand: \LaTeX{} +always leaves a quad after the section number (by the way, +this is not mentioned in the documentation---you have to look +in the macros to figure this out). Because our section header +consists only of the number we have to code a value of $\rm +-0\,pt$\,\dots + +\begin{quote} +Again: {\it non-orthogonal design is bad design}. +\end{quote} + +\beginprog +\def\sect{% + \sect_intro + \@startsection{section}{1}% + {\z@}% + {\pre_sect_skip}% + {\m@ne sp}% + {\normalsize\bf}% + {\null}% + } +\endprog + + +\sect We are finished and just have to restore the category code. + +\beginprog +\catcode`\_=\uscode + +\endinput +\endprog + + + + + +\end{document} diff --git a/support/makeprog/src/progltx.sty b/support/makeprog/src/progltx.sty new file mode 100644 index 0000000000..83a3181c20 --- /dev/null +++ b/support/makeprog/src/progltx.sty @@ -0,0 +1,194 @@ +\chardef\escape=0 +\chardef\open=1 +\chardef\close=2 +\chardef\letter=11 +\chardef\other=12 +%\chardef\active=13 % is defined in Plain already + +\chardef\uscode=\catcode`\_ + +\catcode`\_=\letter +%\font\tentex=cmtex10 % typewriter extended ASCII 10pt +%\let\ttex=\tentex % only with base size 10pt +\def\ttex{\tt} % as a substitute + +\def\setup_verbatim{% + \def\do##1{\catcode`##1\other}\dospecials + \parskip\z@skip \parindent\z@ + \catcode`\`\active \@noligs + \obeylines \@vobeyspaces \obeytabs \frenchspacing + \ttex + } + +\let\tab=\space +\begingroup + \catcode`\^^I=\active% % Attention: no tabs! + \gdef\obeytabs{\catcode`\^^I=\active\def^^I{\tab}} + \global\let^^I=\tab% % if an active tab appears in a \write +\endgroup +\let\origvert=| +\chardef\vbar=`\| + +\def\makebaractive{\catcode`\|\active} +\def\makebarother{\catcode`\|\other} +\makebaractive + +\def|{% + \leavevmode + \hbox\bgroup + \let\par\space \setup_verbatim + \let|\egroup + } +\let\@@verbatim=\@verbatim +\def\@verbatim{% + \makebarother + \@@verbatim + } +\let\@@tabarray=\@tabarray +\def\@tabarray{% + \makebarother + \@@tabarray + } + +\def\multicolumn#1{% + \multispan{#1}% + \begingroup + \makebarother + \restof_multicolumn + } +\def\restof_multicolumn#1#2{% + \@mkpream{#1}% + \def\@sharp{#2}% + \let\protect\relax + \let\@startpbox\@@startpbox \let\@endpbox\@@endpbox + \@arstrut \@preamble + \endgroup + \ignorespaces + } + +\let\@@mkpream=\@mkpream +\def\@mkpream#1{% + \@@mkpream{#1}% + \makebaractive + } +\begingroup + \def\@makeother{\noexpand\@makeother\noexpand} + \xdef\@sanitize{\@sanitize\@makeother\|} +\endgroup +\newif\if@print + +\def\begin_verbatim{% + \endgraf + \bigbreak + \begingroup + \setup_verbatim \makebarother + \@printtrue + \ignore_rest_line + } +\let\end_verbatim=\endgroup % internal command ! +\begingroup + \obeylines% % ^^M is active! ==> every line must end with % + \gdef\ignore_rest_line#1^^M{\set_next_line}% + \gdef\set_next_line#1^^M{\do_set{#1}}% +\endgroup + +\def\do_set#1{% + \endgraf + \check_print{#1}% + \if@print \indent \print_char#1\end_line\end_line + \else \let\set_next_line\end_verbatim + \fi + \set_next_line + } +\let\end_line=\relax +\begingroup +\obeyspaces\obeytabs +\gdef\check_print#1{\cut_at_tab#1^^I\end_line} +\gdef\cut_at_tab#1^^I#2\end_line{\check_first_part#1 \end_line}% blank ! +\gdef\check_first_part#1 #2\end_line{\do_check{#1}} +\endgroup +\def\do_check#1{% + \def\@line{#1}% + \ifx \@line\end_verbatim_line \@printfalse + \fi + } +\newcount\char_count \char_count\z@ + +\def\print_char#1#2\end_line{% + \print_first_char{#1}% + \print_rest_of_line{#2}% + } +{\obeytabs\gdef\@tab{^^I}} + +\def\print_first_char#1{% + \def\@char{#1}% + \advance \char_count\@ne + \ifx \@char\@tab \print_tab + \else \@char + \fi + } +\newcount\count_mod_viii +\def\mod_viii#1{% + \count@ #1\relax \count_mod_viii\count@ + \divide \count@ 8\relax + \multiply \count@ 8\relax + \advance \count_mod_viii -\count@ + } +\def\print_tab{% + \loop \space \mod_viii\char_count + \ifnum \count_mod_viii>\z@ + \advance \char_count\@ne + \repeat + } +\def\print_rest_of_line#1{% + \def\@line{#1}% + \ifx \@line\empty \char_count\z@ + \def\next##1\end_line{\relax}% + \else \let\next\print_char + \fi + \next#1\end_line + } +{\catcode`\/=\escape % / is temporary escape char + \catcode`\\=\other + /gdef/beginprog{% + /gdef/end_verbatim_line{\endprog}% + /begin_verbatim + } + /gdef/beginverbatim{% + /gdef/end_verbatim_line{\endverbatim}% + /begin_verbatim + } +} % here \endgroup can't be used +\newskip\pre_sect_skip \pre_sect_skip=2pc plus 1pc minus 6pt + +\def\chap_intro{% + \gdef\thesection{\arabic{section}.}% + \gdef\chap_intro{\newpage}% + \addvspace{\pre_sect_skip}% + } + +\def\sect_intro{% + \gdef\thesection{\arabic{section}.}% + \global\let\sect_intro\relax + } +\def\chap#1.{% + \chap_intro + \@startsection{section}{1}% + {\z@}% + {\m@ne sp}% + {\medskipamount}% + {\normalsize\bf}% + {#1.}% + } +\def\sect{% + \sect_intro + \@startsection{section}{1}% + {\z@}% + {\pre_sect_skip}% + {\m@ne sp}% + {\normalsize\bf}% + {\null}% + } +\catcode`\_=\uscode + +\endinput diff --git a/support/makeprog/src/tex2doc.l b/support/makeprog/src/tex2doc.l new file mode 100644 index 0000000000..7f0fa9cc0a --- /dev/null +++ b/support/makeprog/src/tex2doc.l @@ -0,0 +1,12 @@ +%{ +/* tex2doc.l 20 Jan 90 + *------------------------------------------------------- + * (c) 1990 by J.Schrod. + * Copyright conditions: GNU General Public License + */ +%} + +%% + +^%%%\ ? ; +^%%%%.*\n ; diff --git a/support/makeprog/webtoc/convert b/support/makeprog/webtoc/convert new file mode 100644 index 0000000000..23ffa7ed08 --- /dev/null +++ b/support/makeprog/webtoc/convert @@ -0,0 +1,6 @@ +: +# Convert webware programs to C +cat web2c.defines $1 \ + | ../../web2c/web2c -hweb2c.h -c`basename $1 .p` \ + | ../../web2c/fixwrites > $2 +rm -f stack.stack diff --git a/support/makeprog/webtoc/makefile b/support/makeprog/webtoc/makefile new file mode 100644 index 0000000000..012e312506 --- /dev/null +++ b/support/makeprog/webtoc/makefile @@ -0,0 +1,49 @@ +# +# Makefile for TeX82 distribution, subdirectory `webware/makeprog' +# +# This file makes MAKEPROG +# +# It assumes that a working tangle exists. + + +BINDIR=/usr/local/bin +SITEDIR=../../common + +CFLAGS=-O -Dlink=link_array -I../../common +LDFLAGS= + +.SUFFIXES: +.SUFFIXES: .o .c .p .ch + +.p.c: + ./convert $*.p $*.c + +.ch.p: + tangle $*.web $*.ch + +.c.o: + $(CC) $(CFLAGS) -c $*.c + +default: makeprog + +all: makeprog + +install: all + mv ../bin/makeprog $(BINDIR) + + +# MAKEPROG +makeprog: makeprog.o makepext.o + $(CC) $(LDFLAGS) $(CFLAGS) -o ../../bin/makeprog makeprog.o \ + makepext.o +makeprog.o: makeprog.c web2c.h +makeprog.p: makeprog.web makeprog.ch +makepext.o: makepext.c $(SITEDIR)/site.h + + +clean: + rm -f *.pool + rm -f *.o + +very_clean: clean + rm -f makeprog makeprog.p makeprog.c diff --git a/support/makeprog/webtoc/makepext.c b/support/makeprog/webtoc/makepext.c new file mode 100644 index 0000000000..c1d55b2d9e --- /dev/null +++ b/support/makeprog/webtoc/makepext.c @@ -0,0 +1,91 @@ +/* + * Hand-coded routines for C TeX. + * This module should contain any system-dependent code. + */ + +#define CATCHINT /* Catch ^C's */ + +#define EXTERN /* Actually instantiate globals here */ + +#include <signal.h> +#include <time.h> +#include <stdio.h> +#include "site.h" + +#define BUF_SIZE 100 /* should agree with tangle.web */ + +extern char buffer[]; /* 0..BUF_SIZE. Input goes here */ +extern char outbuf[]; /* 0..OUT_BUF_SIZE. Output from here */ +extern char xord[], xchr[]; /* character translation arrays */ +extern int limit; + +#ifdef SYSV +#define index strchr /* Sys V compatibility */ +extern int sprintf(); +#else +extern char *sprintf(); +#endif + +/* C library stuff that we're going to use */ +extern char *strcpy(), *strcat(), *malloc(), *index(), *getenv(); + +/* Local stuff */ +static int gargc; +integer argc; +static char **gargv; + + +#define TRUE 1 +#define FALSE 0 + + +/* + * Get things going under Unix: set up for rescanning the command line, + * then call the main body. + */ +main(iargc, iargv) +int iargc; +char *iargv[]; +{ + gargc=argc=iargc; + gargv=iargv; + main_body(); +} + +/* Return the nth argument into the string s (which is indexed starting at 1) */ +argv(n,s) +integer n; +char s[]; +{ + (void) sprintf(s+1, "%s ", gargv[n]); +} + + +/* Same as in Pascal --- return TRUE if EOF or next char is newline */ +eoln(f) +FILE *f; +{ + register int c; + + if (feof(f)) return(TRUE); + c = getc(f); + if (c != EOF) + (void) ungetc(c, f); + return (c == '\n' || c == EOF); +} + +/* Open a file; don't return if error */ +FILE *openf(name, mode) +char *name, *mode; +{ + FILE *result; + char *index(), *cp; + + cp = index(name, ' '); + if (cp) *cp = '\0'; + result = fopen(name, mode); + if (result) return(result); + perror(name); + exit(1); + /*NOTREACHED*/ +} diff --git a/support/makeprog/webtoc/makeprog.c b/support/makeprog/webtoc/makeprog.c new file mode 100644 index 0000000000..d28cc52967 --- /dev/null +++ b/support/makeprog/webtoc/makeprog.c @@ -0,0 +1,675 @@ +#include "web2c.h" +/* 9999 */ +#define bufsize 500 +typedef schar ASCIIcode ; +typedef file_ptr /* of char */ textfile ; +schar history ; +ASCIIcode xord[128] ; +char xchr[128] ; +textfile docfile ; +textfile changefile ; +textfile progfile ; +ASCIIcode buffer[bufsize + 1] ; +integer line ; +integer otherline ; +integer templine ; +integer limit ; +boolean inputhasended ; +boolean changing ; +ASCIIcode changebuffer[bufsize + 1] ; +integer changelimit ; +schar state ; +char docfilename[121], changefilename[121], progfilename[121] ; +char progextension[121] ; + +#include "makeprog.h" +error () { + { + if ( changing ) + (void) Fputs( stdout , ". (change file " ) ; + else + (void) Fputs( stdout , ". (" ) ; + (void) fprintf( stdout , "%s%ld%c\n", "l." , (long)line , ')' ) ; + (void) putc( ' ' , stdout ); + } + flush ( stdout ) ; +} +scanargs () { + integer docdotpos, dotpos, i, a, slashpos ; + char c ; + char fname[121] ; + boolean founddoc, foundchange ; + progextension [ 1 ] = 'p' ; + progextension [ 2 ] = 'r' ; + progextension [ 3 ] = 'o' ; + progextension [ 4 ] = 'g' ; + progextension [ 5 ] = ' ' ; + founddoc = false ; + foundchange = false ; + {register integer for_end; a = 1 ; for_end = argc - 1 ; if ( a <= for_end) + do + { + argv ( a , fname ) ; + if ( fname [ 1 ] != '-' ) + { + if ( ! founddoc ) + { + slashpos = 0 ; + dotpos = -1 ; + i = 1 ; + while ( ( fname [ i ] != ' ' ) && ( i <= 115 ) ) { + + docfilename [ i ] = fname [ i ] ; + if ( fname [ i ] == '.' ) + dotpos = i ; + else if ( fname [ i ] == '/' ) + { + slashpos = i ; + dotpos = -1 ; + } + i = i + 1 ; + } + if ( dotpos == -1 ) + { + dotpos = i ; + docfilename [ dotpos ] = '.' ; + docfilename [ dotpos + 1 ] = 'd' ; + docfilename [ dotpos + 2 ] = 'o' ; + docfilename [ dotpos + 3 ] = 'c' ; + docfilename [ dotpos + 4 ] = ' ' ; + } + dotpos = dotpos - slashpos ; + {register integer for_end; i = 1 ; for_end = dotpos ; if ( i <= + for_end) do + { + progfilename [ i ] = docfilename [ i + slashpos ] ; + } + while ( i++ < for_end ) ; } + docdotpos = dotpos ; + founddoc = true ; + } + else if ( ! foundchange ) + { + dotpos = -1 ; + i = 1 ; + while ( ( fname [ i ] != ' ' ) && ( i <= 115 ) ) { + + changefilename [ i ] = fname [ i ] ; + if ( fname [ i ] == '.' ) + dotpos = i ; + else if ( fname [ i ] == '/' ) + dotpos = -1 ; + i = i + 1 ; + } + if ( dotpos == -1 ) + { + dotpos = i ; + changefilename [ dotpos ] = '.' ; + changefilename [ dotpos + 1 ] = 'c' ; + changefilename [ dotpos + 2 ] = 'h' ; + changefilename [ dotpos + 3 ] = ' ' ; + } + foundchange = true ; + } + else { + + { + (void) putc('\n', stdout ); + (void) Fputs( stdout , "! Usage: makeprog [-progext] docfile[.doc] [changefile[.ch]]" ) ; + } + error () ; + uexit ( 1 ) ; + } + } + else { + + i = 1 ; + while ( ( i < 6 ) && ( fname [ i + 1 ] != ' ' ) ) { + + progextension [ i ] = fname [ i + 1 ] ; + i = i + 1 ; + } + progextension [ i ] = ' ' ; + if ( i > 5 ) + { + { + (void) putc('\n', stdout ); + (void) Fputs( stdout , "! Usage: makeprog [-progext] docfile[.doc] [changefile[.ch]]" ) ; + } + error () ; + uexit ( 1 ) ; + } + } + } + while ( a++ < for_end ) ; } + if ( ! founddoc ) + { + { + (void) putc('\n', stdout ); + (void) Fputs( stdout , "! Usage: makeprog [-progext] docfile[.doc] [changefile[.ch]]" ) ; + } + error () ; + uexit ( 1 ) ; + } + { + i = 1 ; + if ( progextension [ 1 ] == ' ' ) + progfilename [ dotpos ] = ' ' ; + else while ( progextension [ i ] != ' ' ) { + + progfilename [ docdotpos + i ] = progextension [ i ] ; + i = i + 1 ; + } + } + if ( ! foundchange ) + { + changefilename [ 1 ] = '/' ; + changefilename [ 2 ] = 'd' ; + changefilename [ 3 ] = 'e' ; + changefilename [ 4 ] = 'v' ; + changefilename [ 5 ] = '/' ; + changefilename [ 6 ] = 'n' ; + changefilename [ 7 ] = 'u' ; + changefilename [ 8 ] = 'l' ; + changefilename [ 9 ] = 'l' ; + changefilename [ 10 ] = ' ' ; + } +} +initialize () { + schar i ; + history = 0 ; + xchr [ 32 ] = ' ' ; + xchr [ 33 ] = '!' ; + xchr [ 34 ] = '"' ; + xchr [ 35 ] = '#' ; + xchr [ 36 ] = '$' ; + xchr [ 37 ] = '%' ; + xchr [ 38 ] = '&' ; + xchr [ 39 ] = '\'' ; + xchr [ 40 ] = '(' ; + xchr [ 41 ] = ')' ; + xchr [ 42 ] = '*' ; + xchr [ 43 ] = '+' ; + xchr [ 44 ] = ',' ; + xchr [ 45 ] = '-' ; + xchr [ 46 ] = '.' ; + xchr [ 47 ] = '/' ; + xchr [ 48 ] = '0' ; + xchr [ 49 ] = '1' ; + xchr [ 50 ] = '2' ; + xchr [ 51 ] = '3' ; + xchr [ 52 ] = '4' ; + xchr [ 53 ] = '5' ; + xchr [ 54 ] = '6' ; + xchr [ 55 ] = '7' ; + xchr [ 56 ] = '8' ; + xchr [ 57 ] = '9' ; + xchr [ 58 ] = ':' ; + xchr [ 59 ] = ';' ; + xchr [ 60 ] = '<' ; + xchr [ 61 ] = '=' ; + xchr [ 62 ] = '>' ; + xchr [ 63 ] = '?' ; + xchr [ 64 ] = '@' ; + xchr [ 65 ] = 'A' ; + xchr [ 66 ] = 'B' ; + xchr [ 67 ] = 'C' ; + xchr [ 68 ] = 'D' ; + xchr [ 69 ] = 'E' ; + xchr [ 70 ] = 'F' ; + xchr [ 71 ] = 'G' ; + xchr [ 72 ] = 'H' ; + xchr [ 73 ] = 'I' ; + xchr [ 74 ] = 'J' ; + xchr [ 75 ] = 'K' ; + xchr [ 76 ] = 'L' ; + xchr [ 77 ] = 'M' ; + xchr [ 78 ] = 'N' ; + xchr [ 79 ] = 'O' ; + xchr [ 80 ] = 'P' ; + xchr [ 81 ] = 'Q' ; + xchr [ 82 ] = 'R' ; + xchr [ 83 ] = 'S' ; + xchr [ 84 ] = 'T' ; + xchr [ 85 ] = 'U' ; + xchr [ 86 ] = 'V' ; + xchr [ 87 ] = 'W' ; + xchr [ 88 ] = 'X' ; + xchr [ 89 ] = 'Y' ; + xchr [ 90 ] = 'Z' ; + xchr [ 91 ] = '[' ; + xchr [ 92 ] = '\\' ; + xchr [ 93 ] = ']' ; + xchr [ 94 ] = '^' ; + xchr [ 95 ] = '_' ; + xchr [ 96 ] = '`' ; + xchr [ 97 ] = 'a' ; + xchr [ 98 ] = 'b' ; + xchr [ 99 ] = 'c' ; + xchr [ 100 ] = 'd' ; + xchr [ 101 ] = 'e' ; + xchr [ 102 ] = 'f' ; + xchr [ 103 ] = 'g' ; + xchr [ 104 ] = 'h' ; + xchr [ 105 ] = 'i' ; + xchr [ 106 ] = 'j' ; + xchr [ 107 ] = 'k' ; + xchr [ 108 ] = 'l' ; + xchr [ 109 ] = 'm' ; + xchr [ 110 ] = 'n' ; + xchr [ 111 ] = 'o' ; + xchr [ 112 ] = 'p' ; + xchr [ 113 ] = 'q' ; + xchr [ 114 ] = 'r' ; + xchr [ 115 ] = 's' ; + xchr [ 116 ] = 't' ; + xchr [ 117 ] = 'u' ; + xchr [ 118 ] = 'v' ; + xchr [ 119 ] = 'w' ; + xchr [ 120 ] = 'x' ; + xchr [ 121 ] = 'y' ; + xchr [ 122 ] = 'z' ; + xchr [ 123 ] = '{' ; + xchr [ 124 ] = '|' ; + xchr [ 125 ] = '}' ; + xchr [ 126 ] = '~' ; + xchr [ 0 ] = ' ' ; + xchr [ 127 ] = ' ' ; + {register integer for_end; i = 1 ; for_end = 31 ; if ( i <= for_end) do + xchr [ i ] = ' ' ; + while ( i++ < for_end ) ; } + xchr [ 9 ] = chr ( 9 ) ; + {register integer for_end; i = 0 ; for_end = 127 ; if ( i <= for_end) do + xord [ chr ( i ) ] = 32 ; + while ( i++ < for_end ) ; } + {register integer for_end; i = 1 ; for_end = 126 ; if ( i <= for_end) do + xord [ xchr [ i ] ] = i ; + while ( i++ < for_end ) ; } + scanargs () ; + reset ( docfile , docfilename ) ; + reset ( changefile , changefilename ) ; + rewrite ( progfile , progfilename ) ; + state = 2 ; +} +boolean zinputln ( f ) +textfile f ; +{register boolean Result; integer finallimit ; + limit = 0 ; + finallimit = 0 ; + if ( feof ( f ) ) + Result = false ; + else { + + while ( ! eoln ( f ) ) { + + buffer [ limit ] = xord [ getc ( f ) ] ; + limit = limit + 1 ; + if ( ( buffer [ limit - 1 ] != 32 ) && ( buffer [ limit - 1 ] != 9 ) ) + finallimit = limit ; + if ( limit == bufsize ) + { + while ( ! eoln ( f ) ) vgetc ( f ) ; + limit = limit - 1 ; + { + (void) putc('\n', stdout ); + (void) Fputs( stdout , "! Input line too long" ) ; + } + error () ; + history = 2 ; + } + } + readln ( f ) ; + limit = finallimit ; + Result = true ; + } + return(Result) ; +} +boolean linesdontmatch () { + /* 10 */ register boolean Result; integer k ; + Result = true ; + if ( changelimit != limit ) + goto lab10 ; + if ( limit > 0 ) + {register integer for_end; k = 0 ; for_end = limit - 1 ; if ( k <= for_end) + do + if ( changebuffer [ k ] != buffer [ k ] ) + goto lab10 ; + while ( k++ < for_end ) ; } + Result = false ; + lab10: ; + return(Result) ; +} +primethechangebuffer () { + /* 22 30 10 */ integer k ; + changelimit = 0 ; + while ( true ) { + + line = line + 1 ; + if ( ! inputln ( changefile ) ) + goto lab10 ; + if ( limit < 2 ) + goto lab22 ; + if ( buffer [ 0 ] != 64 ) + goto lab22 ; + if ( ( buffer [ 1 ] >= 88 ) && ( buffer [ 1 ] <= 90 ) ) + buffer [ 1 ] = buffer [ 1 ] + 32 ; + if ( buffer [ 1 ] == 120 ) + goto lab30 ; + if ( ( buffer [ 1 ] == 121 ) || ( buffer [ 1 ] == 122 ) ) + { + { + (void) putc('\n', stdout ); + (void) Fputs( stdout , "! Where is the matching @x?" ) ; + } + error () ; + history = 2 ; + } + lab22: ; + } + lab30: ; + do { + line = line + 1 ; + if ( ! inputln ( changefile ) ) + { + { + { + (void) putc('\n', stdout ); + (void) Fputs( stdout , "! Change file ended after @x" ) ; + } + error () ; + history = 2 ; + } + goto lab10 ; + } + } while ( ! ( limit > 0 ) ) ; + { + changelimit = limit ; + {register integer for_end; k = 0 ; for_end = limit - 1 ; if ( k <= + for_end) do + changebuffer [ k ] = buffer [ k ] ; + while ( k++ < for_end ) ; } + } + lab10: ; +} +checkchange () { + /* 10 */ integer n ; + integer k ; + if ( linesdontmatch () ) + goto lab10 ; + n = 0 ; + while ( true ) { + + { + changing = ! changing ; + templine = otherline ; + otherline = line ; + line = templine ; + } + line = line + 1 ; + if ( ! inputln ( changefile ) ) + { + { + { + (void) putc('\n', stdout ); + (void) Fputs( stdout , "! Change file ended before @y" ) ; + } + error () ; + history = 2 ; + } + changelimit = 0 ; + { + changing = ! changing ; + templine = otherline ; + otherline = line ; + line = templine ; + } + goto lab10 ; + } + if ( limit > 1 ) + if ( buffer [ 0 ] == 64 ) + { + if ( ( buffer [ 1 ] >= 88 ) && ( buffer [ 1 ] <= 90 ) ) + buffer [ 1 ] = buffer [ 1 ] + 32 ; + if ( ( buffer [ 1 ] == 120 ) || ( buffer [ 1 ] == 122 ) ) + { + { + (void) putc('\n', stdout ); + (void) Fputs( stdout , "! Where is the matching @y?" ) ; + } + error () ; + history = 2 ; + } + else if ( buffer [ 1 ] == 121 ) + { + if ( n > 0 ) + { + { + (void) putc('\n', stdout ); + (void) fprintf( stdout , "%s%ld%s", "! Hmm... " , (long)n , " of the preceding lines failed to match" ) ; + } + error () ; + history = 2 ; + } + goto lab10 ; + } + } + { + changelimit = limit ; + {register integer for_end; k = 0 ; for_end = limit - 1 ; if ( k <= + for_end) do + changebuffer [ k ] = buffer [ k ] ; + while ( k++ < for_end ) ; } + } + { + changing = ! changing ; + templine = otherline ; + otherline = line ; + line = templine ; + } + line = line + 1 ; + if ( ! inputln ( docfile ) ) + { + { + { + (void) putc('\n', stdout ); + (void) Fputs( stdout , "! CWEB file ended during a change" ) ; + } + error () ; + history = 2 ; + } + inputhasended = true ; + goto lab10 ; + } + if ( linesdontmatch () ) + n = n + 1 ; + } + lab10: ; +} +getline () { + /* 20 */ lab20: if ( changing ) + { + line = line + 1 ; + if ( ! inputln ( changefile ) ) + { + { + { + (void) putc('\n', stdout ); + (void) Fputs( stdout , "! Change file ended without @z" ) ; + } + error () ; + history = 2 ; + } + buffer [ 0 ] = 64 ; + buffer [ 1 ] = 122 ; + limit = 2 ; + } + if ( limit > 1 ) + if ( buffer [ 0 ] == 64 ) + { + if ( ( buffer [ 1 ] >= 88 ) && ( buffer [ 1 ] <= 90 ) ) + buffer [ 1 ] = buffer [ 1 ] + 32 ; + if ( ( buffer [ 1 ] == 120 ) || ( buffer [ 1 ] == 121 ) ) + { + { + (void) putc('\n', stdout ); + (void) Fputs( stdout , "! Where is the matching @z?" ) ; + } + error () ; + history = 2 ; + } + else if ( buffer [ 1 ] == 122 ) + { + primethechangebuffer () ; + { + changing = ! changing ; + templine = otherline ; + otherline = line ; + line = templine ; + } + } + } + } + if ( ! changing ) + { + { + line = line + 1 ; + if ( ! inputln ( docfile ) ) + inputhasended = true ; + else if ( changelimit > 0 ) + checkchange () ; + } + if ( changing ) + goto lab20 ; + } + buffer [ limit ] = 32 ; + if ( ( line % 500 ) == 0 ) + { + (void) fprintf( stdout , "%ld", (long)line ) ; + flush ( stdout ) ; + } + else if ( ( line % 100 ) == 0 ) + { + (void) putc( '.' , stdout ); + flush ( stdout ) ; + } +} +putline () { + integer i ; + {register integer for_end; i = 0 ; for_end = limit - 1 ; if ( i <= for_end) + do + (void) putc( xchr [ buffer [ i ] ] , progfile ); + while ( i++ < for_end ) ; } + (void) putc('\n', progfile ); +} +main_body() { + + initialize () ; + (void) fprintf( stdout , "%s\n", "This is MAKEPROG, C Version 1.0.1." ) ; + (void) fprintf( stdout , "%s\n", " (c) 1988,1990 by J.Schrod." ) ; + (void) fprintf( stdout , "%s\n", " All rights reserved." ) ; + { + line = 0 ; + otherline = 0 ; + changing = true ; + primethechangebuffer () ; + { + changing = ! changing ; + templine = otherline ; + otherline = line ; + line = templine ; + } + limit = 0 ; + buffer [ 0 ] = 32 ; + inputhasended = false ; + } + { + getline () ; + while ( ! inputhasended ) { + + { + if ( buffer [ 0 ] == 92 ) + if ( limit >= 10 ) + { + if ( ( buffer [ 1 ] == 98 ) && ( buffer [ 2 ] == 101 ) && ( buffer [ + 3 ] == 103 ) && ( buffer [ 4 ] == 105 ) && ( buffer [ 5 ] == 110 ) + && ( buffer [ 6 ] == 112 ) && ( buffer [ 7 ] == 114 ) && ( buffer [ + 8 ] == 111 ) && ( buffer [ 9 ] == 103 ) && ( ( buffer [ 10 ] < 65 ) + || ( buffer [ 10 ] > 90 ) ) && ( ( buffer [ 10 ] < 97 ) || ( buffer + [ 10 ] > 122 ) ) ) + state = 0 ; + } + else if ( limit >= 8 ) + if ( ( buffer [ 1 ] == 101 ) && ( buffer [ 2 ] == 110 ) && ( buffer [ + 3 ] == 100 ) && ( buffer [ 4 ] == 112 ) && ( buffer [ 5 ] == 114 ) && + ( buffer [ 6 ] == 111 ) && ( buffer [ 7 ] == 103 ) && ( ( buffer [ 8 ] + == 32 ) || ( buffer [ 8 ] == 9 ) ) ) + state = 2 ; + } + if ( state == 1 ) + putline () ; + else if ( state == 0 ) + state = 1 ; + getline () ; + } + if ( state == 1 ) + { + { + (void) putc('\n', stdout ); + (void) Fputs( stdout , "! Input has ended prematurely" ) ; + } + error () ; + history = 2 ; + } + } + { + if ( changelimit != 0 ) + { + {register integer for_end; limit = 0 ; for_end = changelimit ; if ( + limit <= for_end) do + buffer [ limit ] = changebuffer [ limit ] ; + while ( limit++ < for_end ) ; } + limit = changelimit ; + changing = true ; + line = otherline ; + { + { + (void) putc('\n', stdout ); + (void) Fputs( stdout , "! Change file entry did not match" ) ; + } + error () ; + history = 2 ; + } + } + } + lab9999: switch ( history ) + {case 0 : + { + (void) putc('\n', stdout ); + (void) Fputs( stdout , "(No errors were found.)" ) ; + } + break ; + case 1 : + { + (void) putc('\n', stdout ); + (void) Fputs( stdout , "(Did you see the warning message above?)" ) ; + } + break ; + case 2 : + { + (void) putc('\n', stdout ); + (void) Fputs( stdout , "(Pardon me, but I think I spotted something wrong.)" ) + ; + } + break ; + case 3 : + { + (void) putc('\n', stdout ); + (void) Fputs( stdout , "(That was a fatal error, my friend.)" ) ; + } + break ; + } + (void) putc('\n', stdout ); + if ( ( history != 0 ) && ( history != 1 ) ) + exit ( 1 ) ; + else exit ( 0 ) ; +} diff --git a/support/makeprog/webtoc/makeprog.ch b/support/makeprog/webtoc/makeprog.ch new file mode 100644 index 0000000000..708e176efd --- /dev/null +++ b/support/makeprog/webtoc/makeprog.ch @@ -0,0 +1,475 @@ +% Change file for the MAKEPROG processor, for use with WEB to C. +% This file was created for TANGLE by Howard Trickey and Pavel Curtis. +% Modified for MAKEPROG by Klaus Guntermann at THD + +% History: +% 10/9/82 (HT) Original version +% 11/29 (HT) New version, with conversion to lowercase handled properly +% Also, new control sequence: +% @=...text...@> Put ...text... verbatim on a line +% by itself in the Pascal output. +% (argument must fit on one line) +% This control sequence facilitates putting #include "gcons.h" +% (for example) in files meant for the pc compiler. +% Also, changed command line usage, so that the absence of a +% change file implies no change file, rather than one with the +% same name as the web file, with .ch at the end. +% 1/15/83 (HT) Changed to work with version 1.2, which incorporates the +% above change (though unbundling the output line breaking), +% so mainly had to remove stuff. +% 2/17 (HT) Fixed bug that caused 0-9 in identifiers to be converted to +% Q-Y on output. +% 3/18 (HT) Brought up to work with Version 1.5. Added -r command line +% flag to cause a .rpl file to be written with all the lines +% of the .web file that were replaced because of the .ch file +% (useful for comparing with previous .rpl files, to see if a +% change file will still work with a new version of a .web file) +% Also, made it write a newline just before exit. +% 4/12 (PC) Merged with Pavel's version, including adding a call to exit() +% at the end depending upon the value of history. +% 4/16 (PC) Brought up to date with version 1.5 released April, 1983. +% 6/28 (HWT) Brought up to date with version 1.7 released June, 1983. +% With new change file format, the -r option is now unnecessary. +% 7/17 (HWT) Brought up to date with version 2.0 released July, 1983. +% 12/18 (ETM) Brought up to date with version 2.5 released November, 1983. +% 11/7/84 (ETM) Brought up to date with version 2.6. +% 12/15/85 (ETM) Brought up to date with version 2.8. +% 3/ 7/88 (ETM) Converted for use with WEB2C +% 04/07/89 (-kg) Changed for MAKEPROG + +% NOTE: The module numbers refer to the red-covered listing (Version 2). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% [0] WEAVE: print only changes +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + x +\pageno=\contentspagenumber \advance\pageno by 1 + y +\pageno=\contentspagenumber \advance\pageno by 1 +\let\maybe=\iffalse +\def\title{TANGLE changes C} + z + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% [1] Change banner message +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +@x +@d banner=='This is MAKEPROG, Version 1.0.1.' +@y +@d banner=='This is MAKEPROG, C Version 1.0.1.' +@z + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% [2] add input and output, remove other files, add ref to scan_args, +% [2] and #include external definition for exit(). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +@x +@d end_of_MAKEPROG = 9999 {go here to wrap it up} + +@p @t\4@>@<Compiler directives@>@/ +program MAKEPROG(@!doc_file,@!change_file,@!prog_file); +label end_of_MAKEPROG; {go here to finish} +const @<Constants in the outer block@>@; +type @<Types in the outer block@>@; +var @<Globals in the outer block@>@; +@t\4@>@<Error handling procedures@>@; +@y +@d end_of_MAKEPROG = 9999 {go here to wrap it up} +@d eof==feof + +@p @t\4@>@<Compiler directives@>@/ +program MAKEPROG; +label end_of_MAKEPROG; {go here to finish} +const @<Constants in the outer block@>@; +type @<Types in the outer block@>@; +var @<Globals in the outer block@>@; +@t\4@>@<Error handling procedures@>@; +@<Declaration of |scan_args|@>@/ +@z + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% [4] compiler options +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +@x +@{@&@=$D-@> @} {no debug overhead} +@!debug @{@&@=$D+@> @}@+ gubed @; {but turn everything on when debugging} +@y +@z + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% [17] Allow tabs in input and output files +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +@x +\MAKEPROG{} are allowed to have in their input files, +provided that unsuitable characters do not correspond to +special codes like |carriage_return| that are listed above. + +@<Set init...@>= +for i:=1 to " "-1 do xchr[i]:=' '; +@y +\MAKEPROG{} are allowed to have in their input files, +provided that unsuitable characters do not correspond to +special codes like |carriage_return| that are listed above. +Here we add the tab character to the untranslated ones. +@<Set init...@>= +for i:=1 to " "-1 do xchr[i]:=' '; +xchr[tab_mark]:=chr(tab_mark); +@z + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% [20] terminal output: use standard i/o +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +@x +@d print(#)==write(term_out,#) {`|print|' means write on the terminal} +@y +@d term_out==stdout +@d print(#)==write(term_out,#) {`|print|' means write on the terminal} +@z + +@x +@<Globals...@>= +@!term_out:text_file; {the terminal as an output file} +@y + +@z + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% [21] init terminal +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +@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...@>= + {Nothing need be done C} +@z + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% [22] flush terminal buffer +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +@x +@d update_terminal == break(term_out) {empty the terminal output buffer} +@y +@d update_terminal == flush(term_out) {empty the terminal output buffer} +@z + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% [24] open input files +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +@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@> + +@< Set init... @>= +reset(doc_file); reset(change_file); +@y +The following code opens the input files. +Use the |scan_args| procedure to fill the global file names, +according to the names given on the command line. +These globals, and the |scan_args| procedure will be defined at the end +where they won't disturb the module numbering. +@^system dependencies@> + +@< Set init... @>= +scan_args; +reset(doc_file,doc_file_name); reset(change_file,change_file_name); +@z + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% [26] open output files +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +@x +The following code opens |prog_file|. Since this file is +listed in the program header, we assume that the \PASCAL\ +runtime system has checked that a suitable external file +name have been given. +@^system dependencies@> + +@<Set init...@>= +rewrite(prog_file); +@y +The following code opens |prog_file|. +@^system dependencies@> + +@<Set init...@>= +rewrite(prog_file,prog_file_name); +@z + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Fix f^ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +@x + if eof(f) then input_ln:=false + else begin + while not eoln(f) do + begin buffer[limit]:=xord[f^]; get(f); + incr(limit); + if (buffer[limit-1]<>" ") and (buffer[limit-1]<>tab_mark) then + final_limit:=limit; + if limit=buf_size then + begin while not eoln(f) do get(f); +@y +if eof(f) then input_ln:=false +else begin while not eoln(f) do + begin buffer[limit]:=xord[getc(f)]; + incr(limit); + if (buffer[limit-1]<>" ") and (buffer[limit-1]<>tab_mark) then + final_limit:=limit; + if limit=buf_size then + begin while not eoln(f) do vgetc(f); +@z + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Fix jump_out +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +@x +@d fatal_error(#)==begin print_nl(#); error; mark_fatal; jump_out; + end + +@<Error handling...@>= +procedure jump_out; +begin goto end_of_MAKEPROG; +end; +@y +@d jump_out==uexit(1) +@d fatal_error(#)==begin new_line; print(#); error; mark_fatal; uexit(1); + end +@z + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% [182] write newline just before exit; use value of |history| +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +@x +\noindent We have defined some procedures, and it is time to +use them---here is where \MAKEPROG{} starts, and where it +ends. +@^system dependencies@> +@y +\noindent We have defined some procedures, and it is time to +use them---here is where \MAKEPROG{} starts, and where it +ends. +@^system dependencies@> + +@d UNIXexit==e@&x@&i@&t +@z + +@x + @<Print the job |history|@>; +@y + @<Print the job |history|@>; + new_line; + if (history <> spotless) and (history <> harmless_message) then + UNIXexit(1) + else + UNIXexit(0); +@z + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% [188] system dependent changes--the |scan_args| procedure. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +@x +\noindent This module should be replaced, if necessary, by +changes to the program that are necessary to make +\MAKEPROG{} work at a particular installation. It is +usually best to design your change file so that all changes +to previous modules preserve the module numbering; then +everybody's version will be consistent with the printed +program. More extensive changes, which introduce new +modules, can be inserted here; then only the index itself +will get a new module number. +@^system dependencies@> +@y +@^system dependencies@> + +The user calls \.{MAKEPROG} with arguments on the command line. +These are either file names or flags (beginning with '-'). +The following globals are for communicating the user's desires to the rest +of the program. The various |file_name| variables contain strings with +the full names of those files, as UNIX knows them. + +There are no flags that affect \.{MAKEPROG} at the moment. + +@d max_file_name_length=120 +@d max_extension=5 + +@<Globals...@>= +@!doc_file_name,@!change_file_name,@!prog_file_name: + array[1..max_file_name_length] of char; +@!prog_extension: array[1..max_file_name_length] of char; + +@ The |scan_args| procedure looks at the command line arguments and sets +the |file_name| variables accordingly. +At least one file name must be present: the \.{DOC} file. It may have +an extension, or it may omit it to get |'.doc'| added. +The output file name is formed by replacing the \.{DOC} file +name extension by |'.prg'|. +The output file will be created in the current directory. + +If there is another file name present among the arguments, it is the +change file, again either with an extension or without one to get |'.ch'| +An omitted change file argument means that |'/dev/null'| should be used, +when no changes are desired. + +@<Declaration of |scan_args|@>= +procedure scan_args; +var doc_dot_pos,dot_pos,i,a,slash_pos: integer; {indices} +c: char; +@!fname: array[1..max_file_name_length] of char; {temporary argument holder} +@!found_doc,@!found_change: boolean; {|true| when those file names have + been seen} +begin +prog_extension[1]:='p'; +prog_extension[2]:='r'; +prog_extension[3]:='o'; +prog_extension[4]:='g'; +prog_extension[5]:=' '; +found_doc:=false; +found_change:=false; +for a:=1 to argc-1 do begin + argv(a,fname); {put argument number |a| into |fname|} + if fname[1]<>'-' then begin + if not found_doc then + @<Get |doc_file_name| and |prog_file_name| + variables from |fname|@> + else if not found_change then + @<Get |change_file_name| from |fname|@> + else @<Print usage error message and quit@>; + end + else @<Handle flag argument in |fname|@>; + end; +if not found_doc then @<Print usage error message and quit@>; +@<Add the proper extension@>; +if not found_change then @<Set up null change file@>; +end; + +@ Use all of |fname| for the |doc_file_name| if there is a |'.'| in it, +otherwise add |'.doc'|. +The other file names come from adding things after the dot. +If the name contained directory specifications, we reset the +dot occurred flag and do not use the directory part of the name +for the output files. +The |argv| procedure will not put more than +|max_file_name_length-max_extension| +characters into |fname|, and this leaves enough room in the |file_name| +variables to add the extensions. + +The end of a file name is marked with a |' '|, the convention assumed by +the |reset| and |rewrite| procedures. + +@<Get |doc_file_name|...@>= +begin +slash_pos:=0; +dot_pos:=-1; +i:=1; +while (fname[i]<>' ') and (i<=max_file_name_length-max_extension) do begin + doc_file_name[i]:=fname[i]; + if fname[i]='.' then dot_pos:=i else + if fname[i]='/' then begin + slash_pos:=i; dot_pos:=-1; {reset} + end; + incr(i); + end; +if dot_pos=-1 then begin + dot_pos:=i; + doc_file_name[dot_pos]:='.'; + doc_file_name[dot_pos+1]:='d'; + doc_file_name[dot_pos+2]:='o'; + doc_file_name[dot_pos+3]:='c'; + doc_file_name[dot_pos+4]:=' '; + end; +dot_pos:=dot_pos-slash_pos; {may be unchanged if no slash} +for i:=1 to dot_pos do begin + prog_file_name[i]:= + doc_file_name[i+slash_pos]; + end; +doc_dot_pos:=dot_pos; +found_doc:=true; +end + +@ This section must be called when the doc file was found +and the doc dot position is determined. +@<Add the proper extension@>= +begin +i:=1; +if prog_extension[1]=' ' then prog_file_name[dot_pos]:=' ' +else +while prog_extension[i]<>' ' do +begin prog_file_name[doc_dot_pos+i]:=prog_extension[i]; + i:=i+1; +end; +end + +@ @<Get |change_file_name|...@>= +begin +dot_pos:=-1; +i:=1; +while (fname[i]<>' ') and (i<=max_file_name_length-max_extension) +do begin + change_file_name[i]:=fname[i]; + if fname[i]='.' then dot_pos:=i else + if fname[i]='/' then dot_pos:=-1; + incr(i); + end; +if dot_pos=-1 then begin + dot_pos:=i; + change_file_name[dot_pos]:='.'; + change_file_name[dot_pos+1]:='c'; + change_file_name[dot_pos+2]:='h'; + change_file_name[dot_pos+3]:=' '; + end; +found_change:=true; +end + +@ @<Set up null...@>= +begin + change_file_name[1]:='/'; + change_file_name[2]:='d'; + change_file_name[3]:='e'; + change_file_name[4]:='v'; + change_file_name[5]:='/'; + change_file_name[6]:='n'; + change_file_name[7]:='u'; + change_file_name[8]:='l'; + change_file_name[9]:='l'; + change_file_name[10]:=' '; +end + +@ \.{MAKEPROG} accepts the intended extension of a created file as an +option to overwrite the default extension 'prog'. + +@<Handle flag...@>= +begin + i:=1; + while (i<max_extension+1) and (fname[i+1]<>' ') do + begin + prog_extension[i]:=fname[i+1]; + i:=i+1; + end; + prog_extension[i]:=' '; + if i>max_extension then + @<Print usage error message and quit@>; +end + +@ @<Print usage error message and quit@>= +begin +print_nl('! Usage: makeprog [-progext] docfile[.doc] [changefile[.ch]]'); +error; +jump_out; +end + +@z diff --git a/support/makeprog/webtoc/makeprog.h b/support/makeprog/webtoc/makeprog.h new file mode 100644 index 0000000000..b360edeb59 --- /dev/null +++ b/support/makeprog/webtoc/makeprog.h @@ -0,0 +1,3 @@ +#define inputln(a) zinputln((textfile)(a)) +boolean zinputln(); +boolean linesdontmatch(); diff --git a/support/makeprog/webtoc/web2c.def b/support/makeprog/webtoc/web2c.def new file mode 100644 index 0000000000..2321a39bd2 --- /dev/null +++ b/support/makeprog/webtoc/web2c.def @@ -0,0 +1,100 @@ +@define @field b0; +@define @field b1; +@define @field b2; +@define @field b3; +@define @field gr; +@define @field hh; +@define @field int; +@define @field lh; +@define @field qqqq; +@define @field rh; +@define procedure vgetc(); +@define function abs(); +@define function aopenin(); +@define function aopenout(); +@define function bopenin(); +@define function bopenout(); +@define function chr(); +@define function ord(); +@define function feof(); +@define function eoln(); +@define function fabs(); +@define function getc(); +@define function makefraction(); +@define function odd(); +@define function round(); +@define function takefraction(); +@define function testaccess(); +@define function toint(); +@define function trunc(); +@define function wopenin(); +@define function wopenout(); +@define procedure aclose(); +@define procedure argv(); +@define procedure bclose(); +@define procedure bgetname(); +@define procedure bwrite2bytes(); +@define procedure bwrite4bytes(); +@define procedure bwritebuf(); +@define procedure bwritebyte(); +@define procedure calledit(); +@define procedure clearterminal; +@define procedure close(); +@define procedure dateandtime(); +@define procedure decr(); +@define procedure dumpcore; +@define procedure dumpthings(); +@define procedure exit(); +@define procedure fflush(); +@define procedure flush(); +@define procedure fwrite(); +@define procedure getfmthh(); +@define procedure getfmtint(); +@define procedure getfmtqqqq(); +@define procedure getfmtword(); +@define procedure incr(); +@define procedure lineread(); +@define procedure linewrite(); +@define procedure putfmthh(); +@define procedure putfmtint(); +@define procedure putfmtqqqq(); +@define procedure putfmtword(); +@define procedure read(); +@define procedure readln(); +@define procedure reset(); +@define procedure rewrite(); +@define procedure setpaths; +@define procedure termflush(); +@define procedure topenin; +@define procedure uexit(); +@define procedure undumpthings(); +@define procedure vstrcpy(); +@define procedure wakeupterminal; +@define procedure wclose(); +@define procedure write(); +@define procedure writedvi(); +@define procedure writeln(); +@define type boolean = 0..1; +@define type ccharpointer; +@define type char = 0..127; +@define type integer; +@define type real; +@define type text; +@define var argc; +@define var false; +@define var fmtpathspec; +@define var fontpathspec; +@define var stdin; +@define var inputpathspec; +@define var stdout; +@define var poolpathspec; +@define var readpathspec; +@define var true; +@define function testeof(); +@define procedure zfseek(); +@define function ftell(); +@define function eof(); +@define procedure printreal(); +@define procedure putbyte(); +@define function inputint(); +@define procedure input3ints(); diff --git a/support/makeprog/webtoc/web2c.h b/support/makeprog/webtoc/web2c.h new file mode 100644 index 0000000000..d7157af264 --- /dev/null +++ b/support/makeprog/webtoc/web2c.h @@ -0,0 +1,52 @@ +/* + * Main include file for tangle etc. in C + * + * Tim Morgan 2/11/88 + */ + +#include <stdio.h> +#include "site.h" + +/* + * Global routines implemented as macros, plus some type coercion stuff. + */ + +#define odd(x) ((x) % 2) +#define putbyte(x,f) putc(((x)&255), f) +#define round(x) zround((double)(x)) +#define incr(x) ++x +#define decr(x) --x +#define trunc(x) ( (integer) (x) ) +#define readln(f) while (!feof(f) && getc(f) != '\n') +#define read(f, b) b = getc(f) +#define input3ints(a,b,c) zinput_3ints(&a, &b, &c) +#define zfseek(f,n,w) (void) fseek(f, (long) n, (int) w) +#define eof(f) feof(f) +#define abs(x) ((x>=0)?(x):(-(x))) +#define fabs(x) ((x>=0.0)?(x):(-(x))) +#define Fputs(stream, s) (void) fputs(s, stream) +#define rewrite(f,n) f = openf(n+1, "w") +#define reset(f,n) f = openf(n+1, "r") +#define flush(f) (void) fflush(f) +#define true 1 +#define false 0 +#define chr(x) (x) +#define ord(x) (x) +#define vgetc(f) (void) getc(f) +#define uexit(x) exit(x) + +FILE *openf(); + +/* + * Global Types + */ +typedef FILE *text, *file_ptr; +typedef char *ccharpointer; + +/* + * Global Data and Datastructures + */ + +extern integer argc; +extern integer zround(); +extern integer inputint(); |