% This program by E. W. Sewell, based on one by D. E. Knuth, is % not copyrighted and can be used freely. % Version 1.0 was released in March 1987 % Here is TeX material that gets inserted after \input webmac \def\hang{\hangindent 3em\indent\ignorespaces} \font\ninerm=cmr9 \let\mc=\ninerm % medium caps for names like SAIL \def\PASCAL{Pascal} \def\pb{$\.|\ldots\.|$} % Pascal brackets (|...|) \def\v{\.{\char'174}} % vertical (|) in typewriter font \mathchardef\BA="3224 % double arrow \def\({} % kludge for alphabetizing certain module names \def\title{SCANWEAVE} \def\contentspagenumber{1} % should be odd \def\topofcontents{\null\vfill \centerline{\titlefont The {\ttitlefont SCANWEAVE} processor} \vskip 15pt \centerline{(Version 1.0)} \vfill} \pageno=\contentspagenumber \advance\pageno by 1 @* Introduction. This program takes a \TeX\ file generated by \.{WEAVE} and strips out the sections which have not been changed, outputting the changed sections to a second, greatly reduced \TeX\ file. The index, section names, and table of contents are dropped as well. If the program bears an uncanny resemblance to Donald Knuth's \.{TANGLE}, this is no accident, since it was cloned from \.{TANGLE}. While \.{SCANWEAVE} was developed on a VAX system using VAX Pascal, rather than the Stanford system on which \.{TANGLE} was developed, the system dependencies of the latter were used in \.{SCANWEAVE} so the modifications to implement it on a particular system are almost exactly the same as for \.{TANGLE} and \.{WEAVE}. The program uses a few features of the local \PASCAL\ compiler that may need to be changed in other installations: \yskip\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. \yskip\noindent These features are also present in the \PASCAL\ version of \TeX, where they are used in a similar (but more complex) way. System-dependent portions of \.{SCANWEAVE} can be identified by looking at the entries for `system dependencies' in the index below. @!@^system dependencies@> The ``banner line'' defined here should be changed whenever \.{SCANWEAVE} is modified. @d banner=='This is SCANWEAVE, Version 1.0' @ The program begins with a fairly normal header, made up of pieces that @^system dependencies@> will mostly be filled in later. The \TeX\ input comes from file |tex_file| and the new \TeX\ output goes to file |out_tex_file|. 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_SCANWEAVE|. @d end_of_SCANWEAVE = 9999 {go here to wrap it up} @p @t\4@>@@/ program SCANWEAVE(@!tex_file,@!out_tex_file); label end_of_SCANWEAVE; {go here to finish} const @@/ type @@/ var @@/ @@/ procedure initialize; var @@/ begin @@/ end; @ Some of this code is optional for use when debugging only; such material is enclosed between the delimiters |debug| and $|gubed|$. Other parts, delimited by |stat| and $|tats|$, are optionally included if statistics about \.{WEBMERGE}'s line counts are desired. @d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging} @d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging} @f debug==begin @f gubed==end @# @d stat==@{ {change this to `$\\{stat}\equiv\null$' when gathering usage statistics} @d tats==@t@>@} {change this to `$\\{tats}\equiv\null$' when gathering usage statistics} @f stat==begin @f tats==end @ 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 \.{SCANWEAVE} these directives tell the compiler that @^system dependencies@> it is safe to avoid range checks and to leave out the extra code it inserts for the \PASCAL\ debugger's benefit, although interrupts will occur if there is arithmetic overflow. @= @{@&$C-,A+,D-@} {no range check, catch arithmetic overflow, no debug overhead} @!debug @{@&$C+,D+@}@+ gubed {but turn everything on when debugging} @ Labels are given symbolic names by the following definitions. We insert the label `|exit|:' just before the `\ignorespaces|end|\unskip' 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} @ 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 @ 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 1: $\langle\,$code for $x=1\,\rangle$;\cr 3: $\langle\,$code for $x=3\,\rangle$;\cr |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 `\ignorespaces|else|\unskip' 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. The original author of \.{TANGLE} (Knuth) would have taken the trouble to modify it so that such extensions were done automatically, if he had not wanted to encourage \PASCAL\ compiler writers to make this important change in \PASCAL, where it belongs.) @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 @ The following constants need to be defined. @= @!buf_size=1000; {maximum length of input line} @!out_buf_size=144; {length of output buffer, should be twice |line_length|} @ 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 @=@!history:spotless..fatal_message; {how bad was this run?} @ @=history:=spotless; @* The character set. One of the main goals in the design of \.{WEB} has been to make it readily portable between a wide variety of computers. Yet \.{WEB} 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 \.{WEAVE} and \.{TANGLE} (and \.{SCANWEAVE}!) 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 relevant to users of \.{WEB} only because it is the code used for preprocessed constants like \.{"A"}. If you are writing a program in \.{WEB} that makes use of such one-character constants, you should convert your input to ASCII form, like \.{WEAVE}, \.{TANGLE} and \.{SCANWEAVE} do. Otherwise \.{WEB}'s internal coding scheme does not affect you. @^ASCII code@> 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 @'040 is an invisible blank space.) Code @'136 was once an upward arrow (\.{\char'13}), and code @'137 was once a left arrow (\.^^X), in olden times when the first draft of ASCII code was prepared; but \.{WEB} works with today's standard ASCII in which those codes represent circumflex and underline as shown. @= @!ASCII_code=0..127; {seven-bit numbers, a subrange of the integers} @ 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 \.{WEB} 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|} @= @!text_file=packed file of text_char; @ The \.{WEAVE}, \.{TANGLE} and \.{SCANWEAVE} processors 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. @= @!xord: array [text_char] of ASCII_code; {specifies conversion of input characters} @!xchr: array [ASCII_code] of text_char; {specifies conversion of output characters} @ 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[@@\'101]:=\'A\'} that appears in the present \.{WEB} file might be encoded in, say, {\mc EBCDIC} code on the external medium on which it resides, but \.{SCANWEAVE} 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[@@\'101]:="A"}, because |"A"| is a constant of type |integer|, not |char|, and because we have $|"A"|=65$ regardless of the external character set. @= xchr[@'40]:=' '; xchr[@'41]:='!'; xchr[@'42]:='"'; xchr[@'43]:='#'; xchr[@'44]:='$'; xchr[@'45]:='%'; xchr[@'46]:='&'; xchr[@'47]:='''';@/ xchr[@'50]:='('; xchr[@'51]:=')'; xchr[@'52]:='*'; xchr[@'53]:='+'; xchr[@'54]:=','; xchr[@'55]:='-'; xchr[@'56]:='.'; xchr[@'57]:='/';@/ xchr[@'60]:='0'; xchr[@'61]:='1'; xchr[@'62]:='2'; xchr[@'63]:='3'; xchr[@'64]:='4'; xchr[@'65]:='5'; xchr[@'66]:='6'; xchr[@'67]:='7';@/ xchr[@'70]:='8'; xchr[@'71]:='9'; xchr[@'72]:=':'; xchr[@'73]:=';'; xchr[@'74]:='<'; xchr[@'75]:='='; xchr[@'76]:='>'; xchr[@'77]:='?';@/ xchr[@'100]:='@@'; xchr[@'101]:='A'; xchr[@'102]:='B'; xchr[@'103]:='C'; xchr[@'104]:='D'; xchr[@'105]:='E'; xchr[@'106]:='F'; xchr[@'107]:='G';@/ xchr[@'110]:='H'; xchr[@'111]:='I'; xchr[@'112]:='J'; xchr[@'113]:='K'; xchr[@'114]:='L'; xchr[@'115]:='M'; xchr[@'116]:='N'; xchr[@'117]:='O';@/ xchr[@'120]:='P'; xchr[@'121]:='Q'; xchr[@'122]:='R'; xchr[@'123]:='S'; xchr[@'124]:='T'; xchr[@'125]:='U'; xchr[@'126]:='V'; xchr[@'127]:='W';@/ xchr[@'130]:='X'; xchr[@'131]:='Y'; xchr[@'132]:='Z'; xchr[@'133]:='['; xchr[@'134]:='\'; xchr[@'135]:=']'; xchr[@'136]:='^'; xchr[@'137]:='_';@/ xchr[@'140]:='`'; xchr[@'141]:='a'; xchr[@'142]:='b'; xchr[@'143]:='c'; xchr[@'144]:='d'; xchr[@'145]:='e'; xchr[@'146]:='f'; xchr[@'147]:='g';@/ xchr[@'150]:='h'; xchr[@'151]:='i'; xchr[@'152]:='j'; xchr[@'153]:='k'; xchr[@'154]:='l'; xchr[@'155]:='m'; xchr[@'156]:='n'; xchr[@'157]:='o';@/ xchr[@'160]:='p'; xchr[@'161]:='q'; xchr[@'162]:='r'; xchr[@'163]:='s'; xchr[@'164]:='t'; xchr[@'165]:='u'; xchr[@'166]:='v'; xchr[@'167]:='w';@/ xchr[@'170]:='x'; xchr[@'171]:='y'; xchr[@'172]:='z'; xchr[@'173]:='{'; xchr[@'174]:='|'; xchr[@'175]:='}'; xchr[@'176]:='~';@/ xchr[0]:=' '; xchr[@'177]:=' '; {these ASCII codes are not used} @ Some of the ASCII codes below @'40 have been given symbolic names in \.{WEAVE}, \.{TANGLE} and \.{SCANWEAVE} because they are used with a special meaning. @d tab_mark=@'11 {ASCII code used as tab-skip} @d line_feed=@'12 {ASCII code thrown away at end of line} @d form_feed=@'14 {ASCII code used at end of page} @d carriage_return=@'15 {ASCII code used at end of line} @ When we initialize the |xord| array and the remaining parts of |xchr|, it will be convenient to make use of an index variable, |i|. @= @!i:0..last_text_char; @ Here now is the system-dependent part of the character set. If \.{WEB} 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. But at MIT, for example, the code in this module should be changed to $$\hbox{|for i:=1 to @'37 do xchr[i]:=chr(i);|}$$ \.{WEB}'s character set is essentially identical to MIT's, even with respect to characters less than @'40. @^system dependencies@> Changes to the present module will make \.{WEB} more friendly on computers that have an extended character set, so that one can type things like \.^^Z\ instead of \.{<>}. 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 \.{WEB} 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. (The present file \.{SCANWEAVE.WEB} does not contain any of the non-ASCII characters, because it is intended to be used with all implementations of \.{WEB}. It is based on \.{TANGLE}, which was originally created on a Stanford system that has a convenient extended character set, then ``sanitized'' by applying another program that transliterated all of the non-standard characters into standard equivalents.) @= for i:=1 to @'37 do xchr[i]:=' '; @ The following system-independent code makes the |xord| array contain a suitable inverse to the information in |xchr|. @= for i:=first_text_char to last_text_char do xord[chr(i)]:=@'40; for i:=1 to @'176 do xord[xchr[i]]:=i; @* Input and output. The input conventions of this program are intended to be very much like those of \TeX\ (except, of course, that they are much simpler, because much less needs to be done). Furthermore they are identical to those of \.{TANGLE} and \.{WEAVE}. Therefore people who need to make modifications to all four systems should be able to do so without too many headaches. @ 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 @= @!term_out:text_file; {the terminal as an output file} @!term_in:text_file; {the user's terminal as an input file} @ 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@> @= rewrite(term_out,'TTY:'); {send |term_out| output to the terminal} @ 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} @ The main input comes from |tex_file|. @= @!tex_file:text_file; {primary input} @!out_tex_file : text_file; { output file } @ The following code opens the fixed input and output files. Since these files were listed in the program header, we assume that the \PASCAL\ runtime system has already checked that suitable file names have been given; therefore no additional error checking needs to be done. @^system dependencies@> @p procedure open_input; {prepare to read |tex_file| and write to |out_tex_file|} begin reset(tex_file); rewrite(out_tex_file); end; @ Input goes into an array called |buffer|. @=@!buffer: array[0..buf_size] of ASCII_code; @ 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 \TeX\ 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 @^system dependencies@> line. The value of |limit| must be strictly less than |buf_size|. We assume that none of the |ASCII_code| values of |buffer[j]| for |0<=j" " 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'); loc:=0; error; @.Input line too long@> end; end; read_ln(f); limit:=final_limit; input_ln:=true; end; end; @ The |output_ln| procedure writes the next line of output from the |buffer| array to the specified file. @p procedure output_ln(var f:text_file); {outputs a line } var @!ch : char; { current output character } @!temp : 0..buf_size; begin if limit > 0 then begin for temp := 0 to limit - 1 do begin ch := xchr[buffer[temp]]; f^ := ch; put(f); end; end; write_ln(f); end ; @* Reporting errors to the user. Syntax 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. The actual error indications are provided by a procedure called |error|. @d err_print(#)==begin new_line; print(#); error; end @= procedure error; {prints '\..' and location of error message} var j: 0..out_buf_size; {index into |out_buf|} @!k,@!l: 0..buf_size; {indices into |buffer|} begin @; update_terminal; mark_error; end; @ The error locations can be indicated by using the global variables |loc| and |line|, which tell respectively the first unlooked-at position in |buffer| and the current line number. This routine should be modified on systems whose standard text editor has special line-numbering conventions. @^system dependencies@> @= begin print('. ('); print_ln('l.', tex_line:1, ')'); if loc>=limit then l:=limit else l:=loc; for k:=1 to l do if buffer[k-1]=tab_mark then print(' ') else print(xchr[buffer[k-1]]); {print the characters already read} new_line; for k:=1 to l do print(' '); {space out the next line} for k:=l+1 to limit do print(xchr[buffer[k-1]]); {print the part not yet read} new_line; {this separates the message from future asterisks} end @ 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 \.{SCANWEAVE}. 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_SCANWEAVE| should be copied into the |jump_out| procedure, followed by a call to a system procedure that terminates the program. @d fatal_error(#)==begin new_line; print(#); error; mark_fatal; jump_out; end @= procedure jump_out; begin goto end_of_SCANWEAVE; end; @ @= @!tex_line:integer; {the number of the current line in the main \TeX\ file } @!out_tex_line:integer; { the number of the line in the output \TeX\ file } @!limit:0..buf_size; {the last character position occupied in the buffer} @!loc:0..buf_size; {the next character position to be read from the buffer} @!input_has_ended: boolean; {if |true|, there is no more input} @!output_enabled: boolean; {if |true|, we are copying input lines to output } @ @= open_input; limit:=0; loc:=1; buffer[0]:=" "; tex_line:=0; out_tex_line:=0; input_has_ended:=false; output_enabled := true; @ In this section we determine whether the current line is the beginning of a section (`\.{\\M}' or `\.{\\N}' at beginning of line, followed immediately by a section number) and, if so, whether the section has been modified (`\.{\\*.}' following the number). We update the |output_enabled| flag according. Additionally, if the index section is detected (`\.{\\inx}'), we set the |input_has_ended| flag to terminate the program and set |output_enabled| to false to keep the \.{\\inx} command from being copied to the output file. @d numeric_digit_at(#)==( (buffer[#] <= "9") and (buffer[#] >= "0") ) @d third_char_matches(#)==(buffer[temp_index+2] = #) @d second_char_matches(#)==(buffer[temp_index+1] = #) and third_char_matches @d char_matches(#)==(buffer[temp_index] = #) @d three_chars_match(#)==char_matches(#) and second_char_matches @= temp_index := 1; if (limit > 3) and (buffer[0] = "\") then if ( char_matches("M") or char_matches("N") ) and numeric_digit_at(2) then @ else if three_chars_match("i")("n")("x") then begin output_enabled := false; input_has_ended := true; end; @ Starting at the first digit of the section number, search for `\.{\\*.}', which indicates that this is a changed section. Discontinue the search if `\.{\\*.}' is found or the current position is no longer a numeric digit, which means we have moved past the section number without finding it. @= begin output_enabled := false; keep_looking := true; temp_index := 3; while (not output_enabled) and keep_looking do begin output_enabled := three_chars_match("\")("*")("."); keep_looking := numeric_digit_at(temp_index) ; incr(temp_index); end; end @ The command to generate the table of contents (`\.{\\con}') is normally the last line in a \TeX\ file generated by \.{WEAVE}. Part of its function is to terminate \TeX\ gracefully by generating a `\.{\\bye}' command or equivalent after printing the contents. Since we are dropping the `\.{\\con}' command, we must issue the `\.{\\bye}' command directly. @= buffer[0]:="\"; buffer[1]:="b"; buffer[2]:="y"; buffer[3]:="e"; limit:=4; output_ln(out_tex_file); incr(out_tex_line); @ The |get_line| procedure is called to read in the next line and scan it. We will output an ``I'm alive!'' dot to the terminal every 100 input lines and a new line every 2000. @p procedure get_line; {inputs the next line} label exit; var @!keep_looking : boolean;@!temp_index : integer; begin input_has_ended:=not input_ln(tex_file); if input_has_ended then begin output_enabled := false; return; end else begin incr(tex_line); if (tex_line mod 100) = 0 then begin print('.'); if (tex_line mod 2000) = 0 then new_line; end; @; buffer[limit]:=" "; end; exit: end; @* The main program. We have defined plenty of procedures, and it is time to put the last pieces of the puzzle in place. Here is where \.{SCANWEAVE} starts, and where it ends. @^system dependencies@> @p begin initialize; @; print_ln(banner); {print a ``banner line''} @; end_of_SCANWEAVE: @; stat @;@+tats@;@/ @t\4\4@>{here files should be closed if the operating system requires it} @; end. @ In this procedure we read the input a line at time. We output an ``I'm alive!'' dot to the terminal every 100 output lines and a new line every 2000. @= while not input_has_ended do begin get_line; if output_enabled then begin output_ln(out_tex_file); incr(out_tex_line); end; end; @ @= stat print_nl('Line count statistics:'); print_nl(tex_line:1,' lines in TeX file'); print_nl(out_tex_line:1,' lines in output TeX file'); print_nl(' '); tats @ 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@> @= 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. This module should be replaced, if necessary, by changes to the program that are necessary to make \.{SCANWEAVE} 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. Here is a cross-reference table for the \.{SCANWEAVE} 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 ``ASCII code'' are indexed here too.