summaryrefslogtreecommitdiff
path: root/web/noweb/src/shell/roff.nw
blob: 24ddee69f3998b7eb23d5ae3726defe8f65592cc (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
.nr N 4
.nr O 1i
.nr P 2
.nr W 6.5i
.S 18 18
'ce
Converting noweb markup to troff markup
.sp 0.5i
.S 12 15
.PH "'Converting noweb markup to troff markup''%'"
.P
Toroff is a noweb backend for formatting text with troff
markup.  Toroff was written by Phil Bewig ([[pbewig@netcom.com]])
during April, 1996, and contributed to Norman Ramsey's noweb
literate programming system.  Liam Quin ([[lee@sq.com]]) provided technical
assistance with troff.
Norman Ramsey made the code easier to port and made it conform with
his idea of the noweb philosophy.
Arnold Robbins ([[arnold@skeeve.com]]) modified it for use with
GNU troff (hopefully improving portability even more),
improved the flexibility of the noroff script,
and added extra explanation about the gory troff details.
.P
The high-level view of toroff is that it comes in two parts.
The first part is an ordinary back end, which produces 
troff augmented with special comments, using the [[.tm]] macro.
The second part, [[noroff]], runs troff, with all of its
pre-processors and post-processors.
[[noroff]] also processes the
comments in to a [[tags]] file.
Moreover, [[noroff]] gathers from the tags file the
cross-referencing information gathered on the previous formatting run.
The trick that makes the whole thing work is that the
troff markup added by toroff causes cross-referencing
information to be written to standard error during the formatting run;
the [[noroff]] script cleverly gathers this material into 
the [[tags]] file, 
which can be used as input to the next
formatting run.
As with LaTeX,  the cross-referencing information is always one
formatter run behind, and to get a consistent document you must keep
running [[noroff]] until you reach a fixed point.
([[noroff]] ought to provide some help for this, to show when the tags
file has changed, for example.)
.HU "The shell wrapper"
Toroff is implemented as an awk program in a shell wrapper, and
uses a macro package to control the final formatting.  In the following
discussion, a sample macro package, suitable for use with either [[-mm]]
or [[-ms]], is presented alongside the awk code which calls the
macros.  Users may wish to modify the sample macros to suit local
preferences.  Many do-nothing macros are provided as user exits so
knowledgeable users can modify document formatting without changing
the awk program.
<<toroff>>=
#!/bin/sh
LIB=|LIBDIR|
AWK=nawk
<<process arguments>>
<<invoke awk program>>
@ .P
Processing arguments is simple.
The only effect of the [[-delay]] flag is to control placement of
the ``begin document'' document macro;
the noroff script supplies the command to source the noroff macros
as the very first line. This avoids groff warnings about undefined
strings in the generated markup for the indexing and cross-references.
<<process arguments>>=
delay=0 noindex=0
for i do
    case $i in
        -delay)    delay=1   ;;
        -noindex)  noindex=1 ;;
        *) echo "This can't happen -- $i passed to toroff" 1>&2;
           exit 1;;
    esac
done
@ %def delay noindex
.P
Invoking the awk program is hard, because the program uses both
single quotes and double quotes, so neither can be used to protect the
other, and quoting each quote is ugly.  The pragmatic solution is to
copy the awk program into a temporary file, using a shell here-document.
<<invoke awk program>>=
awkfile=$($LIB/nwmktemp) || { echo "$0: Cannot create temporary file" >&2; exit 1;  }
trap 'rm -f $awkfile' 0 1 2 10 14 15
cat > $awkfile << 'EOF'
<<awk program>>
EOF
$AWK -f $awkfile -v delay=$delay noindex=$noindex
@ %def awkfile tagsfile
.HU "The main program"
The overall structure of the awk program is simple.  The [[tags]]
file is processed in the [[BEGIN]] action, and the noweb source is
processed by awk's pattern-action loop.
<<awk program>>=
<<read and process noweb source>>
<<functions>>
@ .HU "Beginning and ending the document"
The [[-delay]] option allows the initial document chunk to be processed
before invoking the ``begin document'' macro,
so that necessary initialization may
be performed.  The only commands which are useful in such an initial chunk
are bare troff commands.  The delay is handled by special patterns for
the initial document chunk.  Because several noweb files can be
processed in the same formatting run, there can be several document
chunks numbered zero.  The later ones are not treated specially, by the
simple expedient of turning off [[delay]] after the first one.  This
code also handles the [[trailer]] at the end of the document.
<<read and process noweb source>>=
/^@begin docs 0$/ { if (delay) next }
/^@end docs 0$/ { if (delay) { <<start document>>; delay = 0; next } }
/^@header m/ { if (!delay) { <<start document>> } }
/^@trailer/ { print ".ENDOFDOCUMENT" }
<<tmac.w>>=
.de ENDOFDOCUMENT
..
<<start document>>=
printf ".BEGINNINGOFDOCUMENT\n"
<<tmac.w>>=
.de BEGINNINGOFDOCUMENT
..
@ .HU "Labels, references, and section numbers"
Here is the code to number code chunks in the page-number-and-letter
style used by noweb.  The current page number is always stored in
troff's [[%]] number register, and we will arrange an
auto-incrementing number register [[SECTIONLETTER]] which is
reset at each top-of-page and is formatted as lower-case alphabetic
characters using troff's [[.af]] request.  The top-of-page trap is
sprung one unit below the top of the page in order not to interfere with
the top-of-page macros of [[-mm]] or [[-ms]].
<<read and process noweb source>>=
/^@xref label/ { lastxreflabel = $3 }
/^@xref ref/ { lastxrefref = tag(substr($0, 11)) }
@ %def lastxreflabel lastxrefref
<<tmac.w>>=
.de RESETLETTER                     \" reset letter at top of page
.nr SECTIONLETTER 0 1               \" count code sections on same page
.af SECTIONLETTER a                 \" ... formatted as lower-case alpha
..
.wh 1u RESETLETTER                  \" trap just below top of page
@ .P
Note that the link information on the previous chunk shows that this
chunk uses the [[code]] variable, when in fact [[code]] only appears
as text within a comment.  This error is an artifact of the language
independence of noweb.
@ .HU "Beginning and ending chunks"
Except for the initial document chunk, which was processed above, all
beginnings and endings of documentation and code chunks are processed
here.  Variable [[code]] is [[0]] in text chunks, [[1]] in code
chunks and [[2]] in quoted code.  The argument to
the [[.ENDCODECHUNK]] macro is the label which was in effect when the
code chunk was started, which is stored in the variable [[lastdefnlabel]].
<<read and process noweb source>>=
/^@begin docs/ { printf ".BEGINDOCCHUNK\n" }
/^@end docs/ { printf ".ENDDOCCHUNK\n" }
/^@begin code/ { code = 1; printf ".BEGINCODECHUNK\n" }
/^@end code/ { code = 0; printf ".ENDCODECHUNK %s\n", lastdefnlabel }
@ %def code
.P
The macros related to document chunks are simple:  [[.BEGINDOCCHUNK]] does
nothing, and [[.ENDDOCCHUNK]] merely flushes any unprinted output.
<<tmac.w>>=
.de BEGINDOCCHUNK
..
.de ENDDOCCHUNK
.br
..
@ .P
A code chunk uses a font, type size, leading, fill and adjust modes,
indent and tab stops which are distinct from those in the documentation.
These environment variables must be saved on entering a code chunk,
appropriate values for a code chunk must be established, and the
original values restored at the end of a code chunk.
<<tmac.w>>=
.de BEGINCODECHUNK
.sp 0.4v
<<save environment>>
<<set local environment>>
.di CODECHUNK
..
.de ENDCODECHUNK
<<read back code chunk from diversion>>
<<restore environment>>
..
@ .P
The code below replaces the built-in [[.ta]] tab-setting command with a
custom tab-setting command that allows tab stops to be reset and later
recalled.
(A.R.: I don't see a use for this; furthermore this saves
the new settings in [[ta_saved]], it doesn't save the old ones.)
<<tmac.w>>=
.rn ta real_ta
.de ta
.ds ta_saved \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
.real_ta \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
..
@ .P
The font, type size and leading require two saved values, so
that both the current value and the previous value can be restored.
For those not intimately familiar with troff, [[.f]] is the current
font number, [[.s]] is the current point size, [[.v]] is the current
vertical spacing, [[.u]] is 1 or 0 for fill/no-fill, [[.j]] is the
current adjust mode, and [[.i]] is the current indent mode.
<<save environment>>=
.nr OLDft \\n(.f
.ft P
.nr PREVft \\n(.f
.nr OLDps \\n(.s
.ps
.nr PREVps \\n(.s
.nr OLDvs \\n(.v
.vs
.nr PREVvs \\n(.v
.nr OLDfi \\n(.u
.nr OLDad \\n(.j
.nr OLDin \\n(.in
@ .P
Code is printed in a constant-width font at 80% the size of document text.
Code is collected in fill mode with explicit breaks after each line;
although it might seem more natural to collect lines of code in no-fill
mode, that is not possible here because uses of code chunks must be able
to appear on the same line as actual code.
<<set local environment>>=
.ft CW
.ps \\n(.s*4/5
.vs \\n(.vu*4u/5u
.in +0.5i
.real_ta 0.5i 1i 1.5i 2i 2.5i 3i 3.5i 4i
.fi
@ .P
In order to prevent page breaks within code chunks, each code chunk
is read into a diversion and a page break is issued if the code chunk
is too big to fit on the current page.  After the diversion is read back
in, when it is known what page the code is printed on, the location of
the code chunk is written using a [[.tm]] command.
(The [[dn]] number register is the height of the most recent diversion,
and [[.t]] is the distance to the next trap.)
<<read back code chunk from diversion>>=
.br                        \" flush last line of code
.di                        \" end diversion
.if \\n(dn>\\n(.t .bp      \" force form feed if too big
.nf                        \" no fill mode -- already formatted
.in -0.5i                  \" don't re-indent when re-reading text
.CODECHUNK                 \" output body of diversion
.tm ###TAG### \\$1 \\n[%]\\n+[SECTIONLETTER] \" write tag info
.rm CODECHUNK              \" reset diversion for next code chunk
@ .P
Finally, here is the code to restore the environment that existed before
the beginning of the code chunk.
<<restore environment>>=
.ft \\n[PREVft]
.ft \\n[OLDft]
.ps \\n[PREVps]
.ps \\n[OLDps]
.vs \\n[PREVvs]u
.vs \\n[OLDvs]u
.if \\n[OLDfi] .fi
.ad \\n[OLDad]
.in \\n[OLDin]u
.real_ta
@ .HU "Text, code, and quoted code"
In text chunks, text is copied from input to output basically unchanged.
However, when quoted code is included in a line of text, the quoted code
is processed by separate macros and it is possible that the continuation
of the line of text after the quoted code begins with a character which
is special to troff at the beginning of a line:  a space, period, or
single quote.  To prevent problems in this case, variable [[text]] is
set to [[0]] at the beginning of each line and incremented whenever text
is written, and each text after the first is guarded by a non-printing
null character.
<<read and process noweb source>>=
/^@text/ && code == 0 { s = substr($0, 7)
                        if (text++) printf "\\&"
                        printf "%s", substr($0, 7) }
/^@nl/ && code != 1 { text = 0; printf "\n" }
@ %def text
.P
In code chunks, backslashes are replaced with troff's [[\e]] escape
sequence and a non-printing null character is added at the beginning of
each text to guard leading periods.  If your macro package issues a [[.ec]]
command, this code will have to change; fortunately, both [[-mm]] and
[[-ms]] are nicely-behaved with respect to escape characters.
<<read and process noweb source>>=
/^@text/ && code != 0 { s = substr($0, 7)
                        gsub(/\\/, "\\e", s)
                        printf "\\&%s\\c\n", s }
@ .P
Dealing with newlines in code chunks is surprisingly hard.  We would like
to print lines of code in [[nofill]] mode, but since noweb's markup
and filter programs may split lines of code, we are forced to use
[[fill]] mode and deal with newlines ourselves.  Further, if a line is
too long to print on a single line, it must be split, and the continuation
line right-justified instead of left-justified.  All of this processing is
accomplished by calling a macro at each newline; the macro plants a
page-position trap at the next line so that any continuation line will be
right-justified.
(The [[.dt]] command defines a trap for use within the current
diversion, whereas the more common [[.wh]] is for a particular
page position.)
<<read and process noweb source>>=
/^@nl/ && code == 1 { printf ".NEWLINE\n" }
<<tmac.w>>=
.de LINETRAP
.dt \\n[TRAPplace]u    \"cancel current trap
'ad r                  \" right-adjust continuation lines
..
.de NEWLINE
.dt \\n[TRAPplace]u    \" cancel current trap
\&                     \" end continued word
.br                    \" flush output
<<plant newline trap>>
..
@ .P
The newline trap is planted after each definition line, for the first
line of code in the code chunk, and again after each newline in the
code chunk.
(The [[.d]] register indicates the current vertical position
within the diversion.)
<<plant newline trap>>=
.nr TRAPplace \\n(.du+1u       \" location of next trap
.dt \\n[TRAPplace]u LINETRAP   \" plant trap at next line
.ad l                          \" left-adjust first line
@ .P
Quoted code in a documentation chunk is printed within its own macros,
which must not cause a break.
<<read and process noweb source>>=
/^@quote/ { code = 2; printf "\\c\n.BEGINQUOTEDCODE\n" }
/^@endquote/ { code = 0; text++; printf ".ENDQUOTEDCODE\n" }
<<tmac.w>>=
.de BEGINQUOTEDCODE
.nr SAVEft \\n(.f
.ft P
.nr SAVEftP \\n(.f
.ft CW
..
.de ENDQUOTEDCODE
.ft \\n[SAVEftP]
.ft \\n[SAVEft]
..
@ .HU "Definitions and uses of code chunks"
Definitions and uses of code chunks are handled below.  Variable
[[defn[name]]] is set to a plus sign after a definition is printed, so
that continuations of the definition are properly identified.  Variable
[[lastxrefref]] is the tag associated with the most-recently-seen
cross-reference label, and refers to the section number of the original
definition of the code chunk.
<<read and process noweb source>>=
/^@defn/ { name = convquote(substr($0, 7))
           lastdefnlabel = lastxreflabel
	   if (! (name in defn))
	   	defn[name] = "\\(=="
           printf ".DEFINITION %s \"%s\" %s %s\n",
                  tag(lastdefnlabel), name, lastxrefref, defn[name]
           defn[name] = "\\(pl\\(==" }
/^@use/ { name = convquote(substr($0, 6))
          printf "\\c\n"
          printf ".USE \"%s\" %s\n", name, lastxrefref }
@ %def name defn lastdefnlabel
@ The original version of toroff used [[\(oG]] and [[\(cG]] for
the double angle bracket characters.  These appear to be non-standard,
thus we define our own strings to take their place. (A.R.)
<<tmac.w>>=
.ds o< <\h'-0.1m'<
.ds c> >\h'-0.1m'>
.de DEFINITION
.ti -0.5i
\\fR\\(sc\\$1	\\*(o<\\$2 \\$3\\*(c>\\$4\\fP
<<plant newline trap>>
..
.de USE
\\fR\\*(o<\\$1 \\$2\\*(c>\\fP\c   \" section name and original number
..
@ .P
Processing of quoted code within definitions and uses is performed by the
[[convquote]] function.  This must be processed differently than quoted
code within text because the noweb markup program doesn't emit
[[@quote]] .\|.\|. [[@endquote]] markers within definitions and uses.  And,
because the subject string is used as an argument to a macro, it would not
possible to call the [[.BEGINQUOTEDCODE]] and [[.ENDQUOTEDCODE]] macros,
even if noweb did emit the markers within definitions and uses.
.P
The original version just did [[gsub(/\]\]/, "\\*[ENDCONVQUOTE]", s)]],
but this does not catch the case of something like \f(CW[\&[a[i\&]\&]\&]\fP.
A more complicated loop using [[match()]] is in order.
It loops through the string, picking it apart, and replacing
each quoted code section individually.
<<functions>>=
function convquote(s,	out, front, mid, tail) {
    gsub(/\[\[/, "\\*[BEGINCONVQUOTE]", s)
    # gsub(/\]\]/, "\\*[ENDCONVQUOTE]", s)
    out = ""
    mid = "\\*[ENDCONVQUOTE]"
    while (match(s, /\]\]+/) != 0) {
    	# RLENGTH is length of match, want to remove last two chars
	# RSTART is where sequence of ]s begins
	tail = substr(s, RSTART + RLENGTH)
	if (RLENGTH == 2) # easy
		front = substr(s, 1, RSTART - 1)
	else
		front = substr(s, 1, RSTART - 1 + RLENGTH - 2)
	out = out front mid
	s = tail
    }
    out = out s
    return out }
# my test program for the revised function - ADR
# BEGIN { str = "abc[[foo[i]]]]]]]]junk"
#	print str
#	print convquote(str)
#	str2 = "nothing here"
#	print str2
#	print convquote(str2)
#	str3 = "abc[[foo[i]]]]]]]]junk[[bar[i]]more stuff[[baz]]"
#	print str3
#	print convquote(str3)
# }
@ %def convquote
<<tmac.w>>=
.ds BEGINCONVQUOTE \f[CW]
.ds ENDCONVQUOTE   \fP
@ .HU "Cross-referencing and indexing"
The code for producing the ``hypertext links'' at the end of each
code section is given below: for each type, the beginning of the message
is printed, items are accumulated in a list, and the list is printed
after the number of items in the list is known.  The first time any of
the cross-reference or identifier-index messages appears, it is necessary
to reset the point size and leading to the small font used for this
material, which is 80% of the size of code and 64% of the size of text.
All of these lines are part of the diversion which collects a
code chunk.  First is the code to report definitions and uses of code.
<<read and process noweb source>>=
/^@xref begindefs/     { <<checkcode>>; printf ".XREFDEFS\n" }
/^@xref beginuses/     { <<checkcode>>; printf ".XREFUSES\n" }
/^@xref notused/       { <<checkcode>>; printf ".XREFNOTUSED\n" }
/^@xref (def|use)item/ { printf ".ADDLIST %s\n", tag($3) }
/^@xref end(def|use)s/ { printf ".PRINTLIST\n"; code = 1 }
<<tmac.w>>=
.de XREFDEFS
This definition continued in
..
.de XREFUSES
This code used in
..
.de XREFNOTUSED
This code is not used in this document.
.br
..
@ .P
The code to report the definitions of identifiers appears below.  The
[[if]] in [[@index isused]] prevents index definitions from pointing
to themselves.
<<read and process noweb source>>=
/^@index begindefs/ && !noindex { <<checkcode>>; printf ".INDEXDEF\n" }
/^@index isused/ && !noindex {
    if (tag($3) != lastxrefref) printf ".ADDLIST %s\n", tag($3) }
/^@index defitem/ && !noindex { printf ".DEFITEM %s\n.PRINTLIST\n", $3 }
<<tmac.w>>=
.de INDEXDEF
Defines:
.br
..
.de DEFITEM
.ti +1m
\\*[BEGINCONVQUOTE]\\$1\\*[ENDCONVQUOTE],
.if \\n[NLIST]>0 used in
..
@ .P
Finally, here is the code to report uses of identifiers.
<<read and process noweb source>>=
/^@index beginuses/ && !noindex { <<checkcode>>; printf ".INDEXUSE\n" }
/^@index isdefined/ && !noindex { lastuse = tag($3) }
/^@index useitem/   && !noindex {
    printf ".ADDLIST \"\\*[BEGINCONVQUOTE]%s\\*[ENDCONVQUOTE] %s\"\n",
            $3, lastuse }
/^@index enduses/   && !noindex { printf ".PRINTLIST\n" }
@ %def lastuse
<<tmac.w>>=
.de INDEXUSE
Uses
..
@ .P
The macros [[.ADDLIST s]], which adds string [[s]] to a queued list
waiting to be printed, and [[.PRINTLIST]], which prints the list,
appropriately formatted with commas, are described below.  These macros
make use of some interesting troff idioms.  [[LIST]] is an array of
strings; the [[n]]-th string in [[LIST]] can be set to [[s]] by 
[[.ds LISTn s]].  The expression [[.nr n \\$1]] converts the string
passed as the first argument to a macro to the number [[n]].  Loops
are implemented in troff as recursive macros, as in [[.PRINTITEM]].
<<tmac.w>>=
.nr NLIST 0 1 \" initialize list index to 0 with auto-increment 1
.de ADDLIST
.ds LIST\\n+[NLIST] \\$1
..
.de PRINTITEM
.nr PLIST \\$1
.nr PLISTPLUS \\n[PLIST]+1
.if \\n[NLIST]-\\n[PLIST]<0 not used in this document.
.if \\n[NLIST]-\\n[PLIST]=0 \\*[LIST\\n[PLIST]].
.if \\n[NLIST]-\\n[PLIST]=1 \
        \\*[LIST\\n[PLIST]] and \\*[LIST\\n[PLISTPLUS]].
.if \\n[NLIST]-\\n[PLIST]>1 \{ \\*[LIST\\n[PLIST]],
.                        PRINTITEM 1+\\n[PLIST] \}
..
.de PRINTLIST
.PRINTITEM 1
.br
.nr NLIST 0 1 \" re-initialize for next list
..
@ .P
This code and macro reduces the font size in which the cross-reference
and identifier index information is printed at the end of a code chunk.
The [[code]] variable is set to [[0]] to ensure that [[.STARTXREF]]
is performed only once at the end of each code chunk.  This code also
resets the adjustment mode, which was changed to left-adjustment or
right-adjustment by the [[LINETRAP]] macro, and cancels the [[NEWLINE]]
trap at [[TRAPplace]].
<<checkcode>>=
if (code) { code = 0; printf ".STARTXREF\n" }
<<tmac.w>>=
.de STARTXREF
.ps \\n(.s*4/5
.vs \\n(.vu*4u/5u
.ft \\n[OLDft]
.ad \\n[OLDad]
.dt \\n[TRAPplace]u
.sp 0.4v
..
@ .HU "Collecting the lists of chunks and identifiers"
Collecting the lists of chunks and identifiers takes two passes over the
file, because the section numbers which tags refer to aren't known on
the first pass.  Therefore, the strategy on the first pass is to write
the chunk and identifier index entries to the [[tags]] file on standard
error, and actually prepare the lists when reading the [[tags]] file on
the second pass.  Thus, the first pass code shown here merely copies
the chunk and identifier index entries from the noweb intermediate
file to the [[tags]] file using troff's [[.tm]] command.  The
first section below handles chunks, the second section handles
identifiers.
<<read and process noweb source>>=
/^@xref beginchunks/ { printf ".tm ###BEGINCHUNKS###\n" }
/^@xref chunkbegin/  { printf ".tm ###CHUNKBEGIN### %s\n",
                           substr($0, length($3) + 19) }
/^@xref chunkuse/    { printf ".tm ###CHUNKUSE### %s\n", $3 }
/^@xref chunkdefn/   { printf ".tm ###CHUNKDEFN### %s\n", $3 }
/^@xref chunkend/    { printf ".tm ###CHUNKEND###\n" }
/^@xref endchunks/   { printf ".tm ###ENDCHUNKS###\n" }
<<read and process noweb source>>=
/^@index beginindex/ { printf ".tm ###BEGININDEX###\n" }
/^@index entrybegin/ { printf ".tm ###ENTRYBEGIN### %s\n",
                           substr($0, length($3) + 20) }
/^@index entryuse/   { printf ".tm ###ENTRYUSE### %s\n", $3 }
/^@index entrydefn/  { printf ".tm ###ENTRYDEFN### %s\n", $3 }
/^@index entryend/   { printf ".tm ###ENTRYEND###\n" }
/^@index endindex/   { printf ".tm ###ENDINDEX###\n" }
@ .HU "The \f[CW]tags\fP file"
The [[tags]] file is re-created at each formatter run by the troff
idiom (some people would call it a trick) of capturing the output of
troff's [[.tm]] command, which writes to the standard error, in a
file via command-line redirection.  The code below uses an awk idiom; the
[[sub]] simultaneously tests for a match and deletes the matched text.
<<action for [[tags]] line>>=
{
    if      (sub(/^###TAG### /       , "")) tags[$1] = $2
    else if (sub(/^###BEGINCHUNKS###/, "")) printf ".de CLIST\n.CLISTBEGIN\n"
    else if (sub(/^###CHUNKBEGIN### /, "")) { name = convquote($0)
                                              chunkuse = chunkdefn = "" }
    else if (sub(/^###CHUNKUSE### /  , "")) chunkuse = chunkuse " " tag($0)
    else if (sub(/^###CHUNKDEFN### / , "")) chunkdefn = chunkdefn " " tag($0)
    else if (sub(/^###CHUNKEND###/   , ""))
       printf ".CITEM \"%s\" \"%s\" \"%s\"\n", name, chunkdefn, chunkuse
    else if (sub(/^###ENDCHUNKS###/  , "")) printf ".CLISTEND\n..\n"
    else if (sub(/^###BEGININDEX###/ , "")) printf ".de ILIST\n.ILISTBEGIN\n"
    else if (sub(/^###ENTRYBEGIN### /, "")) { name = convquote($0)
                                              entryuse = entrydefn = "" }
    else if (sub(/^###ENTRYUSE### /  , "")) entryuse = entryuse " " tag($0)
    else if (sub(/^###ENTRYDEFN### / , "")) entrydefn = entrydefn " " tag($0)
    else if (sub(/^###ENTRYEND###/   , "")) {
        for (i = 1; i <= split(entrydefn, entryarray); i++)
            sub(entryarray[i], "\\*[BEGINDEFN]&\\*[ENDDEFN]", entryuse)
        printf ".IITEM \"%s\" \"%s\"\n", name, entryuse }
    else if (sub(/^###ENDINDEX###/   , "")) printf ".ILISTEND\n..\n" 
}
@ %def tags chunkuse chunkdefn entryuse entrydefn entryarray
.P
The [[sub]] within the [[ENTRYEND]] causes definitions of identifiers
to be italicized, according to the following defined strings.
<<tmac.w>>=
.ds BEGINDEFN  \fI
.ds ENDDEFN    \fP
@ .P
Lookup of the section number corresponding to a particular tag is
performed by the [[tag]] function.
<<functions>>=
function tag(s) { if (s in tags) return tags[s]; else return "???" }
@ %def tag
@ .P
To use the tags, we have to read the tags file from the back end.
The tags file name is the basename of the file, with the suffix
replaced with [[".nwt"]] (see the toroff script, below).
<<awk program>>=
/^@file / {
  if (tagsfile == "") {
    tagsfile = substr($0, 7)
    sub(".*/", "", tagsfile)
    sub(/\.[^.]*$/, "", tagsfile)
    tagsfile = tagsfile ".nwt"
    while (getline <tagsfile > 0) <<action for [[tags]] line>>
  }
}
@ 
.HU "Using \\f[CW]toroff\fP"
Toroff is one element of the normal noweave pipeline.  Using
toroff in its full generality is hard.  The sample program shown
below 
allows the user to specify the macro package (although it defaults to
[[-mgm]]),
and allows the user to specify troff pre-processors,
post-processors and options.
(Command line options to [[groff]] can be used to invoke tbl, pic, etc.)
See the section below on how to generate the final formatted document.
.P
To avoid a race condition reading the tags file
(troff is writing to it while the awk program is reading it),
we first copy it for
use by the awk program, and then remove the copy.
The location of [[macrodir]] is likely to require customization.
<<noroff>>=
#!/bin/sh
#
# noroff -- run troff using tags file trick

ROFF="groff"
AWK=nawk
macrodir=|LIBDIR|
LIB=|LIBDIR|

opts=

if [ $# -eq 0 ]; then
  echo "Usage: noroff [groff-arguments] files" 1>&2
  exit 1
fi

while [ $# -gt 0 ]
do
	case $1 in
	-*)	opts="$opts $1"
		shift
		;;
	*)	# end of options
		break;
		;;
	esac
done

if [ "$opts" = "" ]
then
	# no options, default to -mm
	# groff already defaults to -Tps
	opts="-mm"
fi
# otherwise assume user passed in all the arguments they want

base="`basename $1 | sed '/\./s/\.[^.]*$//'`"
tagsfile="$base.nwt"
(echo ".so $macrodir/tmac.w"
if [ -r "$tagsfile" ]; then 
   tagstemp=$($LIB/nwmktemp) || { echo "$0: Cannot create temporary file" >&2; exit 1;  }
   cp $tagsfile $tagstemp
   $AWK '<<action for [[tags]] line>>
         <<functions>>' $tagstemp
   rm -f $tagstemp
 fi
 cat "$@") |
($ROFF $opts 2>$tagsfile)
sed '/^###[A-Z][A-Z]*###/d' $tagsfile >&2
@ %def macrodir
@ .HU "Macros for indexing chunks and identifiers"
Toroff creates macros [[.CLIST]] and [[.ILIST]] which insert the
lists of chunks and identifiers, respectively, in the output.  These
macros, in turn, call other macros which format the lists.  The macros
below cause each item to be written on a separate line, with continuation
lines indented one-quarter inch.
<<tmac.w>>=
.de CLISTBEGIN
.in +0.25i
..
.de CITEM
.ti -0.25i
.ie '\\$3'' \\*(o<\\$1 \\$2\\*(c> Not used in this document.
.el         \\*(o<\\$1 \\$2\\*(c> \\$3
.br
..
.de CLISTEND
.in -0.25i
..
<<tmac.w>>=
.de ILISTBEGIN
.in +0.25i
..
.de IITEM
.ti -0.25i
\\f[CW]\\$1\\fP: \\$2
.br
..
.de ILISTEND
.in -0.25i
..
@ .HU "Using \f[CW]noweave -troff\fP and \f[CW]noroff\fP"
To produce the final document,
the tags file is needed for both the [[noweave]]
and the [[noroff]] steps.  This creates a bit of a boot-strapping
problem, since it is troff that first creates the tags file.
The way to do it is to run [[noweave]] and [[noroff]] in turn, twice.
For example, to make a printable version of this document, you would
run the programs like this.
<<Example run>>=
noweave -index -troff roff.nw > roff.tr
noroff roff.tr > /dev/null	# ignore warnings about CLIST and ILIST
noweave -index -troff roff.nw > roff.tr
noroff roff.tr > roff.ps	# print this one
@ .HU "Index of chunks and identifiers"
The automatically-generated index of chunks and identifiers for
toroff is shown below.
.S 10 12
.sp
.CLIST
.sp
.2C
.ILIST