§1  WEBMERGE  INTRODUCTION 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 . While was developed on a VAX system using VAX Pascal, rather than the Stanford system on which was developed, the system dependencies of the latter were used in so the modifications to implement it on a particular system are almost exactly the same as for and .

The program uses a few features of the local Pascal compiler that may need to be changed in other installations:

1) Case statements have a default.

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.

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

For the VAX/VMS versions of , we add a second decimal point and number to the main version number, so we can differentiate between changes to the standard and changes local to the VAX/VMS version.

define banner ≡ ´ ␣ ␣ ␣ ␣ ␣ ´

2* . The program begins with a fairly normal header, made up of pieces that will mostly be filled in later. Actually the heading shown here is not quite normal: The program line does not mention any files. does not use the standard files input and output , and VAX/VMS Pascal does not require other external files to be specified in the program header if they are opened with the open procedure. Declarations for the external files appear later; the input comes from files web file and change file (which is implemented as an array of files), the web output goes to file out web file, the the composite change file output goes to file out change file, and term in and term out are used for debugging and diagnostics.

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

define end of WEBMERGE = 9999 { go here to wrap it up } ⟨ Compiler directives 4* ⟩ program WEBMERGE ;

label end of WEBMERGE ; { go here to finish }

const ⟨ Constants in the outer block 8 ⟩

type ⟨ Types in the outer block 9 ⟩

var ⟨ Globals in the outer block 10 ⟩ ⟨ VAX/VMS procedures 60* ⟩ ⟨ Error handling procedures 29 ⟩

2  INTRODUCTION  WEBMERGE §3

procedure initialize; var ⟨ Local variables for initialization 17 ⟩ begin ⟨ Set initial values 11 ⟩ end; 3* . 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 line counts 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

4* . VAX/VMS Pascal allows the user to specify “attributes” in the program source that affect the translation of the program into machine language. Attributes are denoted by keywords; they are set off from the rest of the source text by enclosing them in brackets. The directives shown below cause range checking and other redundant code to be eliminated when is being generated, and they give the program access to definitions in the “environment” file $ , which describes every VAX/VMS system service and VAX-11 RMS routine. Arithmetic overflow will be detected in all cases. ⟨ Compiler directives 4* ⟩ ≡

[check (none), inherit (´ $ ´ )]

{ no range check, catch arithmetic overflow, no debug overhead }

This code is used in section 2*.

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

define exit = 10 { go here to leave a procedure } define restart = 20 { go here to start a procedure again } define reswitch = 21 { go here to start a case statement again } define continue = 22 { go here to resume a loop } define done = 30 { go here to exit a loop } define found = 31 { go here when you’ve found it } define not found = 32 { go here when you’ve found something else }

6. Here are some macros for common programming idioms.

define incr (#) ≡ # ← # + 1 { increase a variable by unity } define decr (#) ≡ # ← # − 1 { decrease a variable by unity } define loop ≡ while true do { repeat over and over until a goto happens } define do nothing ≡ { empty statement } define return ≡ goto exit { terminate a procedure call } format return ≡ nil format loop ≡ xclause 7* . We assume that case statements may include a default case that applies if no matching label is found. Thus, we shall use constructions like

case x of

1: ⟨ code for x = 1 ⟩; 3: ⟨ code for x = 3 ⟩; othercases ⟨ code for x ≠ 1 and x ≠ 3 ⟩ endcases

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 and TEX allows ‘others :’ as a default label, and other Pascals 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. The original author of (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.)

define othercases ≡ otherwise { default for cases not listed explicitly } define endcases ≡ end { follows the default case in an extended case statement } format othercases ≡ else format endcases ≡ end

8. The following constants need to be defined. ⟨ Constants in the outer block 8 ⟩ ≡

buf size = 1000; { maximum length of input line }

max change files = 6; { number of simultaneous change files }

out buf size = 144; { length of output buffer, should be twice line length }

See also section 63*. This code is used in section 2*.

9. We use the constant max change files to define a valid range of change file numbers. ⟨ Types in the outer block 9 ⟩ ≡

range of change files = 1 . . max change files ;

See also sections 12, 59*, and 61*. This code is used in section 2*.

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.

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 define mark error ≡ history ← error message define mark fatal ≡ history ← fatal message

⟨ Globals in the outer block 10 ⟩ ≡ history : spotless . . fatal message; { how bad was this run? }

See also sections 14, 21*, 24, 26, 32, 34, 47, and 62*. This code is used in section 2*.

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

See also sections 15, 18, 19, 22*, and 69*. This code is used in section 2*.

§12  WEBMERGE  THE CHARACTER SET 5

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.

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 9 ⟩ +≡

ASCII code = 0 . . 127; { seven-bit numbers, a subrange of the integers }

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

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 }

define text file ≡ text

6  THE CHARACTER SET  WEBMERGE §14
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 Pascal’s ord and chr functions. ⟨ Globals in the outer block 10 ⟩ +≡ 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 11 ⟩ +≡

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 ] ← ´ ´ ; 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. Some of the ASCII codes below ´ 40 have been given symbolic names in  ,  and

because they are used with a special meaning.

define tab mark = ´ 11 { ASCII code used as tab-skip } define line feed = ´ 12 { ASCII code thrown away at end of line } define form feed = ´ 14 { ASCII code used at end of page } define carriage return = ´ 15 { ASCII code used at end of line } 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 initialization 17 ⟩ ≡ i: 0 . . last text char ;

This code is used in section 2*.

18. 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);

’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 11 ⟩ +≡

for i ← 1 to ´ 37 do xchr [i] ← ´ ␣´ ;

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

for i ← first text char to last text char do xord [chr (i)] ← ´ 40 ;

for i ← 1 to ´ 176 do xord [xchr [i]] ← i;

8  INPUT AND OUTPUT  WEBMERGE §20

20. 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 and . Therefore people who need to make modifications to all four systems should be able to do so without too many headaches.

21* . Terminal output is done by writing on file term out , which is assumed to consist of characters of type text char :

define print (#) ≡ write ln(term out , #) { ‘print ’ means write on the terminal }

define print ln(#) ≡ write ln(term out , #, chr (13), chr (10)) { ‘print ’ and then start new line } define new line ≡ write ln(term out , chr (13), chr (10)) { start new line } define print nl (#) ≡ { print information starting on a new line }

begin new line; print (#); end

⟨ Globals in the outer block 10 ⟩ +≡ term out : text file; { the terminal as an output file } term in: text file; { the user’s terminal as an input file }

22* . Different systems have different ways of specifying that the output on a certain file will appear on the user’s terminal. Here is how to open the terminal file under VAX/VMS Pascal. ⟨ Set initial values 11 ⟩ +≡

open(term out , ´ $ ´ , , carriage control ← none, record length ← 16383); rewrite(term out ); { send term out output to the terminal }

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.

define update terminal ≡ { empty the terminal output buffer }

24. The main input comes from web file and is overridden by changes in change file, which is implemented as an array of files.

define change file ≡ change files [active change file]

define changing ≡ (active change file > 0) ⟨ Globals in the outer block 10 ⟩ +≡ web file: text file; { primary input } change files : array [range of change files ] of text file; { updates } out web file, out change file: text file; { output files } active change file: 0 . . max change files ; { range of change files plus 0 to indicate no change file } last active change file, number of actual change files : range of change files ;

25. 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. procedure open input ; { prepare to read web file and write to out web file and out change file }

begin reset (web file); rewrite(out web file); rewrite(out change file);

end;

26. Input goes into an array called buffer . ⟨ Globals in the outer block 10 ⟩ +≡ buffer : array [0 . . buf size] of ASCII code;

§27  WEBMERGE  INPUT AND OUTPUT 9

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

Since the inner loop of input ln is part of ’s “inner loop”—each character of input comes in at this place—it is wise to reduce system overhead by making use of special routines that read in an entire array of characters at once. VAX/VMS Pascal allows us to use a ‘varying ’ string to read an entire line from the file. function input ln(var f : text file): boolean; { inputs a line or returns false }

label done;

var

aux buf : varying [buf size] of char ; { intermediate input buffer }

k: 0 . . buf size; { index into buffer } begin limit ← 0; if status (f) ≠ 0 then input ln ← false else begin read ln(f, aux buf , error ← continue); limit ← length(aux buf );

if limit ≥ buf size then begin print nl (´ ␣ ␣ ␣ ␣ ´ ); loc ← 0; error ; limit ← buf size − 1; end;

for k ← 1 to limit do buffer [k − 1] ← xord [aux buf [k]]; while limit > 1 do

if buffer [limit − 1] = ␣ then decr (limit ) else goto done; done: input ln ← true;

end; end;

28* . 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; aux buf : varying [buf size] of char ; { intermediate input buffer } begin if limit > 0 then

begin for temp ← 0 to limit − 1 do begin aux buf [temp + 1] ← xchr [buffer [temp]]; end; end;

aux buf .length ← limit ; write ln(f, aux buf , error ← continue);

end;

10  REPORTING ERRORS TO THE USER  WEBMERGE §29

29. Reporting errors to the user.

Syntax errors are reported to the user by saying

‘err print (´ ␣ ␣ ´ )’,

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 . define err print (#) ≡

begin new line; print (#); error ; end

⟨ Error handling procedures 29 ⟩ ≡ 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 ⟨ Print error location 30 ⟩; update terminal ; mark error ; end;

See also section 31. This code is used in section 2*.

30. 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 one of the instances of change file or web file. This routine should be modified on systems whose standard text editor has special line-numbering conventions. ⟨ Print error location 30 ⟩ ≡

begin if changing then print (´ ␣ ␣ ␣´ , active change file : 1, ´ ␣´ ) else print (´ ␣ ´ ); print ln(´ ´ , 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

This code is used in section 29.

§31  WEBMERGE  REPORTING ERRORS TO THE USER 11

31. 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 . It is used when no recovery from a particular error has been provided.

Some Pascal compilers do not implement non-local goto statements. In such cases the code that appears at label end of WEBMERGE should be copied into the jump out procedure, followed by a call to a system procedure that terminates the program.

define fatal error (#) ≡ begin new line; print (#); error ; mark fatal ; jump out ; end

⟨ Error handling procedures 29 ⟩ +≡ procedure jump out ;

begin goto end of WEBMERGE ;

12  CHANGE FILE HANDLING  WEBMERGE §32

end; 32. Change file handling.

define change line ≡ ch line array [active change file] ⟨ Globals in the outer block 10 ⟩ +≡ line: integer ; { the number of the current line in the current file } web line: integer ; { the number of the current line in the main web file } out web line: integer ; { the number of the line in the output web file } out ch line: integer ; { the number of the line in the output change file } ch line array : array [range of change files ] of integer ;

{ the number of the current line the 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 }

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

define change changing ≡

if changing then

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

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

34. 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 change buffer ≡ ch buffer array [active change file] define change limit ≡ ch limit array [active change file] define change file is open ≡ ch flag array [active change file] define terminate change file ≡ change file is open ← false;

{ here change file should be closed if the operating system requires it }

define output change line ≡ begin output ln(out change file); incr (out ch line); end

⟨ Globals in the outer block 10 ⟩ +≡ ch buffer array : array [range of change files ] of array [0 . . buf size] of ASCII code; ch limit array : array [range of change files ] of 0 . . buf size;

{ the last position occupied in change buffer }

ch flag array : array [range of change files ] of boolean; { flags indicating change file is still open }

35. Here’s a simple function that checks if the two buffers are different. 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 ) ∨ (¬change file is open) 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;

§36  WEBMERGE  CHANGE FILE HANDLING 13

exit : end; 36. 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 ;

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

exit : end;

37. 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). ⟨ Skip over comment lines in the change file; return if end of file 37 ⟩ ≡

loop begin incr (change line);

if ¬input ln(change file) then begin terminate change file; return; end;

if limit < 2 then goto continue;

if buffer [0] ≠ then goto continue;

if (buffer [1] ≥ ) ∧ (buffer [1] ≤ ) then buffer [1] ← buffer [1] + − ; { lowercasify } if buffer [1] = then goto done; if (buffer [1] = ) ∨ (buffer [1] = ) then

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

end; continue: end;

done:

This code is used in section 36.

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

repeat incr (change line);

if ¬input ln(change file) then begin err print (´ ␣ ␣ ␣ ␣ ␣ ´ ); terminate change file; return; end;

until limit > 0;

This code is used in section 36.

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

for k ← 0 to limit − 1 do change buffer [k] ← buffer [k]; end

This code is used in sections 36 and 40.

40. 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 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. procedure check change; { switches to change file if the buffers match }

label exit , done; var n: integer ; { the number of discrepancies found }

k: 0 . . buf size; { index into the buffers } none match: boolean; { none of the change files match } temp index : integer ; prev history : spotless . . fatal message; { previous value of history } flush needed : array [range of change files ] of boolean;

begin ⟨ Determine if the web file line matches any of the change files 45 ⟩; if none match then return; n ← 0; ⟨ Write to the output change file 46 ⟩; loop begin ⟨ Flush any other change files which also match the current line 43 ⟩;

change changing ; { now it’s true } incr (line); if ¬input ln(change file) then

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

{ false again } return; end;

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

begin err print (´ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); input has ended ← true; return; end;

change changing ; { now it’s true } if lines dont match then incr (n) else output change line; change changing ; { now it’s false } end;

exit : end; 41. ⟨ If the current line starts with , report any discrepancies and return 41 ⟩ ≡

if limit > 1 then

if buffer [0] = then

begin if (buffer [1] ≥ ) ∧ (buffer [1] ≤ ) then buffer [1] ← buffer [1] + − ;

{ lowercasify } if (buffer [1] = ) ∨ (buffer [1] = ) then

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

end else if buffer [1] = then

begin output change line;

if n > 0 then begin loc ← 2; err print (´ ␣ ␣´ , n : 1, ´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); end; return; end;

end

This code is used in section 40.

42. ⟨ Initialize the input system 42 ⟩ ≡ open input ; line ← 0; ⟨ Open the input change files 48 ⟩; limit ← 0; loc ← 1; buffer [0] ← ␣ ; input has ended ← false;

This code is used in section 54*.

43. 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 43 ⟩ ≡

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

begin

flush needed [active change file] ← (active change file ≠ last active change file) ∧ this line matches ; if flush needed [active change file] then

begin prev history ← history ; err print (´ ␣ ␣ ␣ ␣´ , last active change file : 1, ´ ␣ ␣´ ,

active change file : 1, ´ ␣ ␣´ , active change file : 1, ´ ␣ ´ ); 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 begin ⟨ Flush current change file entry 44 ⟩; prime the change buffer ; end;

active change file ← 0;

This code is used in section 40.

44. 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 return if end-of-file is encountered. ⟨ Flush current change file entry 44 ⟩ ≡

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

begin terminate change file; goto done; end

else if (limit ≥ 2) ∧ (buffer [0] = ) then

begin if (buffer [1] ≥ ) ∧ (buffer [1] ≤ ) then buffer [1] ← buffer [1] + − ;

{ lowercasify } if buffer [1] = then goto done; end;

end; done:

This code is used in section 43.

45. 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 45 ⟩ ≡

none match ← true; temp index ← 1;

repeat if (temp index ≤ number of actual change files ) then active change file ← temp index ;

none match ← none match ∧ lines dont match; incr (temp index ); 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 40.

46. ⟨ Write to the output change file 46 ⟩ ≡ 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 40.

47. ⟨ Globals in the outer block 10 ⟩ +≡ more names : boolean; { flag indicating there are more change files } filename: packed array [1 . . 200] of char ; 48. 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 open them one at a time. ⟨ Open the input change files 48 ⟩ ≡

more names ← true; active change file ← 0;

while more names do

begin ⟨ Get next change file and open it 49* ⟩;

end; if active change file = 0 then fatal error (´ ␣ ␣ ␣ ´ ); line ← 0; web line ← 0; out web line ← 0; out ch line ← 0; for active change file ← 1 to number of actual change files do

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

49* . This section is highly system-dependent. We have to determine if there are more change files, possibly from the command line, and open the next one. If there are no more, we set the variable more names accordingly. Since this section of code must open a variable number of files, the first plan was to copy from TEX the code which opens input text files dynamically, but it was finally decided that this was overkill. The simplistic routine which follows prompts the user for another change file name. If an end-of-file is received from the terminal, the first character of the file name is set to a blank, or the maximum number of change files is exceeded, more names is set to false.

The logical change to make here is to get the file names from the command line, if that is possible on the target system. ⟨ Get next change file and open it 49* ⟩ ≡

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

if more names then

begin ⟨ Get name of VAX/VMS change file from command line and open it 70* ⟩; end;

This code is used in section 48.

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

label restart ; begin restart : if changing then ⟨ Read from change file and maybe turn off changing 52 ⟩; if ¬changing then

begin ⟨ Read from web file and maybe turn on changing 51 ⟩;

if changing then goto restart ;

end; loc ← 0; buffer [limit ] ← ␣ ; end;

51. ⟨ Read from web file and maybe turn on changing 51 ⟩ ≡ begin incr (line); if ¬input ln(web file) then input has ended ← true else check change; end

This code is used in section 50.

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

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

if limit > 1 then { check if the change has ended }

if buffer [0] = then

begin if (buffer [1] ≥ ) ∧ (buffer [1] ≤ ) then buffer [1] ← buffer [1] + − ;

{ lowercasify } if (buffer [1] = ) ∨ (buffer [1] = ) then

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

else if buffer [1] = then begin 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 50.

53. 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 53 ⟩ ≡

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

if change file is open then

begin terminate change file; if change limit ≠ 0 then

begin { changing is false }

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

end;

This code is used in section 55.

§54  WEBMERGE  THE MAIN PROGRAM 19

54* . 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 starts, and where it ends.

begin initialize; ⟨ Initialize the input system 42 ⟩; print ln(banner ); { print a “banner line” } ⟨ Read the input 55 ⟩;

end of WEBMERGE : stat ⟨ Print statistics about line counts 56 ⟩; tats

if history < fatal message then begin close(out web file, disposition ← save, error ← continue); close(out change file, disposition ← save, error ← continue); end;

⟨ Print the job history 57 ⟩; ⟨ Convert history to program exit status 68* ⟩; end.

55. 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. ⟨ Read the input 55 ⟩ ≡

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

begin print (´ ´ );

if (out web line mod 2000) = 0 then new line; end;

if changing then output change line;

end; ⟨ Check that all changes have been read 53 ⟩;

This code is used in section 54*.

56. ⟨ Print statistics about line counts 56 ⟩ ≡ stat print nl (´ ␣ ␣ ´ ); print nl (web line : 1, ´ ␣ ␣ ␣ ␣ ´ ); for active change file ← 1 to number of actual change files do

print nl (change line : 1, ´ ␣ ␣ ␣ ␣ ␣´ , active change file : 1); print nl (out web line : 1, ´ ␣ ␣ ␣ ␣ ␣ ´ ); print nl (out ch line : 1, ´ ␣ ␣ ␣ ␣ ␣ ´ ); print nl (´ ␣´ ); tats

This code is used in section 54*.

57. 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. ⟨ Print the job history 57 ⟩ ≡

case history of

spotless : print nl (´ ␣ ␣ ␣ ´ ); harmless message: print nl (´ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); error message: print nl (´ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); fatal message: print nl (´ ␣ ␣ ␣ ␣ ␣ ␣ ´ ); end { there are no other cases }

This code is used in section 54*.

20  SYSTEM-DEPENDENT CHANGES  WEBMERGE §58

58* . System-dependent changes. Here are the remaining changes to the program that are necessary to make work on VAX/VMS.

59* . Here is the stuff for magic file operations. ⟨ Types in the outer block 9 ⟩ +≡

unsafe file = [unsafe] file of char ;

fab pointer = ↑fab$type;

nam pointer = ↑nam$type;

60* . ⟨ VAX/VMS procedures 60* ⟩ ≡ function pas$fab(var f : unsafe file): fab pointer ; external ;

See also sections 64*, 65*, 66*, and 67*. This code is used in section 2*.

61* . VAX/VMS command argument names.

define arg size ≡ 255 ⟨ Types in the outer block 9 ⟩ +≡

arg buf = packed array [1 . . arg size] of char ;

62* . Temporary buffer for storing argument strings. ⟨ Globals in the outer block 10 ⟩ +≡ arg : arg buf ; arg len: 0 . . arg size;

63* . VAX/VMS CLI interface routines. ⟨ Constants in the outer block 8 ⟩ +≡

cli$ absent = ˝ 000381F0;

cli$ concat = ˝ 0003FD29;

cli$ defaulted = ˝ 0003FD21;

cli$ locneg = ˝ 00038230;

cli$ locpres = ˝ 0003FD31;

cli$ negated = ˝ 000381F8;

cli$ present = ˝ 0003FD19;

64* . ⟨ VAX/VMS procedures 60* ⟩ +≡ function cli$present (%stdescr name : packed array [l . . u : integer ] of char ): integer ; external ; function cli$get value(%stdescr name : packed array [l1 . . u1 : integer ] of char ;

§65  WEBMERGE  SYSTEM-DEPENDENT CHANGES 21

%stdescr retbuf : packed array [l2 . . u2 : integer ] of char ): integer ; external ; 65* . Check for the presence of a command line argument; return 1 if the argument is specified, 0 if the argument is not present, and -1 if the argument is explicitly negated. If the argument is present and has a value, place its value in arg and set arg len to its length. ⟨ VAX/VMS procedures 60* ⟩ +≡ function get arg (nam : packed array [l . . u : integer ] of char ;

has val : boolean): integer ; label done, exit ; var status : integer ; k: 0 . . arg size; begin get arg ← 0; arg len ← 0; status ← cli$present (nam); if (status = cli$ negated ) ∨ (status = cli$ locneg ) then get arg ← −1 else if (status = cli$ concat ) ∨ (status = cli$ defaulted ) ∨ (status = cli$ present ) ∨ (status = cli$ locpres )

then

begin if has val then

begin if ¬odd (cli$get value(nam, arg )) then return; arg len ← arg size; while arg len > 0 do

if arg [arg len] = ´ ␣´ then decr (arg len) else goto done; done: end; get arg ← 1; end;

exit : end;

66* . The following procedures initialize the various RMS control blocks. The processing is similar for all block types; the code clears the block to zero, then sets the block id and block length fields. ⟨ VAX/VMS procedures 60* ⟩ +≡ procedure init fab(var f : fab$type);

type bytearray = packed array [1 . . fab$c bln] of 0 . . 255; var i: integer ; begin for i ← 1 to fab$c bln do f::bytearray [i] ← 0; f.fab$b bid ← fab$c bid ; f.fab$b bln ← fab$c bln; end;

procedure init nam(var n : nam$type); type bytearray = packed array [1 . . nam$c bln] of 0 . . 255; var i: integer ; begin for i ← 1 to nam$c bln do n::bytearray [i] ← 0; n.nam$b bid ← nam$c bid ; n.nam$b bln ← nam$c bln; end; 67* . Augment the string currently in arg to produce a complete file specification; produce an output parse if out parse is true, otherwise an input file. Use as default the file type passed as ext . If scrounge is true, use the principal input file, web file, for additional defaults. ⟨ VAX/VMS procedures 60* ⟩ +≡ procedure add defaults (ext : packed array [l . . u : integer ] of char ;

scrounge, out parse: boolean); var fab: fab$type; nam: nam$type; def , tmp: arg buf ; f: fab pointer ; i: 0 . . arg size; begin for i ← arg len + 1 to arg size do arg [i] ← ´ ␣´ ; for i ← 1 to arg size do def [i] ← ´ ␣´ ; def [1] ← ´ ´ ; for i ← 1 to length(ext ) do def [i + 1] ← ext [i]; for i ← 1 to arg size do tmp[i] ← ´ ␣´ ; init fab(fab); init nam(nam); fab.fab$l dna ← iaddress (def ); fab.fab$b dns ← arg size; fab.fab$l fna ← iaddress (arg ); fab.fab$b fns ← arg size; fab.fab$l nam ← iaddress (nam); nam.nam$l esa ← iaddress (tmp); nam.nam$b ess ← arg size; if out parse then fab.fab$l fop ← fab$m ofp;

if scrounge then

begin f ← pas$fab(web file); nam.nam$l rlf ← f↑.fab$l nam;

end; if odd ($parse(fab)) then

begin arg ← tmp; arg len ← nam.nam$b esl ;

end; end;

68* . The $exit system service passes a program status value back to whoever invoked ; we set sts$m inhib msg to keep error messages from appearing. It would be more in keeping with VAX/VMS conventions to use lib$stop to report error conditions (allowing exception handlers to gain control before the program exits), but setting the sts$m inhib msg causes the VAX/VMS Pascal default exception handler to reset the status value to sts$k normal . ⟨ Convert history to program exit status 68* ⟩ ≡

begin if history = harmless message then $exit (sts$k warning + sts$m inhib msg ) else if history = error message then $exit (sts$k error + sts$m inhib msg )

else if history = fatal message then $exit (sts$k severe + sts$m inhib msg ); end

This code is used in section 54*.

69* . The following code collects command line arguments from VAX/VMS, and connects external files to web file, out web file and out change file. It resets the input files just to make sure they are accessible; they are reset again at the beginning of the first pass.

define add input defaults (#) ≡ add defaults (#, false) define add output defaults (#) ≡ add defaults (#, true) define setup error (#) ≡

begin { report error during command argument processing } new line; print (#, ´ ␣´ , arg : arg len); mark fatal ; jump out ; end

⟨ Set initial values 11 ⟩ +≡ if get arg (´ ´ , true) ≤ 0 then setup error (´ ␣ ␣ ␣ ␣ ´ ); add input defaults (´ ´ , false); open(web file, arg , readonly , error ← continue); if status (web file) ≤ 0 then reset (web file, error ← continue); if status (web file) > 0 then setup error (´ ␣ ´ ´ ␣ ␣ ␣ ´ ); if get arg (´ ´ , true) ≤ 0 then do nothing ; add output defaults (´ ´ , true); open(out web file, arg , new , disposition ← delete, error ← continue); if status (out web file) ≤ 0 then rewrite(out web file, error ← continue); if status (out web file) > 0 then setup error (´ ␣ ´ ´ ␣ ␣ ␣ ␣ ␣´ ); if get arg (´ ´ , true) ≤ 0 then do nothing ; add output defaults (´ ´ , true); open(out change file, arg , new , disposition ← delete, error ← continue); if status (out change file) ≤ 0 then rewrite(out change file, error ← continue); if status (out change file) > 0 then setup error (´ ␣ ´ ´ ␣ ␣ ␣ ␣ ␣´ );

70* . The following code collects the list of change file names from the VAX/VMS command line, and connects external files to each change file present. It opens and resets the input change files just to make sure they are accessible. ⟨ Get name of VAX/VMS change file from command line and open it 70* ⟩ ≡

more names ← more names ∧ (get arg (´ ´ , true) > 0);

if more names then

begin incr (active change file); number of actual change files ← active change file; add input defaults (´ ´ , true); open(change file, arg , readonly , error ← continue); if status (change file) ≤ 0 then reset (change file, error ← continue); if status (change file) > 0 then setup error (´ ␣ ´ ´ ␣ ␣ ␣ ´ ); end;

This code is used in section 49*.

24  INDEX  WEBMERGE §71

71* . Index. Here is a cross-reference table for the 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.

The following sections were changed by the change file: 1, 2, 3, 4, 7, 13, 21, 22, 23, 27, 28, 49, 54, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71.

$exit : 68* . $parse: 67* . active change file: \underline{24}, 30, 32, 33, 34, 43, 45, 48,

49* , 52, 53, 56, 70* . add defaults : \underline{67*} , 69* . add input defaults : \underline{69*} , 70* . add output defaults : \underline{69*} . arg : \underline{62*} , 65* , 67* , 69* , 70* . arg buf : \underline{61*} , 62* , 67* . arg len: \underline{62*} , 65* , 67* , 69* . arg size: \underline{61*} , 62* , 65* , 67* . ASCII code: 12. ASCII code: \underline{12}, 14, 26, 27* , 34. aux buf : \underline{27*} , \underline{28*} . banner : \underline{1*} , 54* . begin: 3* . boolean: 27* , 32, 34, 35, 40, 47, 65* , 67* . buf size: \underline{8}, 26, 27* , 28* , 29, 32, 34, 35, 36, 40. buffer : \underline{26}, 27* , 28* , 29, 30, 35, 37, 39, 40, 41, 42,

43, 44, 46, 48, 50, 52, 53. bytearray : \underline{66*} . : 69* . : 69* . : 70* . : 69* . carriage return: \underline{16}, 18, 27* . ch: \underline{28*} . ch buffer array : \underline{34}. ch flag array : \underline{34}. ch limit array : \underline{34}. ch line array : \underline{32}. : 38, 40, 52. : 53. change buffer : \underline{34}, 35, 36, 39, 40, 46, 53. change changing : \underline{33}, 40, 46, 48, 52. change file: 2* , \underline{24}, 30, 34, 37, 38, 40, 44, 52, 70* . change file is open: \underline{34}, 35, 48, 53. change files : \underline{24}. change limit : \underline{34}, 35, 36, 39, 40, 46, 53. change line: \underline{32}, 33, 37, 38, 44, 48, 56. changing : \underline{24}, 30, 33, 34, 36, 40, 50, 53, 55. char : 13* , 15, 27* , 28* , 47, 59* , 61* , 64* , 65* , 67* . check : 4* . check change: \underline{40}, 51. chr : 13* , 14, 18, 19, 21* . cli$ absent : 63* . cli$ concat : 63* , 65* . cli$ defaulted : 63* , 65* . cli$ locneg : 63* , 65* . cli$ locpres : 63* , 65* . cli$ negated : 63* , 65* . cli$ present : 63* , 65* . cli$get value: \underline{64*} , 65* . cli$present : \underline{64*} , 65* . continue: \underline{5}, 36, 37. debug: \underline{3*} . decr : \underline{6}, 27* , 65* . def : \underline{67*} . do nothing : \underline{6}, 69* . done: \underline{5}, 27* , 36, 37, 40, 44, \underline{65*} . else: 7* . end: 3* , 7* . end of WEBMERGE : \underline{2*} , 31, 54* . endcases: \underline{7*} . err print : \underline{29}, 33, 37, 38, 40, 41, 43, 52, 53. error : 27* , \underline{29}, 31. error message: \underline{10}, 57, 68* . exit : \underline{5}, 6, 35, 36, 40, 44, \underline{65*} . ext : \underline{67*} . external : 60* , 64* . f: \underline{27*} , \underline{28*} , \underline{60*} , \underline{66*} . fab: \underline{67*} . fab$b bid : 66* . fab$b bln: 66* . fab$b dns : 67* . fab$b fns : 67* . fab$c bid : 66* . fab$c bln: 66* . fab$l dna: 67* . fab$l fna: 67* . fab$l fop: 67* . fab$l nam: 67* . fab$m ofp: 67* . fab$type: 59* , 66* , 67* . fab pointer : \underline{59*} , 60* , 67* . false: 27* , 33, 34, 35, 40, 42, 46, 48, 69* . fatal error : \underline{31}, 48. fatal message: \underline{10}, 40, 54* , 57, 68* . filename: \underline{47}. first text char : \underline{13*} , 19.

§71  WEBMERGE  INDEX 25

flush needed : \underline{40}, 43. form feed : \underline{16}, 27* . found : \underline{5}. get arg : \underline{65*} , 69* , 70* . get line: \underline{50}, 55. gubed: \underline{3*} . harmless message: \underline{10}, 57, 68* . has val : \underline{65*} . history : \underline{10}, 11, 40, 43, 54* , 57, 68* . : 41. i: \underline{17}, \underline{66*} . iaddress : 67* . incr : \underline{6}, 34, 37, 38, 40, 44, 45, 51, 52, 55, 70* . inherit : 4* . init fab: \underline{66*} , 67* . init nam: \underline{66*} , 67* . initialize: \underline{2*} , 54* . inner loop: 27* . input : \underline{2*} . : 27* . input has ended : \underline{32}, 40, 42, 48, 51, 55. input ln: \underline{27*} , 37, 38, 40, 44, 51, 52. integer : 15, 32, 40, 64* , 65* , 66* , 67* . j: \underline{29}. jump out : 2* , 29, \underline{31}, 69* . k: \underline{29}, \underline{35}, \underline{36}, \underline{40}. l: \underline{29}. last active change file: \underline{24}, 33, 43, 45, 52. last text char : \underline{13*} , 17, 19. length: 28* . lib$stop: 68* . limit : 27* , 28* , 30, \underline{32}, 35, 37, 38, 39, 41, 42, 44,

46, 48, 50, 52, 53. line: 30, \underline{32}, 33, 40, 42, 48, 51, 52, 53. line feed : \underline{16}, 27* . line length: 8. lines dont match: \underline{35}, 40, 43, 45. loc: 27* , 30, \underline{32}, 37, 41, 42, 48, 50, 52, 53. loop: \underline{6}. l1 : 64* . l2 : 64* . mark error : \underline{10}, 29. mark fatal : \underline{10}, 31, 69* . mark harmless : \underline{10}, 43. max change files : \underline{8}, 9, 24, 48, 49* . more names : \underline{47}, 48, 49* , 70* . n: \underline{40}, \underline{66*} . nam: \underline{65*} , \underline{67*} . nam$b bid : 66* . nam$b bln: 66* . nam$b esl : 67* . nam$b ess : 67* . nam$c bid : 66* . nam$c bln: 66* . nam$l esa: 67* . nam$l rlf : 67* . nam$type: 59* , 66* , 67* . nam pointer : \underline{59*} . new line: \underline{21*} , 29, 30, 31, 55, 69* . nil: 6. : 69* . none: 4* , 22* . none match: \underline{40}, 45. not found : \underline{5}. number of actual change files : \underline{24}, 43, 45, 48,

53, 56, 70* . odd : 65* , 67* . one matches : \underline{45}. open: 2* . open input : \underline{25}, 42. ord : 14. othercases: \underline{7*} . others : 7* . otherwise: 7* . out buf : 29. out buf size: \underline{8}, 29. out ch line: \underline{32}, 34, 48, 56. out change file: 2* , 24, 25, 34, 54* , 69* . out parse: \underline{67*} . out web file: 2* , \underline{24}, 25, 54* , 55, 69* . out web line: \underline{32}, 48, 55, 56. output : 2* . output change line: \underline{34}, 40, 41, 46, 52, 55. output ln: \underline{28*} , 34, 55. Overflow in arithmetic: 4* . pas$fab: 60* , 67* . prev history : \underline{40}, 43. prime the change buffer : \underline{36}, 43, 48, 52. print : \underline{21*} , 29, 30, 31, 55, 69* . print ln: \underline{21*} , 30, 54* . print nl : \underline{21*} , 27* , 56, 57. range of change files : \underline{9}, 24, 32, 34, 40. read ln: 27* . reset : 25, 69* , 70* . restart : \underline{5}, 50. reswitch: \underline{5}. retbuf : 64* . return: 5, \underline{6}. rewrite: 22* , 25, 69* . scrounge: \underline{67*} . setup error : \underline{69*} , 70* . spotless : \underline{10}, 11, 40, 57. stat: \underline{3*} . status : 27* , \underline{65*} , 69* , 70* . sts$k error : 68* . sts$k normal : 68* . sts$k severe: 68* . sts$k warning : 68* . sts$m inhib msg : 68* . system dependencies: \underline{1*} , 2* , 4* , 7* , 13* , 18, 21* , 22* ,

23* , 25, 27* , 30, 31, 49* , 54* , 57, 59* , 60* , 61* , 63* , 64* , 65* , 66* , 67* , 68* , 69* , 70* .

tab mark : \underline{16}, 30. tats: \underline{3*} . temp: \underline{28*} . temp index : \underline{40}, 45. term in: 2* , \underline{21*} . term out : 2* , \underline{21*} , 22* . terminate change file: \underline{34}, 37, 38, 44, 53. text : 13* . text char : \underline{13*} , 14, 21* . text file: \underline{13*} , 21* , 24, 27* , 28* . this line matches : \underline{43}. tmp: \underline{67*} . true: 6, 27* , 32, 33, 35, 40, 45, 46, 48, 51, 69* , 70* . unsafe: 59* . unsafe file: \underline{59*} , 60* . update terminal : \underline{23*} , 29. u1 : 64* . u2 : 64* . var: \underline{60*} . varying : 27* , 28* . : 40. web file: 2* , \underline{24}, 25, 30, 34, 40, 51, 53, 67* , 69* . web line: \underline{32}, 33, 48, 53, 56. WEBMERGE : \underline{2*} . : 37, 41, 52. write ln: 21* , 28* . xchr : \underline{14}, 15, 17, 18, 19, 28* , 30. xclause: 6. xord : \underline{14}, 17, 19, 27* .

§71  WEBMERGE  NAMES OF THE SECTIONS 27

⟨ Check that all changes have been read 53 ⟩ Used in section 55. ⟨ Compiler directives 4* ⟩ Used in section 2*. ⟨ Constants in the outer block 8, 63* ⟩ Used in section 2*. ⟨ Convert history to program exit status 68* ⟩ Used in section 54*. ⟨ Determine if the web file line matches any of the change files 45 ⟩ Used in section 40. ⟨ Error handling procedures 29, 31 ⟩ Used in section 2*. ⟨ Flush any other change files which also match the current line 43 ⟩ Used in section 40. ⟨ Flush current change file entry 44 ⟩ Used in section 43. ⟨ Get name of VAX/VMS change file from command line and open it 70* ⟩ Used in section 49*. ⟨ Get next change file and open it 49* ⟩ Used in section 48.

⟨ Globals in the outer block 10, 14, 21*, 24, 26, 32, 34, 47, 62* ⟩ Used in section 2*.

⟨ If the current line starts with , report any discrepancies and return 41 ⟩ Used in section 40. ⟨ Initialize the input system 42 ⟩ Used in section 54*. ⟨ Local variables for initialization 17 ⟩ Used in section 2*.

⟨ Move buffer and limit to change buffer and change limit 39 ⟩ Used in sections 36 and 40.

⟨ Open the input change files 48 ⟩ Used in section 42. ⟨ Print error location 30 ⟩ Used in section 29. ⟨ Print statistics about line counts 56 ⟩ Used in section 54*.

⟨ Print the job history 57 ⟩ Used in section 54*.

⟨ Read from change file and maybe turn off changing 52 ⟩ Used in section 50. ⟨ Read from web file and maybe turn on changing 51 ⟩ Used in section 50.

⟨ Read the input 55 ⟩ Used in section 54*. ⟨ Set initial values 11, 15, 18, 19, 22*, 69* ⟩ Used in section 2*.

⟨ Skip over comment lines in the change file; return if end of file 37 ⟩ Used in section 36. ⟨ Skip to the next nonblank line; return if end of file 38 ⟩ Used in section 36.

⟨ Types in the outer block 9, 12, 59*, 61* ⟩ Used in section 2*. ⟨ VAX/VMS procedures 60*, 64*, 65*, 66*, 67* ⟩ Used in section 2*.

⟨ Write to the output change file 46 ⟩ Used in section 40.

The WEBMERGE processor

(Version 1.0) (VAX/VMS Version 1.0.0, April 1986)

The changes required to create this VAX/VMS version of are the sections marked with asterisks (*) below.

Section Page

Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1 The character set . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 5 Input and output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 8 Reporting errors to the user . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 10 Change file handling . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 12 The main program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 19 System-dependent changes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 20 Index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 24