2  INTRODUCTION  WEBMERGE §1

1. Introduction. This program takes a file and merges multiple change files into it, creating a new file and a composite change file. If it bears an uncanny resemblance to Donald Knuth’s , this is no accident, since it was cloned from .

The program uses a few features of the local Modula-2 compiler (Logitech MS-DOS) that may need to be changed in other installations:

1) 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.

System-dependent portions of can be identified by looking at the entries for ‘system dependencies’ in the index below.

The “banner line” defined here should be changed whenever is modified. define banner ≡ ´ ␣ ␣ ␣ ␣ ´

2. The program begins with a fairly normal header, made up of pieces that will mostly be filled in later. The input comes from files web file and change file (which is implemented as an array of files), the new output goes to file out web file, and the new change file output goes to file out change file. module web merge; ⟨ Import List 5 ⟩

const ⟨ Constants in the outer block 6 ⟩ type ⟨ Types in the outer block 12 ⟩ var ⟨ Globals in the outer block 7 ⟩

3. This procedure initializes the module procedure initialize;

var ⟨ Local variables for initialization 16 ⟩ begin

⟨ Set initial values 8 ⟩ end initialize;

4. 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 ’s memory usage are desired.

define debug ≡ (∗ change this to ‘debug ≡ ’ when debugging ∗) define gubed ≡ (∗ change this to ‘gubed ≡ ’ when debugging ∗) format debug ≡ begin format gubed ≡ end define stat ≡ (∗ change this to ‘stat ≡ ’ when gathering usage statistics ∗) define tats ≡ (∗ change this to ‘tats ≡ ’ when gathering usage statistics ∗) format stat ≡ begin format tats ≡ end

§5  WEBMERGE  INTRODUCTION 3

5. In order to keep this module reasonably free of notations that are uniquely PASCALesque, a few macro definitions for low-level output instructions are introduced here. All of the terminal-oriented commands in the remainder of the module will be stated in terms of simple primitives. The boxes signify words that must not be forced to uppercase when the program is d, since Modula-2 is case-sensitive.

define pr char ≡ \underline{\overline{␣ ␣}} (∗ put a given character into the output file ∗) define pr string ≡ \underline{\overline{␣ ␣}} (∗ put a given string into the output file ∗) define rd string ≡ \underline{\overline{␣ ␣}} (∗ read a given string from the input file ∗) define pr card ≡ \underline{\overline{␣ ␣}} (∗ put a given cardinal into the output file, in decimal notation, using

only as many digit positions as necessary ∗)

define form feed ≡ \underline{\overline{␣ ␣}} (∗ control code for form feed ∗) define new line ≡ \underline{\overline{␣ ␣}} (∗ advance to a new line in the output file ∗) define print string (#) ≡ pr string (#) (∗ put a given string into the output file ∗) define read string (#) ≡ rd string (#) (∗ read a given string from the input file ∗) define print cardinal (#) ≡ pr card (#, 1) (∗ put a given cardinal into the output file, in decimal notation,

using only as many digit positions as necessary ∗)

define new page ≡ pr char (form feed ) (∗ advance to a new page in the output file ∗) define print ln(#) ≡ pr string (#); new line;

(∗ put a given string into the output file, followed by a new line ∗)

define print char (#) ≡ pr char (#) (∗ put a given character into the output file ∗) ⟨ Import List 5 ⟩ ≡

from \underline{\overline{␣ ␣}} import pr string , rd string , pr char , pr card , new line; from ascii import form feed ;

See also sections 10 and 20. This code is used in section 2.

6. Let’s define a few constants. ⟨ Constants in the outer block 6 ⟩ ≡

buf size = 1000; (∗ maximum length of input line ∗) max change files = 6; (∗ maximum number of simultaneous change files ∗) file name len = 200; (∗ length of a file name ∗)

This code is used in section 2.

7. 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.

define spotless = 0 (∗ history value for normal jobs ∗)

define harmless message = 1 (∗ history value when non-serious info was printed ∗)

define error message = 2 (∗ history value when an error was noted ∗)

define fatal message = 3 (∗ history value when we had to stop prematurely ∗)

define mark harmless ≡

if history = spotless then

history ← harmless message; end ;

define mark error ≡ history ← error message

define mark fatal ≡ history ← fatal message

define err print (#) ≡ print ln(#); error ; ⟨ Globals in the outer block 7 ⟩ ≡ history : [spotless . . fatal message]; (∗ how bad was this run? ∗)

See also sections 14, 21, 26, and 28. This code is used in section 2.

8. ⟨ Set initial values 8 ⟩ ≡ history ← spotless ;

See also sections 15, 17, and 18. This code is used in section 3.

9. 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. The system passes a program status value back to DOS. We use fatal error to terminate the program abnormally.

define fatal error (#) ≡ mark fatal ; print ln(#); doscall (˝ 4C, history ); ⟨ Terminate program, converting history to program exit status 9 ⟩ ≡

doscall (˝ 4C, history );

This code is used in section 48.

10. If we are going to use doscall we have to import it from system. ⟨ Import List 5 ⟩ +≡

from system import doscall ;

11. Syntax errors are reported to the user by saying

‘err print (´ ␣ ␣ ´ )’,

followed by abnormal termination 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 . procedure error ; (∗ prints ’ ’ and location of error message ∗)

var k, l: [0 . . buf size]; (∗ indices into buffer ∗)

begin ⟨ Print error location 45 ⟩; mark error ;

§12  WEBMERGE  THE CHARACTER SET 5

end error ; 12. The character set. One of the main goals in the design of has been to make it readily portable between a wide variety of computers. Yet 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 and (and !) is converted to an internal sevenbit 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 only because it is the code used for preprocessed constants like . If you are writing a program in that makes use of such one-character constants, you should convert your input to ASCII form, like , and do. Otherwise ’s internal coding scheme does not affect you.

This procedure may not be necessary, since virtually all implementations of Modula-2 use ASCII. Modula-2 is much more standardized than PASCAL.

Here is a table of the standard visible ASCII codes:

0  1  2  3  4  5  6  7

          ´ 040  ␣       !       "       #       $       %       &       '
          ´ 050  (       )       *       +       ,       -       .       /
          ´ 060  0       1       2       3       4       5       6       7
          ´ 070  8       9       :       ;       <       =       >       ?
          ´ 100  @       A       B       C       D       E       F       G
          ´ 110  H       I       J       K       L       M       N       O
          ´ 120  P       Q       R       S       T       U       V       W
          ´ 130  X       Y       Z       [       \       ]       ^_
          ´ 140  `       a       b       c       d       e       f       g
          ´ 150  h       i       j       k       l       m       n       o
          ´ 160  p       q       r       s       t       u       v       w
          ´ 170  x       y       z       {       |       }       ~

(Actually, of course, code ´ 040 is an invisible blank space.) Code ´ 136 was once an upward arrow ( ), and code ´ 137 was once a left arrow ( ), in olden times when the first draft of ASCII code was prepared; but works with today’s standard ASCII in which those codes represent circumflex and underline as shown. ⟨ Types in the outer block 12 ⟩ ≡

ASCII code = [0 . . 127]; (∗ seven-bit numbers, a subrange of the integers ∗)

See also section 19. This code is used in section 2.

6  THE CHARACTER SET  WEBMERGE §13

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 assumes that it is being used with a Modula-2 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 PASCALs 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.

As specified above, this may not be necessary for Modula-2, but since this module was mostly cannibalized from , this section was copied too.

define text char ≡ char (∗ the data type of characters in text files ∗)

define first text char = 0 (∗ ordinal number of the smallest element of text char ∗)

define last text char = 127 (∗ ordinal number of the largest element of text char ∗)

14. The  ,  and  processors convert between ASCII code and the user’s external

character set by means of arrays xord and xchr that are analogous to Modula-2’s ord and chr functions. ⟨ Globals in the outer block 7 ⟩ +≡ xord : array text char of ASCII code; (∗ specifies conversion of input characters ∗) xchr : array ASCII code of text char ; (∗ specifies conversion of output characters ∗)

15. If we assume that every system using 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 ´ ´ ´ that appears in the present file might be encoded in, say, EBCDIC code on the external medium on which it resides, but will convert from this external code to ASCII and back again. Therefore the assignment statement ´ ´ will appear in the corresponding PASCAL file, and PASCAL will compile this statement so that xchr [65] receives the character in the external (char ) code. Note that it would be quite incorrect to say ´ , because is a constant of type integer , not char , and because we have = 65 regardless of the external character set. ⟨ Set initial values 8 ⟩ +≡

xchr [´ 40 ] ← ´ ␣´ ; xchr [´ 41 ] ← ´ ´ ; xchr [´ 42 ] ← ´ ´ ; xchr [´ 43 ] ← ´ #´ ; xchr [´ 44 ] ← ´ $´ ; xchr [´ 45 ] ← ´ %´ ; xchr [´ 46 ] ← ´ ´ ; xchr [´ 47 ] ← \underline{\overline{}}´ ; xchr [´ 50 ] ← ´ ´ ; xchr [´ 51 ] ← ´ ´ ; xchr [´ 52 ] ← ´ ´ ; xchr [´ 53 ] ← ´ ´ ; xchr [´ 54 ] ← ´ ´ ; xchr [´ 55 ] ← ´ ´ ; xchr [´ 56 ] ← ´ ´ ; xchr [´ 57 ] ← ´ ´ ; xchr [´ 60 ] ← ´ ´ ; xchr [´ 61 ] ← ´ ´ ; xchr [´ 62 ] ← ´ ´ ; xchr [´ 63 ] ← ´ ´ ; xchr [´ 64 ] ← ´ ´ ; xchr [´ 65 ] ← ´ ´ ; xchr [´ 66 ] ← ´ ´ ; xchr [´ 67 ] ← ´ ´ ; xchr [´ 70 ] ← ´ ´ ; xchr [´ 71 ] ← ´ ´ ; xchr [´ 72 ] ← ´ ´ ; xchr [´ 73 ] ← ´ ´ ; xchr [´ 74 ] ← ´ ´ ; xchr [´ 75 ] ← ´ ´ ; xchr [´ 76 ] ← ´ ´ ; xchr [´ 77 ] ← ´ ´ ; xchr [´ 100 ] ← ´ ´ ; xchr [´ 101 ] ← ´ ´ ; xchr [´ 102 ] ← ´ ´ ; xchr [´ 103 ] ← ´ ´ ; xchr [´ 104 ] ← ´ ´ ; xchr [´ 105 ] ← ´ ´ ; xchr [´ 106 ] ← ´ ´ ; xchr [´ 107 ] ← ´ ´ ; xchr [´ 110 ] ← ´ ´ ; xchr [´ 111 ] ← ´ ´ ; xchr [´ 112 ] ← ´ ´ ; xchr [´ 113 ] ← ´ ´ ; xchr [´ 114 ] ← ´ ´ ; xchr [´ 115 ] ← ´ ´ ; xchr [´ 116 ] ← ´ ´ ; xchr [´ 117 ] ← ´ ´ ; xchr [´ 120 ] ← ´ ´ ; xchr [´ 121 ] ← ´ ´ ; xchr [´ 122 ] ← ´ ´ ; xchr [´ 123 ] ← ´ ´ ; xchr [´ 124 ] ← ´ ´ ; xchr [´ 125 ] ← ´ ´ ; xchr [´ 126 ] ← ´ ´ ; xchr [´ 127 ] ← ´ ´ ; xchr [´ 130 ] ← ´ ´ ; xchr [´ 131 ] ← ´ ´ ; xchr [´ 132 ] ← ´ ´ ; xchr [´ 133 ] ← ´ ´ ; xchr [´ 134 ] ← ´ ´ ; xchr [´ 135 ] ← ´ ´ ; xchr [´ 136 ] ← ´ ´ ; xchr [´ 137 ] ← ´ ´ ; xchr [´ 140 ] ← ´ ` ´ ; xchr [´ 141 ] ← ´ ´ ; xchr [´ 142 ] ← ´ ´ ; xchr [´ 143 ] ← ´ ´ ; xchr [´ 144 ] ← ´ ´ ; xchr [´ 145 ] ← ´ ´ ; xchr [´ 146 ] ← ´ ´ ; xchr [´ 147 ] ← ´ ´ ; xchr [´ 150 ] ← ´ ´ ; xchr [´ 151 ] ← ´ ´ ; xchr [´ 152 ] ← ´ ´ ; xchr [´ 153 ] ← ´ ´ ; xchr [´ 154 ] ← ´ ´ ; xchr [´ 155 ] ← ´ ´ ; xchr [´ 156 ] ← ´ ´ ; xchr [´ 157 ] ← ´ ´ ; xchr [´ 160 ] ← ´ ´ ; xchr [´ 161 ] ← ´ ´ ; xchr [´ 162 ] ← ´ ´ ; xchr [´ 163 ] ← ´ ´ ; xchr [´ 164 ] ← ´ ´ ; xchr [´ 165 ] ← ´ ´ ; xchr [´ 166 ] ← ´ ´ ; xchr [´ 167 ] ← ´ ´ ; xchr [´ 170 ] ← ´ ´ ; xchr [´ 171 ] ← ´ ´ ; xchr [´ 172 ] ← ´ ´ ; xchr [´ 173 ] ← ´ ´ ; xchr [´ 174 ] ← ´ ´ ; xchr [´ 175 ] ← ´ ´ ; xchr [´ 176 ] ← ´ ´ ; xchr [0] ← ´ ␣´ ; xchr [´ 177 ] ← ´ ␣´ ; (∗ these ASCII codes are not used ∗)

16. 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 initialization 16 ⟩ ≡ i: [0 . . last text char ];

This code is used in section 3.

17. Here now is the system-dependent part of the character set. If is being implemented on a gardenvariety 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

for i ← 1 to ´ 37 do xchr [i] ← chr (i); end ;

’s character set is essentially identical to MIT’s, even with respect to characters less than ´ 40 .

Changes to the present module will make more friendly on computers that have an extended character set, so that one can type things like 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 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 does not contain any of the non-ASCII characters, because it is intended to be used with all implementations of . It is based on , 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.) ⟨ Set initial values 8 ⟩ +≡

for i ← 1 to ´ 37 do

xchr [i] ← ´ ␣´ ; end ;

18. The following system-independent code makes the xord array contain a suitable inverse to the information in xchr . ⟨ Set initial values 8 ⟩ +≡

for i ← first text char to last text char do

xord [chr (i)] ← ´ 40 ; end ; for i ← 1 to ´ 176 do

§19  WEBMERGE  FILE HANDLING 9

xord [xchr [i]] ← i; end ; 19. File Handling. ⟨ Types in the outer block 12 ⟩ +≡

change file range = [1 . . max change files ];

20. Here we define the symbols for use with file handling.

define lookup file ≡ \underline{\overline{␣ ␣}} define close file ≡ \underline{\overline{␣ ␣}} define open input file(#) ≡ lookup file(#, filename, false); define open output file(#) ≡ lookup file(#, filename, true); define close input file(#) ≡ close file(#); define close output file(#) ≡ close file(#); define end file ≡ \underline{\overline{}} define null char ≡ \underline{\overline{␣ ␣}} define file responses ≡ \underline{\overline{␣ ␣}} define end of line(#) ≡ (ch = eol ) define end of file(#) ≡ (#.end file) define read char ≡ \underline{\overline{␣ ␣}} define write char ≡ \underline{\overline{␣ ␣}} define input char (#) ≡ read char (#, ch); define output char (#) ≡ write char (#, ch) define read ln(#) ≡

while ¬end of line(#) do

input char (#); end ;

define write ln(#) ≡ write char (#, eol ); define text file ≡ \underline{\overline{␣ ␣}}

⟨ Import List 5 ⟩ +≡

10  FILE HANDLING  WEBMERGE §21

from \underline{\overline{␣ ␣}} import lookup file, file responses , read char , write char , text file, close file; from ascii import eol , null char ; 21. Input goes into an array called buffer . The two web files, (web file and out web file), and the output change file (out ch file), are defined as separate entities. The change files are implememted as an array of files and the symbol change file always refers to the change file which is currently active.

define change file ≡ change files [active change file]

define changing ≡ (active change file > 0)

define change line ≡ the change line[active change file]

define change buffer ≡ the change buffer [active change file]

define change file is open ≡ the change flags [active change file] define change limit ≡ the change limit [active change file] define change changing ≡

if changing then

last active change file ← active change file; change line ← line; active change file ← 0; line ← web line;

else

active change file ← last active change file; web line ← line; line ← change line; end ;

define output change line ≡ output ln(out change file); inc(out ch line); ⟨ Globals in the outer block 7 ⟩ +≡ buffer : array [0 . . buf size] of ASCII code; web file, out web file, out change file: text file; (∗ main input web file and the output files ∗) change files : array change file range of text file; (∗ the input change files ∗) filename: array [0 . . file name len − 1] of char ; more names : boolean; (∗ flag indicating there are more change files ∗)

22. 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], …, 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.

We assume that none of the ASCII code values of buffer [j] for 0 ≤ j < limit is equal to 0, ´ 177 , line feed , form feed , or carriage return. procedure input ln(var f : text file): boolean; (∗ inputs a line or returns false ∗)

var final limit : [0 . . buf size]; (∗ limit without trailing blanks ∗)

ch: char ; (∗ current input character ∗) line pres : boolean; (∗ temporary result of procedure ∗)

begin limit ← 0; final limit ← 0; ch ← ´ ´ ; if end of file(f) then

line pres ← false else

while ¬end of line(f) do input char (f); if ch = null char then

return false

end ;

buffer [limit ] ← xord [ch]; inc(limit ); if buffer [limit − 1] ≠ ␣ then

final limit ← limit end ; if limit = buf size then

while ¬end of line(f) do

input char (f) end ; dec(limit ); (∗ keep buffer [buf size] empty ∗) err print (´ ␣ ␣ ␣ ␣ ´ ); loc ← 0;

end ; end ; read ln(f); limit ← final limit ; line pres ← true;

end ; return line pres ;

end input ln;

23. The output ln procedure writes the next line of output from the buffer array to the specified file. 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

for temp ← 0 to limit − 1 do

ch ← xchr [buffer [temp]]; output char (f); end ;

end ; write ln(f);

end output ln; 24. In this section we open all of the files. Since we do not know how many change files are to be input (although the maximum is max change files ), we must process them one at a time.

define next file(#) ≡ filename[0] ← ´ ␣´ ; print ln(#); read string (filename); new line;

print ln(filename); new line; more names ← (filename[0] ≠ ´ ´ );

⟨ Initialize the file system 24 ⟩ ≡ next file(´ ␣ ´ ); open input file(web file); next file(´ ´ ); open output file(out web file); next file(´ ´ ); open output file(out change file); more names ← true; active change file ← 0;

while more names do

⟨ Get next change file and open it 25 ⟩; end ; line ← 0; web line ← 0; out web line ← 0; out ch line ← 0; if active change file = 0 then

fatal error (´ ␣ ␣ ␣ ´ ); end ; for active change file ← 1 to number of actual change files do

change line ← 0; change file is open ← true; prime the change buffer ; end ; change changing ; limit ← 0; loc ← 1; buffer [0] ← ␣ ; input has ended ← false;

This code is used in section 48.

25. This section is highly system-dependent. We have to determine if there are more change files, probably from the command line, and open the next one. If there are no more, we set the variable more names accordingly. ⟨ Get next change file and open it 25 ⟩ ≡

next file(´ ´ ); more names ← more names ∧ (active change file < max change files );

if more names then

inc(active change file); number of actual change files ← active change file; open input file(change file);

end ;

This code is used in section 24.

§26  WEBMERGE  CHANGE FILE HANDLING 13

26. Change File Handling. ⟨ Globals in the outer block 7 ⟩ +≡ line: cardinal ; (∗ the number of the current line in the current file ∗) web line: cardinal ; (∗ the number of the current line in the main web file ∗) out web line: cardinal ; (∗ the number of the line in the output web file ∗) out ch line: cardinal ; (∗ the number of the line in the output change file ∗) the change line: array change file range of cardinal ;

(∗ the number of the current line in the various change files ∗)

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 ∗)

27. As we change changing from true to false and back again, we must remember to swap the values of change line and web line so that the err print routine will be sure to report the correct line number.

28. When changing is false, the next line of change file is kept in change buffer [0 . . change limit ], for purposes of comparison with the next line of web file. After the change file has been completely input, we set change limit ← 0, so that no further matches will be made.

define close change file ≡ close input file(change file); change file is open ← false; ⟨ Globals in the outer block 7 ⟩ +≡ the change buffer : array change file range of array [0 . . buf size] of ASCII code; the change limit : array change file range of [0 . . buf size];

(∗ the last position occupied in change buffer ∗)

the change flags : array change file range of boolean; (∗ flag indicating change file is still open ∗) active change file: [0 . . max change files ]; (∗ change file range plus 0 to indicate the main web file ∗) last active change file, number of actual change files : change file range; (∗ change file indexes ∗)

29. Here’s a simple function procedure that checks if the two buffers are different. procedure lines dont match( ): boolean;

var k: [0 . . buf size]; (∗ index into the buffers ∗)

begin

if (change limit ≠ limit ) ∨ (¬change file is open) then

return true; end ; if limit > 0 then

for k ← 0 to limit − 1 do

if change buffer [k] ≠ buffer [k] then

return true; end ; end ; end ; return false;

14  CHANGE FILE HANDLING  WEBMERGE §30

end lines dont match; 30. Procedure prime the change buffer sets change buffer in preparation for the next matching operation. Blank lines in the change file are not used for matching. This procedure is called only when changing is true; hence error messages will be reported correctly. procedure prime the change buffer ;

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 31 ⟩; ⟨ Skip to the next nonblank line; return if end of file 32 ⟩; ⟨ Move buffer and limit to change buffer and change limit 33 ⟩;

end prime the change buffer ;

31. While looking for a line that begins with in the change file, we allow lines that begin with , as long as they don’t begin with or (which would probably indicate that the change file is fouled up).

define lowercasify ≡

if (buffer [1] ≥ ) ∧ (buffer [1] ≤ ) then

buffer [1] ← buffer [1] + − ; end ;

⟨ Skip over comment lines in the change file; return if end of file 31 ⟩ ≡

loop inc(change line); if ¬input ln(change file) then

close change file; return ; elsif (limit ≥ 2) ∧ (buffer [0] = ) then

lowercasify ;

if buffer [1] = then

exit ; elsif (buffer [1] = ) ∨ (buffer [1] = ) then

loc ← 2; err print (´ ␣ ␣ ␣ ␣ ␣ ´ ); end ;

end ; end ;

This code is used in section 30.

32. Here we are looking at lines following the . ⟨ Skip to the next nonblank line; return if end of file 32 ⟩ ≡

repeat inc(change line); if ¬input ln(change file) then

err print (´ ␣ ␣ ␣ ␣ ␣ ´ ); close change file; return ; end ;

until limit > 0;

This code is used in section 30.

33. ⟨ Move buffer and limit to change buffer and change limit 33 ⟩ ≡ change limit ← limit ; if limit > 0 then

for k ← 0 to limit − 1 do

change buffer [k] ← buffer [k]; end ;

end ;

This code is used in sections 30 and 34.

34. The following procedure is used to see if the next change entry should go into effect for any of the change files; 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 the change buffer for one of the change files. If none match, there’s nothing more to do; but if so, a change is called for: All of the text down to the is supposed to match. An error message is issued if any discrepancy is found. Then the procedure prepares to read the next line from the selected change file. procedure check change; (∗ switches to change file if the buffers match ∗)

var n: cardinal ; (∗ the number of discrepancies found ∗) k: [0 . . buf size]; (∗ index into the buffers ∗) none match: boolean; (∗ none of the change files match ∗) temp index : [1 . . max change files + 1]; prev history : [spotless . . fatal message];

(∗ previous value of history ∗) flush needed : array change file range of boolean;

(∗ flags indicating a particular change file must be flushed because of overlap ∗)

begin ⟨ Determine if the web file line matches any of the change files 37 ⟩;

if none match then

return ; end ; n ← 0; ⟨ Write to the output change file 38 ⟩; loop

⟨ Flush any other change files which also match the current line 35 ⟩; change changing ; (∗ now it’s true ∗) inc(line); if ¬input ln(change file) then

err print (´ ␣ ␣ ␣ ␣ ␣ ´ ); change limit ← 0; change changing ;

(∗ false again ∗) return ;

end ; ⟨ If the current line starts with , report any discrepancies and return 39 ⟩; ⟨ Move buffer and limit to change buffer and change limit 33 ⟩; change changing ; (∗ now it’s false ∗) inc(line); if ¬input ln(web file) then

err print (´ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); input has ended ← true; return ; end ; change changing ; (∗ now it’s true ∗) if lines dont match( ) then

inc(n) else

output change line; end ; change changing ; (∗ now it’s false ∗)

end ; end check change; 35. If any other change files match the same line as the currently active change file, they must be flushed, since does not allow overlapping change files. We make two passes through the list of change files because the call to prime the change buffer if a flush occurs will modify buffer and interfere with further comparisons.

define this line matches ≡ (¬lines dont match( )) ⟨ Flush any other change files which also match the current line 35 ⟩ ≡

for active change file ← 1 to number of actual change files do flush needed [active change file] ← (active change file ≠ last active change file) ∧ this line matches ; if flush needed [active change file] then

prev history ← history ; print string (´ ␣ ␣ ␣ ␣´ ); print cardinal (last active change file); print string (´ ␣ ␣´ ); print cardinal (active change file); print string (´ ␣ ␣´ ); print cardinal (active change file); err print (´ ␣ ´ ); history ← prev history ; mark harmless ;

end ; end ; for active change file ← 1 to number of actual change files do

if flush needed [active change file] then ⟨ Flush current change file entry 36 ⟩; prime the change buffer ;

end ; end ; active change file ← 0;

This code is used in section 34.

36. We flush the aborted change by skipping to the next . This procedure is almost the same as the one to skip over comment lines, except that we expect to hit the and the , do not want the error messages, and we want to exit rather than return if end-of-file is encountered. ⟨ Flush current change file entry 36 ⟩ ≡

loop inc(change line); if ¬input ln(change file) then

close change file; exit ; elsif (limit ≥ 2) ∧ (buffer [0] = ) then

lowercasify ; if buffer [1] = then

exit ; end ;

end ; end ;

This code is used in section 35.

37. We are going to compare the target line with each of the active change files. The files are searched in ascending order. The first one to match becomes the active change file.

define one matches ≡ (¬none match) ⟨ Determine if the web file line matches any of the change files 37 ⟩ ≡

none match ← true; temp index ← 1; repeat

if (temp index ≤ number of actual change files ) then

active change file ← temp index ; end ; inc(temp index ); none match ← none match ∧ lines dont match( );

until one matches ∨ (temp index > number of actual change files ); last active change file ← active change file; active change file ← 0;

This code is used in section 34.

38. ⟨ Write to the output change file 38 ⟩ ≡ change changing ; (∗ now it’s true ∗) buffer [0] ← ; buffer [1] ← ; limit ← 2; output change line; limit ← change limit ; buffer [0] ← change buffer [0]; buffer [1] ← change buffer [1]; change changing ; (∗ now it’s false ∗) output change line;

This code is used in section 34.

39. ⟨ If the current line starts with , report any discrepancies and return 39 ⟩ ≡

if (limit > 1) ∧ (buffer [0] = ) then lowercasify ; if (buffer [1] = ) ∨ (buffer [1] = ) then

loc ← 2; err print (´ ␣ ␣ ␣ ␣ ␣ ´ ); elsif buffer [1] = then

output change line; if n > 0 then

loc ← 2; print string (´ ␣ ␣´ ); print cardinal (n); err print (´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ );

end ; return ;

end ; end ;

This code is used in section 34.

§40  WEBMERGE  MAIN INPUT LOOP 19

40. Main Input Loop. ⟨ Read the input 40 ⟩ ≡

while ¬input has ended do get line; output ln(out web file); inc(out web line); if (out web line mod 100) = 0 then

print char (´ ´ );

if (out web line mod 2000) = 0 then

new line; end ;

end ;

if changing then

output change line; end ;

end ;

This code is used in section 48.

41. The get line procedure is called to put the next line of merged input into the buffer and update the other variables appropriately. A space is placed at the right end of the line. procedure get line; (∗ inputs the next line ∗)

var previous change mode: boolean; begin

repeat previous change mode ← changing ;

if changing then

⟨ Read from change file and maybe turn off changing 43 ⟩; else

⟨ Read from web file and maybe turn on changing 42 ⟩; end ;

until previous change mode = changing ; loc ← 0; buffer [limit ] ← ␣ ;

end get line;

42. ⟨ Read from web file and maybe turn on changing 42 ⟩ ≡ inc(line); if ¬input ln(web file) then

input has ended ← true else

check change; end ;

This code is used in section 41.

20  MAIN INPUT LOOP  WEBMERGE §43

43. ⟨ Read from change file and maybe turn off changing 43 ⟩ ≡ inc(line); if ¬input ln(change file) then

err print (´ ␣ ␣ ␣ ␣ ␣ ´ ); buffer [0] ← ; buffer [1] ← ; limit ← 2; end ; if limit > 1 then

(∗ check if the change has ended ∗) if buffer [0] = then

lowercasify ;

if (buffer [1] = ) ∨ (buffer [1] = ) then

loc ← 2; err print (´ ␣ ␣ ␣ ␣ ␣ ´ ); elsif buffer [1] = then

output change line; limit ← 0; output change line; change changing ;

active change file ← last active change file; prime the change buffer ; active change file ← 0; end ;

end ; end ;

This code is used in section 41.

44. 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 web file. ⟨ Check that all changes have been read 44 ⟩ ≡

close input file(web file); close output file(out web file); close output file(out change file); for active change file ← 1 to number of actual change files do

if change file is open then

close change file;

if change limit ≠ 0 then (∗ changing is false ∗) for loc ← 0 to change limit do

buffer [loc] ← change buffer [loc]; end ; limit ← change limit ; line ← web line; loc ← change limit ; err print (´ ␣ ␣ ␣ ␣ ␣ ␣ ´ );

end ; end ;

end ;

This code is used in section 48.

45. The error locations can be indicated by using the global variables loc, line, and changing , which tell respectively the first unlooked-at position in buffer , the current line number, and whether or not the current line is from change file or web file. This routine should be modified on systems whose standard text editor has special line-numbering conventions.

define tab mark = 9 ⟨ Print error location 45 ⟩ ≡

if changing then

print string (´ ␣ ␣ ␣´ ); print cardinal (active change file); print char (´ ␣´ ); else

print string (´ ␣ ´ ); end ; print string (´ ´ ); print cardinal (line); print ln(´ ´ ); if loc ≥ limit then

l ← limit else

l ← loc; end ; for k ← 1 to l do

if buffer [k − 1] = tab mark then

print char (´ ␣´ ) else

print char (xchr [buffer [k − 1]]); end ; (∗ print the characters already read ∗)

end ; new line; for k ← 1 to l do

print char (´ ␣´ ); end ; (∗ space out the next line ∗) for k ← l + 1 to limit do

print char (xchr [buffer [k − 1]]); end ; (∗ print the part not yet read ∗) new line; (∗ this separates the message from future output ∗)

This code is used in section 11.

46. Here we simply report the history to the user. ⟨ Print the job history 46 ⟩ ≡

case history of

spotless : print ln(´ ␣ ␣ ␣ ´ ) | harmless message: print ln(´ ␣ ␣ ␣ ␣ ␣ ␣ ´ ) | error message: print ln(´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ) | fatal message: print ln(´ ␣ ␣ ␣ ␣ ␣ ␣ ´ )

end ; (∗ there are no other cases ∗)

This code is used in section 48.

47. ⟨ Print statistics about line counts 47 ⟩ ≡ stat

new line; print ln(´ ␣ ␣ ´ ); print cardinal (web line); print ln(´ ␣ ␣ ␣ ␣ ␣ ´ ); for active change file ← 1 to number of actual change files do

print cardinal (change line); print string (´ ␣ ␣ ␣ ␣ ␣´ ); print cardinal (active change file); new line;

end ; print cardinal (out web line); print ln(´ ␣ ␣ ␣ ␣ ␣ ´ ); print cardinal (out ch line); print ln(´ ␣ ␣ ␣ ␣ ␣ ´ );

tats

This code is used in section 48.

§48  WEBMERGE  MAIN PROGRAM 23

48. Main Program. This is the main program. begin

initialize; print ln(banner ); ⟨ Initialize the file system 24 ⟩; ⟨ Read the input 40 ⟩; ⟨ Check that all changes have been read 44 ⟩; ⟨ Print statistics about line counts 47 ⟩; ⟨ Print the job history 46 ⟩; ⟨ Terminate program, converting history to program exit status 9 ⟩;

end web merge.

24  INDEX  WEBMERGE §49

49. Index.

active change file: 21, 24, 25, \underline{28}, 35, 37, 43,

44, 45, 47. ascii : 5, 20. ASCII code: 12. ASCII code: \underline{12}, 14, 21, 22, 28. banner : \underline{1}, 48. begin: 4. boolean: 21, 22, 26, 28, 29, 34, 41. buf size: \underline{6}, 11, 21, 22, 23, 26, 28, 29, 30, 34. buffer : 11, \underline{21}, 22, 23, 24, 29, 31, 33, 34, 35, 36,

38, 39, 41, 43, 44, 45. cardinal : 26, 34. carriage return: 17, 22. ch: 20, \underline{22}, \underline{23}. : 32, 34, 43. : 44. change buffer : \underline{21}, 28, 29, 30, 33, 34, 38, 44. change changing : \underline{21}, 24, 34, 38, 43. change file: 2, \underline{21}, 25, 28, 31, 32, 34, 36, 43, 45. change file is open: \underline{21}, 24, 28, 29, 44. change file range: \underline{19}, 21, 26, 28, 34. change files : \underline{21}. change limit : \underline{21}, 28, 29, 30, 33, 34, 38, 44. change line: \underline{21}, 24, 27, 31, 32, 36, 47. changing : \underline{21}, 27, 28, 30, 34, 40, 41, 44, 45. char : 13, 15, 21, 22, 23. check change: \underline{34}, 42. chr : 13, 14, 17, 18. close change file: \underline{28}, 31, 32, 36, 44. close file: \underline{20}. close input file: \underline{20}, 28, 44. close output file: \underline{20}, 44. debug: \underline{4}. dec: 22. doscall : 9, 10. end: 4. end file: \underline{20}. end of file: \underline{20}, 22. end of line: \underline{20}, 22. eol : 20. err print : \underline{7}, 11, 22, 27, 31, 32, 34, 35, 39, 43, 44. error : 7, \underline{11}. error message: \underline{7}, 46. f: \underline{22}, \underline{23}. false: 20, 22, 24, 27, 28, 29, 34, 38. fatal error : \underline{9}, 24. fatal message: \underline{7}, 34, 46. file name len: \underline{6}, 21. file responses : \underline{20}. filename: 20, \underline{21}, 24. final limit : \underline{22}. first text char : \underline{13}, 18. flush needed : \underline{34}, 35. form feed : \underline{5}, 22. get line: 40, \underline{41}. gubed: \underline{4}. harmless message: \underline{7}, 46. history : \underline{7}, 8, 9, 34, 35, 46. : 39. i: \underline{16}. inc: 21, 22, 25, 31, 32, 34, 36, 37, 40, 42, 43. initialize: \underline{3}, 48. input : 5. : 22. input char : \underline{20}, 22. input has ended : 24, \underline{26}, 34, 40, 42. input ln: \underline{22}, 31, 32, 34, 36, 42, 43. integer : 15. k: \underline{11}, \underline{29}, \underline{30}, \underline{34}. l: \underline{11}. last active change file: 21, \underline{28}, 35, 37, 43. last text char : \underline{13}, 16, 18. limit : 22, 23, 24, \underline{26}, 29, 31, 32, 33, 36, 38,

39, 41, 43, 44, 45. line: 21, 24, \underline{26}, 34, 42, 43, 44, 45. line feed : 22. line pres : \underline{22}. lines dont match: \underline{29}, 34, 35, 37. loc: 22, 24, \underline{26}, 31, 39, 41, 43, 44, 45. lookup file: \underline{20}. lowercasify : \underline{31}, 36, 39, 43. mark error : \underline{7}, 11. mark fatal : \underline{7}, 9. mark harmless : \underline{7}, 35. max change files : \underline{6}, 19, 24, 25, 28, 34. more names : \underline{21}, 24, 25. n: \underline{34}. new line: \underline{5}, 24, 40, 45, 47. new page: \underline{5}. next file: \underline{24}, 25. none match: \underline{34}, 37. null char : \underline{20}, 22. number of actual change files : 24, 25, \underline{28}, 35,

37, 44, 47. one matches : \underline{37}. open input file: \underline{20}, 24, 25. open output file: \underline{20}, 24. ord : 14. out ch file: 21. out ch line: 21, 24, \underline{26}, 47. out change file: 2, 21, 24, 44. out web file: 2, 21, 24, 40, 44.

§49  WEBMERGE  INDEX 25

out web line: 24, \underline{26}, 40, 47. output : 5. output change line: \underline{21}, 34, 38, 39, 40, 43. output char : \underline{20}, 23. output ln: 21, \underline{23}, 40. pr card : \underline{5}. pr char : \underline{5}. pr string : \underline{5}. prev history : \underline{34}, 35. previous change mode: \underline{41}. prime the change buffer : 24, \underline{30}, 35, 43. print cardinal : \underline{5}, 35, 39, 45, 47. print char : \underline{5}, 40, 45. print ln: \underline{5}, 7, 9, 24, 45, 46, 47, 48. print string : \underline{5}, 35, 39, 45, 47. rd string : \underline{5}. read char : \underline{20}. read ln: \underline{20}, 22. read string : \underline{5}, 24. spotless : \underline{7}, 8, 34, 46. stat: \underline{4}. system: 10. system dependencies: \underline{1}, 2, 5, 9, 10, 13, 17,

20, 22, 25, 45. tab mark : \underline{45}. tats: \underline{4}. temp: \underline{23}. temp index : \underline{34}, 37. text char : \underline{13}, 14. text file: \underline{20}, 21, 22, 23. the change buffer : 21, \underline{28}. the change flags : 21, \underline{28}. the change limit : 21, \underline{28}. the change line: 21, \underline{26}. this line matches : \underline{35}. true: 20, 22, 24, 26, 27, 29, 34, 37, 38, 42. : 34. web file: 2, \underline{21}, 24, 28, 34, 42, 44, 45. web line: 21, 24, \underline{26}, 27, 44, 47. web merge: \underline{2}, 48. : 39, 43. write char : \underline{20}. write ln: \underline{20}, 23. xchr : \underline{14}, 15, 16, 17, 18, 23, 45. xord : \underline{14}, 16, 18, 22.

26  NAMES OF THE SECTIONS  WEBMERGE §49

⟨ Check that all changes have been read 44 ⟩ Used in section 48. ⟨ Constants in the outer block 6 ⟩ Used in section 2. ⟨ Determine if the web file line matches any of the change files 37 ⟩ Used in section 34. ⟨ Flush any other change files which also match the current line 35 ⟩ Used in section 34. ⟨ Flush current change file entry 36 ⟩ Used in section 35. ⟨ Get next change file and open it 25 ⟩ Used in section 24.

⟨ Globals in the outer block 7, 14, 21, 26, 28 ⟩ Used in section 2.

⟨ If the current line starts with , report any discrepancies and return 39 ⟩ Used in section 34.

⟨ Import List 5, 10, 20 ⟩ Used in section 2.

⟨ Initialize the file system 24 ⟩ Used in section 48. ⟨ Local variables for initialization 16 ⟩ Used in section 3.

⟨ Move buffer and limit to change buffer and change limit 33 ⟩ Used in sections 30 and 34.

⟨ Print error location 45 ⟩ Used in section 11. ⟨ Print statistics about line counts 47 ⟩ Used in section 48.

⟨ Print the job history 46 ⟩ Used in section 48.

⟨ Read from change file and maybe turn off changing 43 ⟩ Used in section 41. ⟨ Read from web file and maybe turn on changing 42 ⟩ Used in section 41.

⟨ Read the input 40 ⟩ Used in section 48. ⟨ Set initial values 8, 15, 17, 18 ⟩ Used in section 3.

⟨ Skip over comment lines in the change file; return if end of file 31 ⟩ Used in section 30. ⟨ Skip to the next nonblank line; return if end of file 32 ⟩ Used in section 30. ⟨ Terminate program, converting history to program exit status 9 ⟩ Used in section 48. ⟨ Types in the outer block 12, 19 ⟩ Used in section 2. ⟨ Write to the output change file 38 ⟩ Used in section 34.

The WEBMERGE processor

(Version 1.0)

Section Page

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 2 The character set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 5 File Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 9 Change File Handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26 13 Main Input Loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 19 Main Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 23 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 24