summaryrefslogtreecommitdiff
path: root/macros/plain/contrib/chbar/chbars.doc
blob: 77610d2db3b115e98c82007f15b02af422a4a3cc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
% This is CHBARS.DOC			  as of 09 Oct 89
%---------------------------------------------------------
% (c) 1989 by J.Schrod. copy conditions see below.

%
% Macro package for creating changebars in Plain TeX
% MAKEPROG is needed
%
% documented in LaTeX (for Anne and Chris)

%
%	VERSION HISTORY  (MSCF -- most significant change first)
%
% DATE	   PERSON  REMARK
% 89-10-09 -js	   converted to LaTeX (progltx)
% 89-09-25 -js	   repaired \mark processing in horizontal mode
% 89-08    -js	   first version (for EuroTeX89 in Karlsruhe)
%

% author's current address:
%
%	Detig$\,\cdot\,$Schrod \TeX{}sys
%	Joachim Schrod
%	Kranichweg 1
%
%	D-6074 R\"odermark-Urberach
%	FR Germany
%
%	Tel. (+6074) 1617
%	Bitnet: XITIJSCH@DDATHD21



% should be progtex...
\documentstyle[progltx,a4-9]{article}


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%
% local macros
%

\let\mc=\small		% for names like GNU

\def\PS{{\sc PostScript}}
\def\DVI{{\tt DVI}}
\def\GNU{{\mc GNU}}

\chardef\bs=`\\

%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%




\begin{document}


\title{Changebars without {\tt \bs{}special}'s}
\author{\sc Joachim Schrod}

\maketitle



\begin{abstract}
It is common practice to use vertical bars in the margins of a
document to mark pieces of text which have changed since the last
version(s) of this document. Such vertical bars are usually
called {\em changebars}. It has often been said that it is
impossible to produce changebars with \TeX{} without the usage of
|\special| commands (driver directives), which extend the
primitives of \TeX{}. This paper presents a \TeX{} macro file
which implements changebars without such a usage. The macro file
is written for the usage with Plain~\TeX{} but the implementation 
strategy can be used with \LaTeX{}, too.
\end{abstract}



\chap Introduction.

Changebars are used to mark modified parts in existing documents.  For
the usage in \TeX{} documents, there exist only solutions that use
driver/printer features by the way of inserting |\special| commands in
the \TeX{} source, e.g.\ for \PS{} drivers.  This results in documents
that are no longer as freely interchangeable as the \DVI{} concept would
allow---device dependency is problematic especially for this application
that is useful for multi-authoring or standards development.

This macro package offers a pure \TeX{} solution.  Nevertheless, it has
its restrictions, too.	The page break will no longer be optimal,
because no strechability or shrinkability on a page on top of the last
mark of a change is supported.	But this seems to be acceptable,
especially as the change bar feature often will be used for proof
reading and not in the final document.	This restriction is the reason
why no change marks can be used on titlepages or for similar
constructions.	Changes in floating insertions (footnotes, figures) are
not discovered.

The demonstrated solution is written in Plain \TeX{}, because it was
easier and could be presented better at the Euro\TeX89 conference in
Karlsruhe.  An adaption to \LaTeX{} is possible, but would require
modifications to the \LaTeX{} kernel, i.e.\ the output routine and the
layout parameters of the standard styles.


\sect This program is free software; you can redistribute it and/or
modify it under the terms of the \GNU{} General Public License as
published by the Free Software Foundation; either version~1, or (at your
option) any later version.

This program is distributed in the hope that it will be useful, but
{\bf without any warranty\/}; without even the implied warranty of
{\bf merchantability\/} or {\bf fitness for a particular purpose}.  See
the \GNU{} General Public License for more details.

%You should have received a copy of the \GNU{} General Public License
%along with this program; if not, write to the Free Software Foundation,
If you have not received a copy of the \GNU{} General Public License
along with this program, write to the Free Software Foundation,
Inc., 675~Mass Ave, Cambridge, MA~02139, USA.


\sect A changed area is described by two marks, |\beginchange| and
|\endchange|.  The method for writing a change bar consists of three
parts:	First, it will be indicated to the output routine that a change
has occured.  Next, the position of the changed area will be found out
and fixed.  A list of all changed areas is built, that can at last be
marked by the output routine using vertical rules at the right margin.

But before we start we declare some shorthands for category codes.  By
declaring the at sign~(`|@|') as well as the underscore~`(|_|)' as
letters we can use them in our macros.	(I agree with D.~Knuth that
|\identifier_several_words_long| is more readable than
|\IdentifierSeveralWordsLong| and in every case better than |\p@@@s|.)
With the at sign we can use the ``private'' Plain macros and with the
underscore we can make our own macros more readable.  But as we have to
restore these category codes at the end of this macro file we store
their former values in the control sequences |\atcode| and |\uscode|.
This method is better than to use a group because not all macros have to
be defined global this way.

\beginprog
\chardef\letter=11

\chardef\atcode=\catcode`\@
\chardef\uscode=\catcode`\_

\catcode`\@=\letter
\catcode`\_=\letter
\endprog




\chap Triggering the Output Routine.

\begin{sloppypar}
First, the output routine must know that changes occured.  This is done
by the macros |\beginchange| and |\endchange| that append a reserved
penalty value |\break_penalty| to the current list that is below
$-10\,000$.  The penalty will be in the range $|\change_penalty_group|
\cdot 100 - 99 \ldots |\change_penalty_group| \cdot 100$.
For the output it must be differentiated if the mark indicates
begin or end of the changed area.  This is done with the values
|\change_penalty_begin| and |\change_penalty_end| that are used as the
second-to-last digit of the change penalty.
\end{sloppypar}

It must also be differentiated between the beginning of a mark being in
horizontal or vertical mode.  In horizontal mode, the change bar must
not begin at the baseline of the actual text position, but on top of the
actual line.  This is marked in the last digit, an odd digit will be
used in horizontal mode.

Note that the values mentioned above are used as digits here that can be
concatenated.  If they are not followed by an other digit they should be
terminated by |\space| to stop the look-ahead for digits.

These interactions with the output routine adds a lot of dead cycles; we
must therefore increase |\maxdeadcycles| to prevent \TeX{} from grumbling.

\beginprog
\def\change_penalty_group{-101}
\def\change_penalty_begin{0}
\def\change_penalty_end{1}

\maxdeadcycles=100
\endprog


\sect The marks set the change penalty value (including the indication
of begin or end of mark).  The rest (|\trigger_output|) is the same action
for both. An end change mark in horizontal mode can be preceeded by
glue that could cause a line break, thus including the following line to
the change area as well.  To avoid this unwanted behaviour, the space is
saved in |\save_space|, discarded in front of the mark and restored
afterwards.

\beginprog
\newcount\break_penalty
\newskip\save_space

\def\beginchange{%
   \break_penalty=\change_penalty_group\change_penalty_begin0
   \trigger_output
   }
\def\endchange{%
   \ifhmode  \save_space=\lastskip \unskip  \fi
   \break_penalty=\change_penalty_group\change_penalty_end0
   \trigger_output
   \ifhmode  \hskip\save_space	\fi
   }
\endprog


\sect In |\trigger_output| the real output trigger is done.  We must consider
that an |\output|-invocation by our |\break_penalty| can be discarded at
the beginning of a page.  So we trigger the output routine twice, first
with a special penalty value that is 2~less than the correct value
(including the code for horizontal or vertical mode).  After the first
page break it is asserted that the current list is empty.  The output
routine has to save the former page contents if necessary.

Now we set the penalty to the correct value.  The second page break does
the real work, restores the page contents and handles the split
insertions (footnotes, figures,~\dots)

In horizontal mode |\spacefactor| must not be destroyed.

\beginprog
\newcount\save_space_factor

\def\trigger_output{%
   \ifinner  \errmessage{Change cannot be marked inside a box}%
   \else
      \ifvmode	\let\do_in_vmode=\relax
	 \advance \break_penalty by -2
      \else  \save_space_factor=\spacefactor
	 \let\do_in_vmode=\vadjust
	 \advance \break_penalty by -3
      \fi
      \do_in_vmode{%
	 \penalty\break_penalty
	 \null
	 \advance \break_penalty by 2  \penalty\break_penalty
	 }%
      \ifhmode	\spacefactor\save_space_factor	\fi
   \fi
   }
\endprog


\sect The usage of the output routine as an information passing system
has always it's difficulties.  One of the real hard parts is the
handling of the page marks, special token lists that are handled by
\TeX{}. These token lists provide informations about text on a page,
they are usually used to create head lines etc.  The user may access
these token lists with three control sequences:  |\botmark| is the
last page mark given, |\topmark| is the |\botmark| of the previous
page, and |\firstmark| is the first page mark on the actual page or
|\topmark| if none was given.  Here ``page'' is used in the \TeX{}
sense, i.e.\ as the material which has been collected between two
|\output| invocations.	Of course, the page marks must not be
destroyed---and that means they must be reinserted after each
special use of the output routine.

But we have luck: A ``special use'' consists of two |\output|
invocations, so we can insert |\topmark| again as a page mark after
the first invocation where it will be the only page mark on that
\TeX{} page.  The second invocation will automatically transform this
page mark into the ``last page mark on the previous page,'' i.e.\ in
|\topmark|---that's what we need!  Furthermore |\firstmark| and
|\botmark| are saved in control sequences during the first invocation,
they will be inserted again, too.

There's one situation where this approach doesn't work: In front of
the first page mark |\topmark|, |\firstmark|, and |\botmark| expand
to an empty token list. If we save them then and insert their old
values we have inserted empty page marks. If other page marks follow
on the same ``real'' page |\firstmark| will be empty instead of
expanding to the token list of the first page mark.  To prevent this
we must not save and restore page marks before the first |\mark| has
been added to the main vertical list.

Well, that can be controlled with a switch---but this switch must be
set very carefully.  If it is set immediately by the first |\mark|
this may be in horizontal mode and special output invocations can
occur above this page mark (i.e., there may be a |\beginchange| in the
same paragraph in front of the |\mark|).  Therefore the setting of the
switch must be delayed until the vertical position of the |\mark|
(precisely:  the position of the |\mark| in the current list) is
reached.  In horizontal mode this can be done with a |\vadjust| and
the output routine! Voil\`a, this is another command group for the
output routine with only one command.

\beginprog
\newif\if@save_mark@  \@save_mark@false

\def\mark_penalty_group{-102}
\endprog


\sect We will redefine |\mark| so that the first page mark either sets
the switch to true (in vertical mode all possible special page breaks
are already handled) or forces the |\output| routine to do this at an
appropriate place. In the last case we can use |\trigger_output|
again. Afterwards we restore the original meaning of |\mark| again to
reduce the processing overhead (and the dead cycles).

This change of |\mark| has the consequence that the first |\mark| in a
document cannot be used anymore in horizontal mode inside a vertical
box that shall be split afterwards.  But this is only sensible if this
mark shall be used as |\splitfirstmark| because it will almost never
migrate to the outer list---really a rare case!

\beginprog
\let\@@mark=\mark
\def\mark{%
   \ifvmode
      \ifinner \else  \global\@save_mark@true \fi % split marks!
   \else  \break_penalty=\mark_penalty_group00   % this will corrupt \vsplit
      \trigger_output
   \fi
   \global\let\mark=\@@mark
   \@@mark
   }
\endprog


\sect 
\begin{sloppypar}
If the output routine is triggered with the mark penalty value
it will call |\start_saving_page_marks|.
\end{sloppypar}

\beginprog
\def\start_saving_page_marks{%	     % this may be executed twice
   \unvbox255
   \global\@save_mark@true
   }
\endprog


\sect To finish the treatment of page marks we can formulate the two
macros which are used at the first resp.\ second invocation of a
``special output,'' the principles have already been explained.

\beginprog
\def\backup_page_marks{%
   \if@save_mark@
      \mark{\topmark}%
      \xdef\save_firstmark{\firstmark}%
      \xdef\save_botmark{\botmark}%
   \fi
   }

\def\restore_page_marks{%
   \if@save_mark@
      \mark{\save_firstmark}\mark{\save_botmark}%
   \fi
   }
\endprog





\chap Positioning the Change Marks.

Now we must handle the positions of the bars.  |\change_pos| will hold
the position of the actual mark, i.e.\ the distance between top of page
and actual mark.  |\top_change_pos| will hold the beginning of a changed
area; a value of |\maxdimen| indicates that no change is in effect.  If
a changed area is completed, it is appended to the list |\bar_list| as
an element |\bar(\top_change_pos,\change_pos)|.  This list contains all
changed areas within the current page so that bars can be written later
on.  A single bar will be produced by |\write_bar|.

\noindent The definition of |\bar| to |\relax| allows the concatenation
of new elements to |\bar_list| with |\xdef|.

\beginprog
\newdimen\change_pos
\newdimen\top_change_pos  \top_change_pos=\maxdimen

\let\bar_list=\empty

\let\bar=\relax
\def\write_bar(#1,#2){%
   \setbox0=\hbox{\vrule height -#1  depth #2}%
   \dp0=0pt  \ht0=0pt
   \box0
   }
\endprog


\sect If the output routine was activated by a |\outputpenalty| value
within the range of our reserved penalties, the change handling will
occur, otherwise standard plain output can be done.

\beginprog
\newcount\penalty_group

\output={%
   \boxmaxdepth=\maxdepth
   \penalty_group=\outputpenalty  \divide \penalty_group by 100
   \ifnum \penalty_group=\change_penalty_group\space  \change_handling
   \else
      \ifnum \penalty_group=\mark_penalty_group\space
	 \start_saving_page_marks
      \else \plainoutput
      \fi
   \fi
   }
\endprog


\sect As explained before, the change handling must differentiate
between the kind of the change command (beginning is indicated by
$|\change_cmd|=0$, end by~1) and between the mode (horizontal indicated
by an odd |\change_mode| value, vertical by an even).  A change mode
higher than one indicates that we are doing the first page break that
has to backup the page as far as it exists already and results in an
empty current list of page elements.

\beginprog
\newcount\change_cmd
\newcount\change_mode

\def\change_handling{%
   \change_cmd=-\outputpenalty			   % ==> absolute value
   \advance \change_cmd by \change_penalty_group00 % subtraction
   \change_mode=\change_cmd
   \divide \change_cmd by 10			   % second-to-last digit
   \advance \change_mode by -\number\change_cmd0   % last digit
   \ifnum \change_mode>1  \backup_page
   \else
      \ifcase \change_cmd  \begin_change
      \or \end_change
      \else \errmessage{Invalid changepenalty}%
      \fi
   \fi
   }
\endprog


\sect Processing a mark during the second trigger of the output routine
means restoring the page and storing the positions.  At the beginning,
the begin of the change is saved, at the end, we know the bar already
and put it into the bar list.  Then the positioning values are
reinitialized.

As within every output invocation, the box 255 must be unboxed.  As we
are here in the second invocation of the output routine the |\box255|
consists only of the empty |\vbox| we have inserted in
|\trigger_output|.  We can therefore throw it away.

\beginprog
\def\begin_change{%
   \restore_page
   \setbox0=\box255
   \ifdim \top_change_pos=\maxdimen
      \global\top_change_pos=\change_pos  \global\change_pos=0pt
   \else \errmessage{Nested change bars are not supported}%
   \fi
   }

\def\end_change{%
   \restore_page
   \setbox0=\box255
   \ifdim \top_change_pos=\maxdimen
      \errmessage{No change is in effect}%
   \else
      \xdef\bar_list{\bar_list \bar(\the\top_change_pos,\the\change_pos)}%
      \global\top_change_pos=\maxdimen	\global\change_pos=0pt
   \fi
   }
\endprog




\chap Handling the Page Contents.

We handle the part of the page that was collected up to now by putting
it into a box.	This fixes the position of the change mark so that
|\change_pos| can be set and stored later on in |\top_change_pos| or as
the lower end of a bar in |\bar_list|.

To save the page contents in the first output invocation, we have to
save the page in |\save_page|.	Before that, we store the size of the
box (which equals |\pagegoal|!)\ in |\page_goal|.  If the unboxing
caused an increase of height (i.e.\ if $|\pagetotal|>|\pagegoal|$),
we eject the page up to the change mark.  Now we have to compute the
current position of our mark in |\change_pos|.	It is fixed by the size
of the |\save_page|, but in case of a begin mark in horizontal mode we
must decrease it from the baseline position to the top of the last line.
Finally, we must save the values for the allowed insertions and change
them to the maximal value so that a rest that is split from an insertion
will be appended to the insertion box at the second invocation in every
case.

The |\vsize| is initialized to |\maxdimen|.  This allows to control
whether this first output invocation ocurred or if it was discarded.
For the same reason |\change_pos| is initialized to~0pt.

\beginprog
\newbox\save_page

\newdimen\page_goal
\newdimen\save_vsize		\save_vsize=\maxdimen
\newdimen\save_dimen_topins
\newdimen\save_dimen_footins

\def\backup_page{%
   \global\page_goal=\ht255
   \global\setbox\save_page=\vbox{\unvbox255}%
   \ifdim \ht\save_page>\page_goal  \eject_page_so_far	\fi
   \change_pos=\ht\save_page  \global\advance \change_pos by \dp\save_page
   \ifnum \change_cmd=\change_penalty_begin\space
      \ifodd \change_mode  \higher_change_pos  \fi
   \fi
   \global\save_vsize=\vsize  \global\vsize=\maxdimen
   \global\save_dimen_topins=\dimen\topins  \global\dimen\topins=\maxdimen
   \global\save_dimen_footins=\dimen\footins
	 \global\dimen\footins=\maxdimen
   \backup_page_marks
   }

\change_pos=0pt
\endprog


\sect To eject a page as far as it is we restore it from the
|\save_page| back to box~255.  In horizontal mode and at a begin mark
the last line contains the mark and must not be output.  So we remove it
and the preceding glue from the stored rest, just leaving a single hbox
to be on top of the actual page (in |\save_page|) now.	Then normal
output can be done with box~255.

\beginprog
\def\eject_page_so_far{%
   \begingroup
      \vbadness=20000 % don't complain about underfull vboxes
      \global\setbox255=\vbox to \page_goal{%
	 \unvbox\save_page
	 \ifnum \change_cmd=\change_penalty_begin\space
	    \ifodd \change_mode
	       \global\setbox\save_page=\lastbox
	       \unskip
	    \fi
	 \fi
	 }%
   \endgroup
   \plainoutput
   }
\endprog


\sect 
\begin{sloppypar}
In horizontal mode and at a begin mark, we need the position of
the mark (|\change_pos|) on the upper boundary of the last line in
|\save_page|.  If there is just one line left from a recent eject, the
height is given by the topskip decreased by the height of this hbox.  If
the height of the box is larger than |\topskip| the skip will not be
inserted and the change position results to~0pt.  Otherwise,
|\save_page| is a vbox whose last hbox we delete temporarily using
box~0.	Height and depth of the rest are the actual position on the page.
\end{sloppypar}

The double of the page we have constructed this way will immediately be
fed back to the garbage collector because it could have become
reasonably large.

\beginprog
\def\higher_change_pos{%
   \ifhbox \save_page % rest of page from \eject_page_so_far
      \change_pos=\topskip  \global\advance \change_pos by -\ht\save_page
      \ifdim \change_pos<0pt  \global\change_pos=0pt  \fi
   \else
      \setbox0=\vbox{%
	 \unvcopy\save_page
	 \setbox0=\lastbox % delete last line
	 }%
      \change_pos=\ht0	\global\advance \change_pos by \dp0
      \setbox0=\box\voidb@x
   \fi
   }
\endprog


\sect To restore a page during the second output invocation, we first
restore the saved values, but only if they were really changed (this can
be discovered by the value of |\save_vsize|).  Now the |\save_page| is
appended to the current list as a box, which stops later usage of its
stretch- and shrinkability!  Then the collected insertions can be
inserted again. The page marks have to be inserted, too.

\beginprog
\def\restore_page{%
   \ifdim \save_vsize=\maxdimen
   \else  \global\vsize=\save_vsize
      \global\dimen\topins=\save_dimen_topins
      \global\dimen\footins=\save_dimen_footins
      \global\save_vsize=\maxdimen
      \restore_page_marks
   \fi
   \box\save_page % discards stretch- and shrinkability!
   \ifvoid \topins
   \else  \insert\topins{\floatingpenalty=0 \unvbox\topins}%
   \fi
   \ifvoid \footins
   \else  \insert\footins{\floatingpenalty=20000 \unvbox\footins}%
   \fi
   }
\endprog


\sect {\em Please note, that there is still a problem with this concept 
of handling the output trigger:}

\bigskip

If the first output trigger is discarded because a page break has occured 
just in front, footnote parts may be juggled around. I.e., if a 
footnote is split in three parts, the first part was just been shipped 
out, the second part is inserted back into the recent contributions by 
the output routine but {\em behind\/} the third part which is saved in 
the ``special place'' (according to the \TeX{}book, p.~125). A solution to
this problem might be to insert a |\do_change| again within the second
output trigger and finishing the treatmend afterwards. Afterwards a 
full triggering process (two output invocations) is executed again and 
alle insertion parts will be accessible in the insertion box.

By the way, the almost same problem appears in \LaTeX{}, too. Almost: in
\LaTeX{} this can happen every time because at the first output invocation
the |\dimen|-values of the footnote insertion is not increased. I leave 
the problem open to the reader\,\dots





\chap Writing the Stuff.

The positions of the bars which mark the changed areas are relative to
the top of the text, i.e.\ the height of the top insertion is not
included.  Therefore it is best to write them just after the top
insertions before the page text---but to do this we have to change the
Plain macro |\pagecontents|.

Below is the new definition, I have just rearranged it a little bit so
that it is more legible.  The line I have inserted is marked with
`|%%%%|'.  |\insert_current_bar| inserts a last element in |\bar_list|
if a changed area is not yet finished, afterwards all bars can be
written.

\beginprog
\def\pagecontents{%
   \ifvoid \topins  \else \unvbox\topins \fi
   \insert_current_bar	\write_all_bars 		 %%%%
   \dimen@=\dp\@cclv \unvbox\@cclv % open up \box255
   \ifvoid \footins
   \else % footnote info is present
      \vskip\skip\footins
      \footnoterule
      \unvbox\footins
   \fi
   \ifr@ggedbottom \kern-\dimen@ \vfil \fi
   }
\endprog


\sect If $|\top_change_pos|=|\maxdimen|$ no change is active.  Otherwise
the current change reaches from the begin mark (|\top_change_pos|) to
the end of the page, i.e.\ we insert a virtual end mark.  Because the
change continues on the next page we insert a virtual begin mark on the
top of the page, too.

\beginprog
\def\insert_current_bar{%
   \ifdim \top_change_pos=\maxdimen
   \else
      \change_pos=\ht255  \advance\change_pos by \dp255
      \xdef\bar_list{\bar_list \bar(\the\top_change_pos,\the\change_pos)}%
      \global\top_change_pos=0pt
   \fi
   }
\endprog


\sect Now we can write all bars---if they exist anyway.  It's rather
easy, we just have to define |\bar| to |\write_bar| and execute
|\bar_list|.  The resulting output must not use vertical place.  We must
not forget to delete the list, or we will get the same bars on the next
page again.

\noindent |\BarDistance| is the amount of place between the text margin
and the change bars.

\beginprog
\newbox\@bars
\newdimen\BarDistance  \BarDistance=2cc

\def\write_all_bars{%
   \ifx \bar_list\empty
   \else			       % changes exist
      \setbox\@bars=\hbox to \hsize{%
	 \hskip\hsize  \hskip\BarDistance
	 \vbox to 0pt{\offinterlineskip
	    \let\bar=\write_bar  \bar_list
	    }%
	 \hss
	 }%
      \ht\@bars=0pt \dp\@bars=0pt \box\@bars
      \global\let\bar_list=\empty
   \fi
   }
\endprog


\sect We finish the macro file so that garbage (e.g.\ of exchanges
between systems) can come afterwards.

\beginprog
\catcode`\@=\atcode
\catcode`\_=\uscode

\endinput
\endprog



\end{document}